From 9712fa03b769c3d1ed588b28b256cb53d4a2f15a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 17 Nov 2009 18:33:19 +0100 Subject: [PATCH 001/269] Initial public commit --- .gitignore | 3 + Doxyfile | 1510 ++++++++++++++++++++++++++++ LICENSE.txt | 26 + README.txt | 118 +++ sound/.gitignore | 1 + sound/imp/.gitignore | 1 + sound/imp/audiere_imp.cpp | 94 ++ sound/imp/audiere_imp.h | 73 ++ sound/imp/input_ffmpeg.cpp | 222 ++++ sound/imp/input_ffmpeg.h | 71 ++ sound/imp/openal_ffmpeg.h | 28 + sound/imp/output_openal.cpp | 349 +++++++ sound/imp/output_openal.h | 124 +++ sound/imp/sound_pair.h | 75 ++ sound/input.h | 75 ++ sound/sound.h | 172 ++++ sound/tests/.gitignore | 1 + sound/tests/Makefile | 17 + sound/tests/audiere_test.cpp | 7 + sound/tests/common.cpp | 41 + sound/tests/cow.wav | Bin 0 -> 37546 bytes sound/tests/ffmpeg_openal_test.cpp | 7 + sound/tests/owl.ogg | Bin 0 -> 18641 bytes 23 files changed, 3015 insertions(+) create mode 100644 .gitignore create mode 100644 Doxyfile create mode 100644 LICENSE.txt create mode 100644 README.txt create mode 100644 sound/.gitignore create mode 100644 sound/imp/.gitignore create mode 100644 sound/imp/audiere_imp.cpp create mode 100644 sound/imp/audiere_imp.h create mode 100644 sound/imp/input_ffmpeg.cpp create mode 100644 sound/imp/input_ffmpeg.h create mode 100644 sound/imp/openal_ffmpeg.h create mode 100644 sound/imp/output_openal.cpp create mode 100644 sound/imp/output_openal.h create mode 100644 sound/imp/sound_pair.h create mode 100644 sound/input.h create mode 100644 sound/sound.h create mode 100644 sound/tests/.gitignore create mode 100644 sound/tests/Makefile create mode 100644 sound/tests/audiere_test.cpp create mode 100644 sound/tests/common.cpp create mode 100644 sound/tests/cow.wav create mode 100644 sound/tests/ffmpeg_openal_test.cpp create mode 100644 sound/tests/owl.ogg diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..cd24d78972 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +upload_docs.sh +docs +*~ diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000000..4dfff9188c --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1510 @@ +# Doxyfile 1.5.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# 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 a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = GOOI + +# 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 = 1 + +# 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, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. + +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. + +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 is your file systems +# 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 = 8 + +# 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 = + +# 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, C#, 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 + +EXTENSION_MAPPING = + +# 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 make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# 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 to 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 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 SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_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 and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = 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 namespace 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 = YES + +# 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 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_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 + +# 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 sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define 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 defines 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 + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# 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. The 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 = + +#--------------------------------------------------------------------------- +# 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 + +# This WARN_NO_PARAMDOC option can be abled 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 = sound + +# 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++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.h + +# 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 +# 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. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem 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 = */tests/* + +# 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. + +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, INPUT_FILTER +# is applied to all files. + +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 + +#--------------------------------------------------------------------------- +# 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 = NO + +# 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 and C++ 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 = NO + +# 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 = docs + +# 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. + +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 the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = 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. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# 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 = NO + +# 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 = "Doxygen generated docs" + +# 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 = org.doxygen.Project + +# 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 = + +# 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 = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# 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 FRAME, 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 (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NONE + +# 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 + +# 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 + +#--------------------------------------------------------------------------- +# 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. + +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, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# 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 = + +# 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 + +#--------------------------------------------------------------------------- +# 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 stylesheet 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 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 +# in the INCLUDE_PATH (see below) will be search if 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. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and 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. +# Optionally an initial location of the external documentation +# can be added for each tagfile. 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. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# 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 + +# 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 is superseded by the HAVE_DOT option below. This is only a +# fallback. 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 = YES + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need 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 = FreeSans + +# 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 output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +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 +# 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 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 graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES 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 png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# 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 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 + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..1b98eeb9b9 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,26 @@ +Game-oriented object interfaces (GOOI) is licensed under the +'zlib/libpng' license: + +---- + +Copyright (c) 2009 Nicolay Korslund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000000..0b0befca55 --- /dev/null +++ b/README.txt @@ -0,0 +1,118 @@ +Welcome to GOOI v0.1 +-------------------- + +Written by: Nicolay Korslund (korslund@gmail.com) +License: zlib/png (see LICENSE.txt) +WWW: http://asm-soft.com/gooi/ +Documentation: http://asm-soft.com/gooi/docs + + + +GOOI stands for Game-Oriented Object Interfaces. It is meant to become +a small set of generic interfaces for various game middleware +libraries, such as sound, input, graphics, and so on. It consists of +several independent modules, one for each of these areas. These may be +used together to build an entire game engine, or they can be used +individually as separate libraries. + +However, GOOI does NOT actually implement a game engine, or any new +fundamental functionality. More on that below. + +Currently there is only the Sound module, but more will come in the +future (including input, 2D/3D graphics, GUI, physics, file +system/archive access, and more.) + + +Main idea +--------- + +The idea behind to provide a uniform, consistent interface to other +game libraries. The library does not provide ANY functionality on its +own. Instead it connects to a backend implementation of your choice. + +The Sound module, for example, currently has backends for OpenAL +(output only), FFmpeg (input only) and for Audiere. Hopefully we'll +soon add IrrKlang, FMod, DirectSound and Miles to that. It can combine +libraries to get more complete functionality (like using OpenAL for +output and FFmpeg to decode sound files), and it's also easy to write +your own backend if you're using a different (or home-brewed) sound +system. + +Regardless of what backend you use, the front-end interface (found in +sound/sound.h) is identical, and as a library user you shouldn't +notice much difference at all if you swap one backend for another at a +later point. + +The goal in the long run is to support a wide variety of game-related +libraries, and as many backend libraries (free and commercial) as +possible, so that you the user will have to write as little code as +possible. + + + +What is it good for +------------------- + +The main point of GOOI, as we said above, is that it connects to any +library of your choice "behind the scenes" but provides the same, +super-simple interface front-end for all of them. There can benefit +you in many ways: + +- If you want to use a new library that GOOI support. You don't + have to scour the net for tutorials and usage examples, since much + of the common usage code is already included in the implementation + classes. + +- If you don't want to pollute your code with library-specific code. + The GOOI interfaces can help you keep your code clean, and its user + interface is often simpler than the exteral library one. + +- If you are creating a library that depends on a specific feature + (such as sound), but you don't want to lock your users into any + specific sound library. GOOI works as an abstraction that lets your + users select their own implementation. My own Monster scripting + language ( http://monsterscript.net ) will use this tactic, to + provide native-but-generic sound, input and GUI support, among other + features. + +- If you want to support multiple backends, or make it possible to + easily switch backends later. You can select backends at compile + time or even at runtime. Maybe you decide to switch to to a + commercial library at a late stage in development, or you discover + that your favorite backend doesn't work on all the platforms you + want to reach. + +The GOOI implementations are extremely light-weight - often just one +or two cpp/h pairs. You plug them directly into your program, there's +no separate build step required. + +Since the library aims to be very modularly put together, you can +also, in many cases, just copy-and-paste the parts you need and ignore +the rest. Or modify stuff without fearing that the whole 'system' will +come crashing down, because there is no big 'system' to speak of. + + +Past and future +--------------- + +GOOI started out as a spin-off from OpenMW, another project of mine +( http://openmw.sourceforge.net ). OpenMW is an attempt to recreate +the engine behind the commercial game Morrowind, using only open +source software. + +The projects are still tightly interlinked, and the will continue to +be until OpenMW is finished. That means that all near-future work on +GOOI for my part will be more or less guided by what OpenMW needs. But +I'll gladly accept external contributions that are not OpenMW-related. + + +Conclusion +---------- + +As you might have guessed, GOOI is more a concept in development than +a finished library right now. + +All feedback, ideas, concepts, questions and code are very +welcome. Send them to: korslund@gmail.com + +I will put up a forum later as well if there's enough interest. diff --git a/sound/.gitignore b/sound/.gitignore new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/sound/.gitignore @@ -0,0 +1 @@ + diff --git a/sound/imp/.gitignore b/sound/imp/.gitignore new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/sound/imp/.gitignore @@ -0,0 +1 @@ + diff --git a/sound/imp/audiere_imp.cpp b/sound/imp/audiere_imp.cpp new file mode 100644 index 0000000000..ea094409fc --- /dev/null +++ b/sound/imp/audiere_imp.cpp @@ -0,0 +1,94 @@ +#include "audiere_imp.h" + +// Exception handling +class Audiere_Exception : public std::exception +{ + std::string msg; + + public: + + Audiere_Exception(const std::string &m) : msg(m) {} + ~Audiere_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw Audiere_Exception("Audiere exception: " + msg); +} + +using namespace audiere; +using namespace GOOI::Sound; + +AudiereManager::AudiereManager() +{ + needsUpdate = false; + has3D = false; + canRepeatStream = true; + canLoadFile = true; + canLoadSource = false; + + device = OpenDevice(""); + + if(device == NULL) + fail("Failed to open device"); +} + +// --- Manager --- + +Sound *AudiereManager::load(const std::string &file, bool stream) +{ return new AudiereSound(file, device, stream); } + + +// --- Sound --- + +AudiereSound::AudiereSound(const std::string &file, + AudioDevicePtr _device, + bool _stream) + : device(_device), stream(_stream) +{ + sample = OpenSampleSource(file.c_str()); + if(!sample) + fail("Couldn't load file " + file); + + buf = CreateSampleBuffer(sample); +} + +Instance *AudiereSound::getInstance(bool is3d, bool repeat) +{ + // Ignore is3d. Audiere doesn't implement 3d sound. We could make a + // hack software 3D implementation later, but it's not that + // important. + + SampleSourcePtr sample = buf->openStream(); + if(!sample) + fail("Failed to open sample stream"); + + OutputStreamPtr sound = OpenSound(device, sample, stream); + + if(repeat) + sound->setRepeat(true); + + return new AudiereInstance(sound); +} + + +// --- Instance --- + +AudiereInstance::AudiereInstance(OutputStreamPtr _sound) + : sound(_sound) {} + +void AudiereInstance::play() +{ sound->play(); } + +void AudiereInstance::stop() +{ sound->stop(); } + +void AudiereInstance::pause() +{ stop(); } + +bool AudiereInstance::isPlaying() +{ return sound->isPlaying(); } + +void AudiereInstance::setVolume(float vol) +{ sound->setVolume(vol); } diff --git a/sound/imp/audiere_imp.h b/sound/imp/audiere_imp.h new file mode 100644 index 0000000000..9f667a98ab --- /dev/null +++ b/sound/imp/audiere_imp.h @@ -0,0 +1,73 @@ +#ifndef GOOI_SOUND_AUDIERE_H +#define GOOI_SOUND_AUDIERE_H + +#include "../sound.h" + +#include +#include + +namespace GOOI { +namespace Sound { + +/// Implementation of Sound::Manager for Audiere +class AudiereManager : public Manager +{ + audiere::AudioDevicePtr device; + + public: + AudiereManager(); + + virtual Sound *load(const std::string &file, bool stream=false); + + /// disabled + virtual Sound *load(InputSource *input, bool stream=false) + { assert(0); } + /// disabled + virtual void update() { assert(0); } + /// disabled + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { assert(0); }; +}; + +/// Audiere Sound implementation +class AudiereSound : public Sound +{ + audiere::AudioDevicePtr device; + audiere::SampleSourcePtr sample; + audiere::SampleBufferPtr buf; + + bool stream; + + public: + virtual Instance *getInstance(bool is3d, bool repeat); + virtual void drop() + { delete this; } + + AudiereSound(const std::string &file, audiere::AudioDevicePtr device, + bool stream); +}; + +/// Audiere Instance implementation +class AudiereInstance : public Instance +{ + audiere::OutputStreamPtr sound; + + public: + virtual void play(); + virtual void stop(); + virtual void pause(); + virtual bool isPlaying(); + virtual void setVolume(float); + /// disabled + virtual void setPos(float x, float y, float z) + { assert(0); } + virtual void drop() + { delete this; } + + AudiereInstance(audiere::OutputStreamPtr); +}; + +}} // Namespace +#endif diff --git a/sound/imp/input_ffmpeg.cpp b/sound/imp/input_ffmpeg.cpp new file mode 100644 index 0000000000..c73689b782 --- /dev/null +++ b/sound/imp/input_ffmpeg.cpp @@ -0,0 +1,222 @@ +#include "input_ffmpeg.h" +#include + +using namespace GOOI::Sound; + +// Static output buffer. Not thread safe, but supports multiple +// streams operated from the same thread. +static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; +bool FFM_InputManager::init = false; + +FFM_Exception::FFM_Exception(const std::string &m) + : msg(m) {} + +const char* FFM_Exception::what() const throw() +{ return msg.c_str(); } + +FFM_Exception::~FFM_Exception() throw() {} + +static void fail(const std::string &msg) +{ + throw FFM_Exception("FFMpeg exception: " + msg); +} + + +// --- Manager --- + +FFM_InputManager::FFM_InputManager() +{ + if(!init) + { + av_register_all(); + av_log_set_level(AV_LOG_ERROR); + init = true; + } +} + +InputSource *FFM_InputManager::load(const std::string &file) +{ return new FFM_InputSource(file); } + + +// --- Source --- + +FFM_InputSource::FFM_InputSource(const std::string &file) +{ + // FFmpeg doesn't handle several instances from one source. So we + // just store the filename. + name = file; +} + +InputStream *FFM_InputSource::getStream() +{ return new FFM_InputStream(name); } + +void FFM_InputSource::drop() +{ delete this; } + + +// --- Stream --- + +FFM_InputStream::FFM_InputStream(const std::string &file) +{ + std::string msg; + AVCodec *codec; + + empty = false; + + if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) + fail("Error loading audio file " + file); + + if(av_find_stream_info(FmtCtx) < 0) + { + msg = "Error in file stream " + file; + goto err; + } + + // Pick the first audio stream, if any + for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) + { + // Pick the first audio stream + if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) + break; + } + + if(StreamNum == FmtCtx->nb_streams) + fail("File " + file + " didn't contain any audio streams"); + + // Open the decoder + CodecCtx = FmtCtx->streams[StreamNum]->codec; + codec = avcodec_find_decoder(CodecCtx->codec_id); + + if(!codec || avcodec_open(CodecCtx, codec) < 0) + { + msg = "Error loading " + file + ": "; + if(codec) + msg += "coded error"; + else + msg += "no codec found"; + goto err; + } + + // No errors, we're done + return; + + // Handle errors + err: + av_close_input_file(FmtCtx); + fail(msg); +} + +FFM_InputStream::~FFM_InputStream() +{ + avcodec_close(CodecCtx); + av_close_input_file(FmtCtx); +} + +void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + if(rate) *rate = CodecCtx->sample_rate; + if(channels) *channels = CodecCtx->channels; + if(bits) *bits = 16; +} + +uint32_t FFM_InputStream::getData(void *data, uint32_t length) +{ + if(empty) return 0; + + uint32_t left = length; + uint8_t *outPtr = (uint8_t*)data; + + // First, copy over any stored data we might be sitting on + { + int s = storage.size(); + int copy = s; + if(s) + { + // Make sure there's room + if(copy > left) + copy = left; + + // Copy + memcpy(outPtr, &storage[0], copy); + outPtr += copy; + left -= copy; + + // Is there anything left in the storage? + s -= copy; + if(s) + { + assert(left == 0); + + // Move it to the start and resize + memmove(&storage[0], &storage[copy], s); + storage.resize(s); + } + } + } + + // Next, get more input data from stream, and decode it + while(left) + { + AVPacket packet; + + // Get the next packet, if any + if(av_read_frame(FmtCtx, &packet) < 0) + break; + + // We only allow one stream per file at the moment + assert(StreamNum == packet.stream_index); + + // Decode the packet + int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; + int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, + &len, packet.data, packet.size); + assert(tmp < 0 || tmp == packet.size); + + // We don't need the input packet any longer + av_free_packet(&packet); + + if(tmp < 0) + fail("Error decoding audio stream"); + + // Copy whatever data we got, and advance the pointer + if(len > 0) + { + // copy = how many bytes do we copy now + int copy = len; + if(copy > left) + copy = left; + + // len = how many bytes are left uncopied + len -= copy; + + // copy data + memcpy(outPtr, outBuf, copy); + + // left = how much space is left in the caller output + // buffer + left -= copy; + outPtr += copy; + assert(left >= 0); + + if(len > 0) + { + // There were uncopied bytes. Store them for later. + assert(left == 0); + storage.resize(len); + memcpy(&storage[0], outBuf, len); + } + } + } + + // End of loop. Return the number of bytes copied. + assert(left <= length); + + // If we're returning less than asked for, then we're done + if(left > 0) + empty = true; + + return length - left; +} + +void FFM_InputStream::drop() +{ delete this; } diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h new file mode 100644 index 0000000000..17c8f7352e --- /dev/null +++ b/sound/imp/input_ffmpeg.h @@ -0,0 +1,71 @@ +#ifndef GOOI_SOUND_FFMPEG_H +#define GOOI_SOUND_FFMPEG_H + +#include "../input.h" +#include +#include + +extern "C" +{ +#include +#include +} + +namespace GOOI { +namespace Sound { + +/// FFmpeg exception +class FFM_Exception : public std::exception +{ + std::string msg; + + public: + + FFM_Exception(const std::string &m); + ~FFM_Exception() throw(); + virtual const char* what() const throw(); +}; + +/// FFMpeg implementation of InputManager +class FFM_InputManager : public InputManager +{ + static bool init; + + public: + FFM_InputManager(); + virtual InputSource *load(const std::string &file); +}; + +/// FFMpeg implementation of InputSource +class FFM_InputSource : public InputSource +{ + std::string name; + + public: + FFM_InputSource(const std::string &file); + + virtual InputStream *getStream(); + virtual void drop(); +}; + +/// FFMpeg implementation of InputStream +class FFM_InputStream : public InputStream +{ + AVFormatContext *FmtCtx; + AVCodecContext *CodecCtx; + int StreamNum; + bool empty; + + std::vector storage; + + public: + FFM_InputStream(const std::string &file); + ~FFM_InputStream(); + + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + virtual uint32_t getData(void *data, uint32_t length); + virtual void drop(); +}; + +}} // namespaces +#endif diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h new file mode 100644 index 0000000000..92fd32553c --- /dev/null +++ b/sound/imp/openal_ffmpeg.h @@ -0,0 +1,28 @@ +#ifndef GOOI_FFMPEG_OPENAL_H +#define GOOI_FFMPEG_OPENAL_H + +#include "sound_pair.h" +#include "input_ffmpeg.h" +#include "output_openal.h" + +namespace GOOI { +namespace Sound { + +/// A PairManager filter that adds FFmpeg decoding to OpenAL +class OpenAL_FFM_Manager : public PairManager +{ + public: + OpenAL_FFM_Manager() + { + set(new FFM_InputManager, + new OpenAL_Manager); + } + ~OpenAL_FFM_Manager() + { + delete snd; + delete inp; + } +}; + +}} +#endif diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp new file mode 100644 index 0000000000..f7dc11507c --- /dev/null +++ b/sound/imp/output_openal.cpp @@ -0,0 +1,349 @@ +#include "output_openal.h" +#include + +#include + +using namespace GOOI::Sound; + + +// ---- Helper functions and classes ---- + +class OpenAL_Exception : public std::exception +{ + std::string msg; + + public: + + OpenAL_Exception(const std::string &m) : msg(m) {} + ~OpenAL_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw OpenAL_Exception("OpenAL exception: " + msg); +} + +static void checkALError(const std::string &msg) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + fail("\"" + std::string(alGetString(err)) + "\" while " + msg); +} + +static void getALFormat(InputStream *inp, int &fmt, int &rate) +{ + int ch, bits; + inp->getInfo(&rate, &ch, &bits); + + fmt = 0; + + if(bits == 8) + { + if(ch == 1) fmt = AL_FORMAT_MONO8; + if(ch == 2) fmt = AL_FORMAT_STEREO8; + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); + } + } + if(bits == 16) + { + if(ch == 1) fmt = AL_FORMAT_MONO16; + if(ch == 2) fmt = AL_FORMAT_STEREO16; + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); + } + } + + if(fmt == 0) + fail("Unsupported input format"); +} + + +// ---- Manager ---- + +OpenAL_Manager::OpenAL_Manager() + : Context(NULL), Device(NULL) +{ + needsUpdate = true; + has3D = true; + canRepeatStream = false; + canLoadFile = false; + canLoadSource = true; + + // Set up sound system + Device = alcOpenDevice(NULL); + Context = alcCreateContext(Device, NULL); + + if(!Device || !Context) + fail("Failed to initialize context or device"); + + alcMakeContextCurrent(Context); +} + +OpenAL_Manager::~OpenAL_Manager() +{ + // Deinitialize sound system + alcMakeContextCurrent(NULL); + if(Context) alcDestroyContext(Context); + if(Device) alcCloseDevice(Device); +} + +Sound *OpenAL_Manager::load(const std::string &file, bool stream) +{ assert(0 && "OpenAL cannot decode files"); } + +Sound *OpenAL_Manager::load(InputSource *source, bool stream) +{ return new OpenAL_Sound(source, this, stream); } + +void OpenAL_Manager::update() +{ + // Loop through all the streaming sounds and update them + LST::iterator it, next; + for(it = streaming.begin(); + it != streaming.end(); + it++) + { + (*it)->update(); + } +} + +void OpenAL_Manager::setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) +{ + ALfloat orient[6]; + orient[0] = fx; + orient[1] = fy; + orient[2] = fz; + orient[3] = ux; + orient[4] = uy; + orient[5] = uz; + alListener3f(AL_POSITION, x, y, z); + alListenerfv(AL_ORIENTATION, orient); + checkALError("setting listener position"); +} + +OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst) +{ + streaming.push_front(inst); + return streaming.begin(); +} + +void OpenAL_Manager::remove_stream(LST::iterator it) +{ + streaming.erase(it); +} + + +// ---- Sound ---- + +OpenAL_Sound::~OpenAL_Sound() +{ + // Kill the input source + if(source) source->drop(); + + // And any allocated buffers + if(bufferID) + alDeleteBuffers(1, &bufferID); +} + +Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) +{ + assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); + + if(stream) + return new OpenAL_Stream_Instance(source->getStream(), owner); + + // Load the buffer if it hasn't been done already + if(bufferID == 0) + { + // Get an input stream and load the file from it + InputStream *inp = source->getStream(); + + std::vector buffer; + + // Add 32 kb at each increment + const int ADD = 32*1024; + + // Fill the buffer. We increase the buffer until it's large + // enough to fit all the data. + while(true) + { + // Increase the buffer + int oldlen = buffer.size(); + buffer.resize(oldlen+ADD); + + // Read the data + size_t len = inp->getData(&buffer[oldlen], ADD); + + // If we read less than requested, we're done. + if(len < ADD) + { + // Downsize the buffer to the right size + buffer.resize(oldlen+len); + break; + } + } + + // Get the format + int fmt, rate; + getALFormat(inp, fmt, rate); + + // We don't need the file anymore + inp->drop(); + source->drop(); + source = NULL; + + // Move the data into OpenAL + alGenBuffers(1, &bufferID); + alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); + checkALError("loading sound buffer"); + } // End of buffer loading + + // At this point, the file data has been loaded into the buffer + // in 'bufferID', and we should be ready to go. + assert(bufferID != 0); + + return new OpenAL_Simple_Instance(bufferID); +} + + +// ---- OpenAL_Instance_Base ---- + +void OpenAL_Instance_Base::play() +{ + alSourcePlay(inst); + checkALError("starting playback"); +} + +void OpenAL_Instance_Base::stop() +{ + alSourceStop(inst); + checkALError("stopping"); +} + +void OpenAL_Instance_Base::pause() +{ + alSourcePause(inst); + checkALError("pausing"); +} + +bool OpenAL_Instance_Base::isPlaying() +{ + ALint state; + alGetSourcei(inst, AL_SOURCE_STATE, &state); + + return state == AL_PLAYING; +} + +void OpenAL_Instance_Base::setVolume(float volume) +{ + if(volume > 1.0) volume = 1.0; + if(volume < 0.0) volume = 0.0; + alSourcef(inst, AL_GAIN, volume); + checkALError("setting volume"); +} + +void OpenAL_Instance_Base::setPos(float x, float y, float z) +{ + alSource3f(inst, AL_POSITION, x, y, z); + checkALError("setting position"); +} + + +// ---- OpenAL_Simple_Instance ---- + +OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) +{ + // Create instance and associate buffer + alGenSources(1, &inst); + alSourcei(inst, AL_BUFFER, buf); +} + +OpenAL_Simple_Instance::~OpenAL_Simple_Instance() +{ + // Stop + alSourceStop(inst); + + // Return sound + alDeleteSources(1, &inst); +} + + +// ---- OpenAL_Stream_Instance ---- + +OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, + OpenAL_Manager *_owner) + : stream(_stream), owner(_owner) +{ + // Deduce the file format from the stream info + getALFormat(stream, fmt, rate); + + // Create the buffers and the sound instance + alGenBuffers(BUFS, bufs); + alGenSources(1, &inst); + + checkALError("initializing"); + + // Fill the buffers and que them + for(int i=0; iadd_stream(this); +} + +void OpenAL_Stream_Instance::queueBuffer(ALuint bId) +{ + char buf[SIZE]; + + // Get the data + int len = stream->getData(buf, SIZE); + if(len == 0) + return; + + // .. and stash it + alBufferData(bId, fmt, buf, len, rate); + alSourceQueueBuffers(inst, 1, &bId); +} + +OpenAL_Stream_Instance::~OpenAL_Stream_Instance() +{ + // Remove ourselves from streaming list + owner->remove_stream(lit); + + // Stop + alSourceStop(inst); + + // Kill the input stream + stream->drop(); + + // Return sound + alDeleteSources(1, &inst); + + // Delete buffers + alDeleteBuffers(BUFS, bufs); +} + +void OpenAL_Stream_Instance::update() +{ + ALint count; + alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); + + for(int i = 0;i < count;i++) + { + // Unque a finished buffer + ALuint bId; + alSourceUnqueueBuffers(inst, 1, &bId); + + // Queue a new buffer + queueBuffer(bId); + } +} diff --git a/sound/imp/output_openal.h b/sound/imp/output_openal.h new file mode 100644 index 0000000000..e2e506aa28 --- /dev/null +++ b/sound/imp/output_openal.h @@ -0,0 +1,124 @@ +#ifndef GOOI_SOUND_OPENAL_H +#define GOOI_SOUND_OPENAL_H + +#include "../sound.h" + +#include +#include +#include + +namespace GOOI { +namespace Sound { + +class OpenAL_Stream_Instance; + +/// OpenAL implementation of Manager +class OpenAL_Manager : public Manager +{ +public: + // List of all streaming sounds - these need to be updated regularly + typedef std::list LST; + + OpenAL_Manager(); + virtual ~OpenAL_Manager(); + + LST::iterator add_stream(OpenAL_Stream_Instance*); + void remove_stream(LST::iterator); + + virtual Sound *load(const std::string &file, bool stream=false); + virtual Sound *load(InputSource* input, bool stream=false); + virtual void update(); + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz); + + private: + ALCdevice *Device; + ALCcontext *Context; + + LST streaming; +}; + +/// OpenAL implementation of Sound +class OpenAL_Sound : public Sound +{ + InputSource *source; + OpenAL_Manager *owner; + bool stream; + + // Used for non-streaming files, contains the entire sound buffer if + // non-zero + ALuint bufferID; + + public: + OpenAL_Sound(InputSource *src, OpenAL_Manager *own, bool str) + : source(src), owner(own), stream(str), bufferID(0) {} + ~OpenAL_Sound(); + + virtual Instance *getInstance(bool is3d, bool repeat); + void drop() { delete this; } +}; + +/// Shared parent class that holds an OpenAL sound instance. Just used +/// for shared functionality, has no setup or cleanup code. +class OpenAL_Instance_Base : public Instance +{ + protected: + ALuint inst; + + public: + void drop() { delete this; } + virtual void play(); + virtual void stop(); + virtual void pause(); + virtual bool isPlaying(); + virtual void setVolume(float); + virtual void setPos(float x, float y, float z); +}; + +/// Non-streaming OpenAL-implementation of Instance. Uses a shared +/// sound buffer in OpenAL_Sound. +class OpenAL_Simple_Instance : public OpenAL_Instance_Base +{ + public: + OpenAL_Simple_Instance(ALuint buf); + ~OpenAL_Simple_Instance(); +}; + +/// Streaming OpenAL-implementation of Instance. +class OpenAL_Stream_Instance : public OpenAL_Instance_Base +{ + // Since OpenAL streams have to be updated manually each frame, we + // need to have a sufficiently large buffer so that we don't run out + // of data in the mean time. Each instance will take around 512 Kb + // of memory, independent of how large the file is. + static const int BUFS = 4; + static const int SIZE = 128*1024; + + // Buffers + ALuint bufs[BUFS]; + + // Sound format settings + int rate, fmt; + + // Source of data + InputStream *stream; + + OpenAL_Manager *owner; + + // List iterator, used for removing ourselves from the streaming + // list when we're deleted. + OpenAL_Manager::LST::iterator lit; + + // Load and queue a new buffer + void queueBuffer(ALuint buffer); + +public: + OpenAL_Stream_Instance(InputStream*, OpenAL_Manager*); + ~OpenAL_Stream_Instance(); + + void update(); +}; + +}} // namespaces +#endif diff --git a/sound/imp/sound_pair.h b/sound/imp/sound_pair.h new file mode 100644 index 0000000000..751c72b1af --- /dev/null +++ b/sound/imp/sound_pair.h @@ -0,0 +1,75 @@ +#ifndef GOOI_SOUND_PAIR_H +#define GOOI_SOUND_PAIR_H + +#include "sound.h" + +#include + +namespace GOOI { +namespace Sound { + +/** + @brief This filter class adds file loading capabilities to a + Sound::Manager class, by associating an InputManager with it. + + The class takes an existing Manager able to load streams, and + associates an InputManager with it. The combined class is able to + load files directly. + + Example: + \code + + // Combine FFmpeg input and OpenAL output. OpenAL cannot decode + // sound files on its own. + SoundPairManager mg(new FFM_InputManager, new OpenAL_Manager); + + // We can now load filenames directly. + mg.load("file1.mp3"); + \endcode +*/ +class PairManager : public Manager +{ + protected: + Manager *snd; + InputManager *inp; + + public: + /// Empty constructor + PairManager() {} + + /// Assign an input manager and a sound manager to this object + PairManager(InputManager *_inp, Manager *_snd) + { set(_inp, _snd); } + + /// Assign an input manager and a sound manager to this object + void set(InputManager *_inp, Manager *_snd) + { + inp = _inp; + snd = _snd; + + needsUpdate = snd->needsUpdate; + has3D = snd->has3D; + canRepeatStream = snd->canRepeatStream; + + // Both these should be true, or the use of this class is pretty + // pointless + canLoadSource = snd->canLoadSource; + canLoadFile = canLoadSource; + assert(canLoadSource && canLoadFile); + } + + virtual Sound *load(const std::string &file, bool stream=false) + { return load(inp->load(file), stream); } + + virtual Sound *load(InputSource *input, bool stream=false) + { return snd->load(input, stream); } + + virtual void update() { snd->update(); } + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } +}; + +}} +#endif diff --git a/sound/input.h b/sound/input.h new file mode 100644 index 0000000000..a0d2826aa3 --- /dev/null +++ b/sound/input.h @@ -0,0 +1,75 @@ +#ifndef GOOI_SOUND_INPUT_H +#define GOOI_SOUND_INPUT_H + +#include +#include + +namespace GOOI { +namespace Sound { + +/// An abstract interface for a read-once stream of audio data. +/** All instances of this is created through InputSource. Objects + should be manually deleted through a call to drop() when they are + no longer needed. +*/ +class InputStream +{ + public: + /// Get the sample rate, number of channels, and bits per + /// sample. NULL parameters are ignored. + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0; + + /// Get decoded sound data from the stream. + /** Stores 'length' bytes (or less) in the buffer pointed to by + 'output'. Returns the number of bytes written. The function will + only return less than 'length' at the end of the stream. When + the stream is empty, all subsequent calls will return zero. + + @param output where to store data + @param length number of bytes to get + @return number of bytes actually written + */ + virtual uint32_t getData(void *output, uint32_t length) = 0; + + /// Kill this object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~InputStream() {} +}; + +/// Abstract interface representing one sound source. +/** A sound source may represent one sound file or buffer, and is a + factory for producing InputStream objects from that + sound. Instances of this class are created by an InputManager. All + instances should be deleted through drop() when they are no longer + needed. + */ +class InputSource +{ + public: + /// Create a stream from this sound + virtual InputStream *getStream() = 0; + + /// Kill this object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~InputSource() {} +}; + +/// Main interface to a sound decoder backend. +/** An input manager is a factory of InputSource objects. + */ +class InputManager +{ + public: + /// Load a sound input source from file + virtual InputSource *load(const std::string &file) = 0; + + /// Virtual destructor + virtual ~InputManager() {} +}; + +}} // namespaces +#endif diff --git a/sound/sound.h b/sound/sound.h new file mode 100644 index 0000000000..509efa8163 --- /dev/null +++ b/sound/sound.h @@ -0,0 +1,172 @@ +#ifndef GOOI_SOUND_SOUND_H +#define GOOI_SOUND_SOUND_H + +#include +#include "input.h" + +namespace GOOI { +namespace Sound { + +/// Abstract interface for sound instances +/** This class represents one sound instance, which may be played, + stopped, paused and so on. Instances are created from the Sound + class. All instances must be terminated manually using the drop() + function when they are no longer in use. +*/ +class Instance +{ + public: + /// Play or resume the sound + virtual void play() = 0; + + /// Stop the sound + virtual void stop() = 0; + + /// Pause the sound, may be resumed later + virtual void pause() = 0; + + /// Check if the sound is still playing + virtual bool isPlaying() = 0; + + /// Set the volume. The parameter must be between 0.0 and 1.0. + virtual void setVolume(float) = 0; + + /// Set the position. May not have any effect on 2D sounds. + virtual void setPos(float x, float y, float z) = 0; + + /// Kill the current object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~Instance() {} +}; + +/// Abstract interface for sound files or sources +/** This class acts as a factory for sound Instance objects. + Implementations may choose to store shared sound buffers or other + optimizations in subclasses of Sound. Objects of this class are + created through the Manager class. All objects of this class + should be terminated manually using the drop() function when they + are no longer in use. +*/ +class Sound +{ + public: + /** + @brief Create an instance of this sound + + See also the capability flags in the Manager class. + + @param is3d true if this the sound is to be 3d enabled + @param repeat true if the sound should loop + @return new Instance object + */ + virtual Instance *getInstance(bool is3d, bool repeat) = 0; + + // Some prefab functions + + /// Shortcut for creating 3D instances + Instance *get3D(bool loop=false) + { return getInstance(true, loop); } + /// Shortcut for creating 2D instances + Instance *get2D(bool loop=false) + { return getInstance(false, loop); } + + /// Kill the current object + virtual void drop() = 0; + + /// Virtual destructor + virtual ~Sound() {} +}; + +/// Abstract interface for the main sound manager class +/** The sound manager is used to load sound files and is a factory for + Sound objects. It is the main entry point to a given sound system + implementation. + + The class also contains a set of public bools which describe the + capabilities the particular system. These should be set by + implementations (base classes) in their respective constructors. + */ +class Manager +{ + public: + /** @brief If set to true, you should call update() regularly (every frame + or so) on this sound manager. If false, update() should not be + called. + */ + bool needsUpdate; + + /** @brief true if 3D functions are available. If false, all use of + 3D sounds and calls to setPos / setListenerPos will result in + undefined behavior. + */ + bool has3D; + + /** @brief true if 'repeat' and 'stream' can be used simultaneously. + If false, repeating a streamed sound will give undefined + behavior. + */ + bool canRepeatStream; + + /// true if we can load sounds directly from file + bool canLoadFile; + + /// true if we can load sounds from an InputSource + bool canLoadSource; + + /** + @brief Load a sound from an input source. Only valid if + canLoadSource is true. + + This function loads a sound from a given stream as defined by + InputSource and InputStream. The InputSource and all streams + created from it will be dropped when drop() is called on the + owning sound / instance. + + @param input the input source + @param stream true if the file should be streamed. + Implementations may use this for optimizing playback of + large files, but they are not required to. + @return a new Sound object + */ + virtual Sound *load(InputSource *input, bool stream=false) = 0; + + /** + @brief Load a sound directly from file. Only valid if canLoadFile + is true. + + @param file filename + @param stream true if the file should be streamed + @see load(InputSource*,bool) + */ + virtual Sound *load(const std::string &file, bool stream=false) = 0; + + /// Call this every frame if needsUpdate is true + /** + Update function that should be called regularly (about every + frame in a normal game setting.) Implementions may use this to + fill streaming buffers and similar. Implementations that do not + need this should set needsUpdate to false. + */ + virtual void update() = 0; + + /// Set listener position (coordinates, front and up vectors) + /** + Only valid if has3D is true. + + @param x,y,z listener position + @param fx,fy,fz listener's looking direction + @param ux,uy,uz listener's up direction + */ + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) = 0; + + /// Virtual destructor + virtual ~Manager() {} +}; + +}} // Namespaces + +#endif diff --git a/sound/tests/.gitignore b/sound/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/sound/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/sound/tests/Makefile b/sound/tests/Makefile new file mode 100644 index 0000000000..4cdf5ab58e --- /dev/null +++ b/sound/tests/Makefile @@ -0,0 +1,17 @@ +GCC=g++ -I../ -I../imp/ + +all: audiere_test ffmpeg_openal_test + +L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +L_OPENAL=$(shell pkg-config --libs openal) + +L_AUDIERE=-laudiere + +ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp + $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) + +audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) + +clean: + rm *_test diff --git a/sound/tests/audiere_test.cpp b/sound/tests/audiere_test.cpp new file mode 100644 index 0000000000..16b1322727 --- /dev/null +++ b/sound/tests/audiere_test.cpp @@ -0,0 +1,7 @@ +#include "audiere_imp.h" + +using namespace GOOI::Sound; + +AudiereManager mg; + +#include "common.cpp" diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp new file mode 100644 index 0000000000..78dbce5b22 --- /dev/null +++ b/sound/tests/common.cpp @@ -0,0 +1,41 @@ +// This file is included directly into the test programs + +#include +#include + +using namespace std; + +void play(const char* name, bool music=false) +{ + cout << "Playing " << name << "\n"; + + Sound *snd = NULL; + Instance *s = NULL; + + try + { + snd = mg.load(name, music); + s = snd->getInstance(false, false); + s->play(); + + while(s->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } + + if(s) s->drop(); + if(snd) snd->drop(); +} + +int main() +{ + play("cow.wav"); + play("owl.ogg", true); + return 0; +} diff --git a/sound/tests/cow.wav b/sound/tests/cow.wav new file mode 100644 index 0000000000000000000000000000000000000000..494e6c4ac12a3d318532fdf8178d72f775bc40af GIT binary patch literal 37546 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�b}Ou2(@Nod9rZet}z#nWoL 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"`&tnb`wrwjv1+m1l^X62V>RQ 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 uxXE2GB)VyFe*N* Date: Sat, 21 Nov 2009 11:41:44 +0100 Subject: [PATCH 002/269] Renamed to MANGLE --- Doxyfile | 2 +- LICENSE.txt | 2 +- README.txt | 44 +++++++++++++++--------------- sound/imp/audiere_imp.cpp | 2 +- sound/imp/audiere_imp.h | 6 ++-- sound/imp/input_ffmpeg.cpp | 2 +- sound/imp/input_ffmpeg.h | 6 ++-- sound/imp/openal_ffmpeg.h | 6 ++-- sound/imp/output_openal.cpp | 2 +- sound/imp/output_openal.h | 6 ++-- sound/imp/sound_pair.h | 6 ++-- sound/input.h | 6 ++-- sound/sound.h | 6 ++-- sound/tests/audiere_test.cpp | 2 +- sound/tests/ffmpeg_openal_test.cpp | 2 +- 15 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Doxyfile b/Doxyfile index 4dfff9188c..705b0aa321 100644 --- a/Doxyfile +++ b/Doxyfile @@ -25,7 +25,7 @@ DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. -PROJECT_NAME = GOOI +PROJECT_NAME = Mangle # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or diff --git a/LICENSE.txt b/LICENSE.txt index 1b98eeb9b9..ccfcc9f220 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Game-oriented object interfaces (GOOI) is licensed under the +Minimal Abstraction Game Layer (Mangle) is licensed under the 'zlib/libpng' license: ---- diff --git a/README.txt b/README.txt index 0b0befca55..2a0d579ec3 100644 --- a/README.txt +++ b/README.txt @@ -1,21 +1,21 @@ -Welcome to GOOI v0.1 --------------------- +Welcome to Mangle v0.1 +---------------------- Written by: Nicolay Korslund (korslund@gmail.com) License: zlib/png (see LICENSE.txt) -WWW: http://asm-soft.com/gooi/ -Documentation: http://asm-soft.com/gooi/docs +WWW: http://asm-soft.com/mangle/ +Documentation: http://asm-soft.com/mangle/docs -GOOI stands for Game-Oriented Object Interfaces. It is meant to become -a small set of generic interfaces for various game middleware +Mangle stands for Minimal Abstraction Game Layer, and it's meant to +become a small set of generic interfaces for various game middleware libraries, such as sound, input, graphics, and so on. It consists of several independent modules, one for each of these areas. These may be used together to build an entire game engine, or they can be used individually as separate libraries. -However, GOOI does NOT actually implement a game engine, or any new +However, Mangle does NOT actually implement a game engine, or any new fundamental functionality. More on that below. Currently there is only the Sound module, but more will come in the @@ -53,24 +53,23 @@ possible. What is it good for ------------------- -The main point of GOOI, as we said above, is that it connects to any +The main point of Mangle, as we said above, is that it connects to any library of your choice "behind the scenes" but provides the same, super-simple interface front-end for all of them. There can benefit you in many ways: -- If you want to use a new library that GOOI support. You don't - have to scour the net for tutorials and usage examples, since much - of the common usage code is already included in the implementation - classes. +- If you want to use a new library that Mangle support. You don't have + to scour the net for tutorials and usage examples, since much of the + common usage code is already included in the implementation classes. - If you don't want to pollute your code with library-specific code. - The GOOI interfaces can help you keep your code clean, and its user - interface is often simpler than the exteral library one. + The Mangle interfaces can help you keep your code clean, and its + user interface is often simpler than the exteral library one. - If you are creating a library that depends on a specific feature (such as sound), but you don't want to lock your users into any - specific sound library. GOOI works as an abstraction that lets your - users select their own implementation. My own Monster scripting + specific sound library. Mangle works as an abstraction that lets + your users select their own implementation. My own Monster scripting language ( http://monsterscript.net ) will use this tactic, to provide native-but-generic sound, input and GUI support, among other features. @@ -82,7 +81,7 @@ you in many ways: that your favorite backend doesn't work on all the platforms you want to reach. -The GOOI implementations are extremely light-weight - often just one +The Mangle implementations are extremely light-weight - often just one or two cpp/h pairs. You plug them directly into your program, there's no separate build step required. @@ -95,22 +94,23 @@ come crashing down, because there is no big 'system' to speak of. Past and future --------------- -GOOI started out as a spin-off from OpenMW, another project of mine +Mangle started out as a spin-off from OpenMW, another project of mine ( http://openmw.sourceforge.net ). OpenMW is an attempt to recreate the engine behind the commercial game Morrowind, using only open source software. The projects are still tightly interlinked, and the will continue to be until OpenMW is finished. That means that all near-future work on -GOOI for my part will be more or less guided by what OpenMW needs. But -I'll gladly accept external contributions that are not OpenMW-related. +Mangle for my part will be more or less guided by what OpenMW +needs. But I'll gladly accept external contributions that are not +OpenMW-related. Conclusion ---------- -As you might have guessed, GOOI is more a concept in development than -a finished library right now. +As you might have guessed, Mangle is more a concept in development +than a finished library right now. All feedback, ideas, concepts, questions and code are very welcome. Send them to: korslund@gmail.com diff --git a/sound/imp/audiere_imp.cpp b/sound/imp/audiere_imp.cpp index ea094409fc..266fcb2e25 100644 --- a/sound/imp/audiere_imp.cpp +++ b/sound/imp/audiere_imp.cpp @@ -18,7 +18,7 @@ static void fail(const std::string &msg) } using namespace audiere; -using namespace GOOI::Sound; +using namespace Mangle::Sound; AudiereManager::AudiereManager() { diff --git a/sound/imp/audiere_imp.h b/sound/imp/audiere_imp.h index 9f667a98ab..083e33258c 100644 --- a/sound/imp/audiere_imp.h +++ b/sound/imp/audiere_imp.h @@ -1,12 +1,12 @@ -#ifndef GOOI_SOUND_AUDIERE_H -#define GOOI_SOUND_AUDIERE_H +#ifndef MANGLE_SOUND_AUDIERE_H +#define MANGLE_SOUND_AUDIERE_H #include "../sound.h" #include #include -namespace GOOI { +namespace Mangle { namespace Sound { /// Implementation of Sound::Manager for Audiere diff --git a/sound/imp/input_ffmpeg.cpp b/sound/imp/input_ffmpeg.cpp index c73689b782..9ce822444b 100644 --- a/sound/imp/input_ffmpeg.cpp +++ b/sound/imp/input_ffmpeg.cpp @@ -1,7 +1,7 @@ #include "input_ffmpeg.h" #include -using namespace GOOI::Sound; +using namespace Mangle::Sound; // Static output buffer. Not thread safe, but supports multiple // streams operated from the same thread. diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h index 17c8f7352e..8c19d381e1 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/imp/input_ffmpeg.h @@ -1,5 +1,5 @@ -#ifndef GOOI_SOUND_FFMPEG_H -#define GOOI_SOUND_FFMPEG_H +#ifndef MANGLE_SOUND_FFMPEG_H +#define MANGLE_SOUND_FFMPEG_H #include "../input.h" #include @@ -11,7 +11,7 @@ extern "C" #include } -namespace GOOI { +namespace Mangle { namespace Sound { /// FFmpeg exception diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h index 92fd32553c..7e41fd8b9a 100644 --- a/sound/imp/openal_ffmpeg.h +++ b/sound/imp/openal_ffmpeg.h @@ -1,11 +1,11 @@ -#ifndef GOOI_FFMPEG_OPENAL_H -#define GOOI_FFMPEG_OPENAL_H +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H #include "sound_pair.h" #include "input_ffmpeg.h" #include "output_openal.h" -namespace GOOI { +namespace Mangle { namespace Sound { /// A PairManager filter that adds FFmpeg decoding to OpenAL diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp index f7dc11507c..c56e8c3798 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/imp/output_openal.cpp @@ -3,7 +3,7 @@ #include -using namespace GOOI::Sound; +using namespace Mangle::Sound; // ---- Helper functions and classes ---- diff --git a/sound/imp/output_openal.h b/sound/imp/output_openal.h index e2e506aa28..41f801e5a6 100644 --- a/sound/imp/output_openal.h +++ b/sound/imp/output_openal.h @@ -1,5 +1,5 @@ -#ifndef GOOI_SOUND_OPENAL_H -#define GOOI_SOUND_OPENAL_H +#ifndef MANGLE_SOUND_OPENAL_H +#define MANGLE_SOUND_OPENAL_H #include "../sound.h" @@ -7,7 +7,7 @@ #include #include -namespace GOOI { +namespace Mangle { namespace Sound { class OpenAL_Stream_Instance; diff --git a/sound/imp/sound_pair.h b/sound/imp/sound_pair.h index 751c72b1af..6b15f74611 100644 --- a/sound/imp/sound_pair.h +++ b/sound/imp/sound_pair.h @@ -1,11 +1,11 @@ -#ifndef GOOI_SOUND_PAIR_H -#define GOOI_SOUND_PAIR_H +#ifndef MANGLE_SOUND_PAIR_H +#define MANGLE_SOUND_PAIR_H #include "sound.h" #include -namespace GOOI { +namespace Mangle { namespace Sound { /** diff --git a/sound/input.h b/sound/input.h index a0d2826aa3..b030885fb4 100644 --- a/sound/input.h +++ b/sound/input.h @@ -1,10 +1,10 @@ -#ifndef GOOI_SOUND_INPUT_H -#define GOOI_SOUND_INPUT_H +#ifndef MANGLE_SOUND_INPUT_H +#define MANGLE_SOUND_INPUT_H #include #include -namespace GOOI { +namespace Mangle { namespace Sound { /// An abstract interface for a read-once stream of audio data. diff --git a/sound/sound.h b/sound/sound.h index 509efa8163..0d1aecf6e9 100644 --- a/sound/sound.h +++ b/sound/sound.h @@ -1,10 +1,10 @@ -#ifndef GOOI_SOUND_SOUND_H -#define GOOI_SOUND_SOUND_H +#ifndef MANGLE_SOUND_SOUND_H +#define MANGLE_SOUND_SOUND_H #include #include "input.h" -namespace GOOI { +namespace Mangle { namespace Sound { /// Abstract interface for sound instances diff --git a/sound/tests/audiere_test.cpp b/sound/tests/audiere_test.cpp index 16b1322727..defb8c95fe 100644 --- a/sound/tests/audiere_test.cpp +++ b/sound/tests/audiere_test.cpp @@ -1,6 +1,6 @@ #include "audiere_imp.h" -using namespace GOOI::Sound; +using namespace Mangle::Sound; AudiereManager mg; diff --git a/sound/tests/ffmpeg_openal_test.cpp b/sound/tests/ffmpeg_openal_test.cpp index 19e34f6422..28391af3ec 100644 --- a/sound/tests/ffmpeg_openal_test.cpp +++ b/sound/tests/ffmpeg_openal_test.cpp @@ -1,6 +1,6 @@ #include "openal_ffmpeg.h" -using namespace GOOI::Sound; +using namespace Mangle::Sound; OpenAL_FFM_Manager mg; From 325f2f17b35383cbcd2ce0f63c4f99bd1f463ca5 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 21 Nov 2009 11:49:31 +0100 Subject: [PATCH 003/269] Renamed sound_pair to input_filter / InputFilter --- sound/imp/{sound_pair.h => input_filter.h} | 20 ++++++++++---------- sound/imp/openal_ffmpeg.h | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) rename sound/imp/{sound_pair.h => input_filter.h} (79%) diff --git a/sound/imp/sound_pair.h b/sound/imp/input_filter.h similarity index 79% rename from sound/imp/sound_pair.h rename to sound/imp/input_filter.h index 6b15f74611..f81f1e8a5e 100644 --- a/sound/imp/sound_pair.h +++ b/sound/imp/input_filter.h @@ -1,5 +1,5 @@ -#ifndef MANGLE_SOUND_PAIR_H -#define MANGLE_SOUND_PAIR_H +#ifndef MANGLE_INPUT_FILTER_H +#define MANGLE_INPUT_FILTER_H #include "sound.h" @@ -19,15 +19,15 @@ namespace Sound { Example: \code - // Combine FFmpeg input and OpenAL output. OpenAL cannot decode - // sound files on its own. - SoundPairManager mg(new FFM_InputManager, new OpenAL_Manager); + // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot + // decode sound files on its own. + InputFilter mg(new OpenAL_Manager, new FFM_InputManager); // We can now load filenames directly. mg.load("file1.mp3"); \endcode */ -class PairManager : public Manager +class InputFilter : public Manager { protected: Manager *snd; @@ -35,14 +35,14 @@ class PairManager : public Manager public: /// Empty constructor - PairManager() {} + InputFilter() {} /// Assign an input manager and a sound manager to this object - PairManager(InputManager *_inp, Manager *_snd) - { set(_inp, _snd); } + InputFilter(Manager *_snd, InputManager *_inp) + { set(_snd, _inp); } /// Assign an input manager and a sound manager to this object - void set(InputManager *_inp, Manager *_snd) + void set(Manager *_snd, InputManager *_inp) { inp = _inp; snd = _snd; diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h index 7e41fd8b9a..80882d0f52 100644 --- a/sound/imp/openal_ffmpeg.h +++ b/sound/imp/openal_ffmpeg.h @@ -1,7 +1,7 @@ #ifndef MANGLE_FFMPEG_OPENAL_H #define MANGLE_FFMPEG_OPENAL_H -#include "sound_pair.h" +#include "input_filter.h" #include "input_ffmpeg.h" #include "output_openal.h" @@ -9,13 +9,13 @@ namespace Mangle { namespace Sound { /// A PairManager filter that adds FFmpeg decoding to OpenAL -class OpenAL_FFM_Manager : public PairManager +class OpenAL_FFM_Manager : public InputFilter { public: OpenAL_FFM_Manager() { - set(new FFM_InputManager, - new OpenAL_Manager); + set(new OpenAL_Manager, + new FFM_InputManager); } ~OpenAL_FFM_Manager() { From 7139f5284d5b5d0b87be3774baf3b663a975caf2 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 19 Dec 2009 08:26:01 +0100 Subject: [PATCH 004/269] Added Stream::InputStream interface, and a client implementation to Ogre::DataStream --- README.txt | 6 +-- stream/imp_client/iwrapper.h | 30 +++++++++++++++ stream/imp_client/ogre_datastream.h | 60 +++++++++++++++++++++++++++++ stream/input.h | 50 ++++++++++++++++++++++++ stream/tests/.gitignore | 1 + stream/tests/Makefile | 15 ++++++++ stream/tests/dummy_input.cpp | 48 +++++++++++++++++++++++ stream/tests/dummy_test.cpp | 39 +++++++++++++++++++ stream/tests/ogre_client_test.cpp | 21 ++++++++++ 9 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 stream/imp_client/iwrapper.h create mode 100644 stream/imp_client/ogre_datastream.h create mode 100644 stream/input.h create mode 100644 stream/tests/.gitignore create mode 100644 stream/tests/Makefile create mode 100644 stream/tests/dummy_input.cpp create mode 100644 stream/tests/dummy_test.cpp create mode 100644 stream/tests/ogre_client_test.cpp diff --git a/README.txt b/README.txt index 2a0d579ec3..2052d7d7b6 100644 --- a/README.txt +++ b/README.txt @@ -18,9 +18,9 @@ individually as separate libraries. However, Mangle does NOT actually implement a game engine, or any new fundamental functionality. More on that below. -Currently there is only the Sound module, but more will come in the -future (including input, 2D/3D graphics, GUI, physics, file -system/archive access, and more.) +Currently there's modules for sound and streams / archives (file +access). More will come in the future (including input, 2D/3D +graphics, GUI, physics, and more.) Main idea diff --git a/stream/imp_client/iwrapper.h b/stream/imp_client/iwrapper.h new file mode 100644 index 0000000000..ba2d1fb1fb --- /dev/null +++ b/stream/imp_client/iwrapper.h @@ -0,0 +1,30 @@ +#ifndef MANGLE_STREAM_IWRAPPER_H +#define MANGLE_STREAM_IWRAPPER_H + +#include "../input.h" +#include + +namespace Mangle { +namespace Stream { + +/** A generic wrapper class for a Stream::Input object. + + This is used by other implementations. + */ +class _IWrapper +{ + private: + bool autoDel; + + protected: + InputStream *inp; + + public: + _IWrapper(InputStream *_inp, bool _autoDel = false) + : inp(_inp), autoDel(_autoDel) { assert(inp != NULL); } + + virtual ~_IWrapper() { if(autoDel) delete inp; } +}; + +}} // namespaces +#endif diff --git a/stream/imp_client/ogre_datastream.h b/stream/imp_client/ogre_datastream.h new file mode 100644 index 0000000000..63ca66e204 --- /dev/null +++ b/stream/imp_client/ogre_datastream.h @@ -0,0 +1,60 @@ +#ifndef MANGLE_STREAM_OGRECLIENT_H +#define MANGLE_STREAM_OGRECLIENT_H + +#include +#include +#include "iwrapper.h" + +namespace Mangle { +namespace Stream { + +/** An OGRE DataStream that wraps a Mangle::Stream input. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class MangleDataStream : public Ogre::DataStream, _IWrapper +{ + void init() + { + // Get the size, if possible + if(inp->hasSize) + mSize = inp->size(); + } + + public: + /// Constructor without name + MangleDataStream(InputStream *inp, bool autoDel=false) + : _IWrapper(inp, autoDel) { init(); } + + /// Constructor for a named data stream + MangleDataStream(const Ogre::String &name, InputStream *inp, bool autoDel=false) + : _IWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } + + + // Only implement the DataStream functions we have to implement + + size_t read(void *buf, size_t count) + { return inp->read(buf,count); } + + void skip(long count) + { + assert(inp->isSeekable && inp->hasPosition); + inp->seek(inp->tell() + count); + } + + void seek(size_t pos) + { assert(inp->isSeekable); inp->seek(pos); } + + size_t tell() const + { assert(inp->hasPosition); return inp->tell(); } + + bool eof() const { return inp->eof(); } + + /// Does nothing + void close() {} +}; + +}} // namespaces +#endif diff --git a/stream/input.h b/stream/input.h new file mode 100644 index 0000000000..0a178d8fc6 --- /dev/null +++ b/stream/input.h @@ -0,0 +1,50 @@ +#ifndef MANGLE_STREAM_INPUT_H +#define MANGLE_STREAM_INPUT_H + +#include + +namespace Mangle { +namespace Stream { + +/// An abstract interface for a stream data. +class InputStream +{ + public: + // Feature options. These should be set in the constructor. + + /// If true, seek() works + bool isSeekable; + + /// If true, tell() works + bool hasPosition; + + /// If true, size() works + bool hasSize; + + /// Virtual destructor + virtual ~InputStream() {} + + /** Read a given number of bytes from the stream. Returns the actual + number read. If the return value is less than count, then the + stream is empty or an error occured. + */ + virtual size_t read(void* buf, size_t count) = 0; + + /// Seek to an absolute position in this stream. Not all streams are + /// seekable. + virtual void seek(size_t pos) = 0; + + /// Get the current position in the stream. Non-seekable streams are + /// not required to keep track of this. + virtual size_t tell() const = 0; + + /// Return the total size of the stream. For streams where this is + /// not applicable, size() should return zero. + virtual size_t size() const = 0; + + /// Returns true if the stream is empty + virtual bool eof() const = 0; +}; + +}} // namespaces +#endif diff --git a/stream/tests/.gitignore b/stream/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/stream/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/stream/tests/Makefile b/stream/tests/Makefile new file mode 100644 index 0000000000..7d70414699 --- /dev/null +++ b/stream/tests/Makefile @@ -0,0 +1,15 @@ +GCC=g++ -I../ -I../imp_client/ + +all: ogre_client_test dummy_test + +I_OGRE=$(shell pkg-config --cflags OGRE) +L_OGRE=$(shell pkg-config --libs OGRE) + +ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/ogre_datastream.h + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) + +dummy_test: dummy_test.cpp dummy_input.cpp ../input.h + $(GCC) $< -o $@ + +clean: + rm *_test diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp new file mode 100644 index 0000000000..4625540ec3 --- /dev/null +++ b/stream/tests/dummy_input.cpp @@ -0,0 +1,48 @@ +// This file is shared between several test programs +#include "input.h" +#include +#include + +using namespace Mangle::Stream; + +// A simple dummy stream +const char _data[12] = "hello world"; + +class DummyInput : public InputStream +{ +private: + int pos; + +public: + DummyInput() : pos(0) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void *buf, size_t count) + { + assert(pos >= 0 && pos <= 11); + if(count+pos > 11) + count = 11-pos; + assert(count <= 11); + + memcpy(buf, _data+pos, count); + pos += count; + + assert(pos >= 0 && pos <= 11); + return count; + } + + void seek(size_t npos) + { + if(npos > 11) npos = 11; + pos = npos; + } + + size_t tell() const { return pos; } + size_t size() const { return 11; } + + bool eof() const { return pos == 11; } +}; diff --git a/stream/tests/dummy_test.cpp b/stream/tests/dummy_test.cpp new file mode 100644 index 0000000000..64dd8f19b8 --- /dev/null +++ b/stream/tests/dummy_test.cpp @@ -0,0 +1,39 @@ +#include "dummy_input.cpp" + +#include +#include + +using namespace std; + +int main() +{ + InputStream *inp = new DummyInput(); + + cout << "Size: " << inp->size() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking...\n"; + inp->seek(3); + cout << "Pos: " << inp->tell() << endl; + char data[12]; + memset(data, 0, 12); + cout << "Reading: " << inp->read(data, 4) << endl; + cout << "Four bytes: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking again...\n"; + inp->seek(33); + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << "\nSeek to 6\n"; + inp->seek(6); + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Over-reading: " << inp->read(data, 200) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + inp->seek(0); + cout << "Finally, reading the entire string: " << inp->read(data,11) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + + return 0; +} diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp new file mode 100644 index 0000000000..b6a46ad3bb --- /dev/null +++ b/stream/tests/ogre_client_test.cpp @@ -0,0 +1,21 @@ +#include "dummy_input.cpp" +#include "ogre_datastream.h" +#include + +using namespace Ogre; +using namespace std; + +int main() +{ + InputStream *inp = new DummyInput(); + DataStreamPtr p(new MangleDataStream("hello", inp, true)); + cout << "Name: " << p->getName() << endl; + cout << "As string: " << p->getAsString() << endl; + cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; + p->seek(0); + cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; + p->skip(5); + p->skip(-2); + cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; + return 0; +} From bbb44e07bfa1d3cb2161ee4e1c2156ca135d2f45 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 19 Dec 2009 20:53:53 +0100 Subject: [PATCH 005/269] Added VFS+tests, make Ogre::Archive client implementation. --- Doxyfile | 2 +- stream/tests/dummy_input.cpp | 2 +- vfs/imp_client/ogre_archive.cpp | 83 +++++++++++++++++++++++ vfs/imp_client/ogre_archive.h | 53 +++++++++++++++ vfs/imp_client/wrapper.h | 30 +++++++++ vfs/tests/.gitignore | 1 + vfs/tests/Makefile | 15 +++++ vfs/tests/dummy_test.cpp | 40 +++++++++++ vfs/tests/dummy_vfs.cpp | 115 ++++++++++++++++++++++++++++++++ vfs/tests/ogre_client_test.cpp | 39 +++++++++++ vfs/vfs.h | 79 ++++++++++++++++++++++ 11 files changed, 457 insertions(+), 2 deletions(-) create mode 100644 vfs/imp_client/ogre_archive.cpp create mode 100644 vfs/imp_client/ogre_archive.h create mode 100644 vfs/imp_client/wrapper.h create mode 100644 vfs/tests/.gitignore create mode 100644 vfs/tests/Makefile create mode 100644 vfs/tests/dummy_test.cpp create mode 100644 vfs/tests/dummy_vfs.cpp create mode 100644 vfs/tests/ogre_client_test.cpp create mode 100644 vfs/vfs.h diff --git a/Doxyfile b/Doxyfile index 705b0aa321..f2a1c74552 100644 --- a/Doxyfile +++ b/Doxyfile @@ -564,7 +564,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = sound +INPUT = sound stream vfs # 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 diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp index 4625540ec3..0bfe5ad52a 100644 --- a/stream/tests/dummy_input.cpp +++ b/stream/tests/dummy_input.cpp @@ -1,5 +1,5 @@ // This file is shared between several test programs -#include "input.h" +#include "../input.h" #include #include diff --git a/vfs/imp_client/ogre_archive.cpp b/vfs/imp_client/ogre_archive.cpp new file mode 100644 index 0000000000..c9dccab902 --- /dev/null +++ b/vfs/imp_client/ogre_archive.cpp @@ -0,0 +1,83 @@ +#include "ogre_archive.h" + +#include "../../stream/imp_client/ogre_datastream.h" + +using namespace Mangle::VFS; +using namespace Mangle::Stream; + +Ogre::DataStreamPtr MangleArchive::open(const Ogre::String& filename) const +{ + return Ogre::DataStreamPtr(new MangleDataStream + (filename, vfs->open(filename), true)); +} + +static void fill(Ogre::FileInfoList &out, FileInfoList &in) +{ + int size = in.size(); + out.resize(size); + + for(int i=0; ilist("", recursive, dirs); + Ogre::StringVector *res = new Ogre::StringVector; + + fill(*res, lst); + + return Ogre::StringVectorPtr(res); +} + +Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs) +{ + FileInfoList lst = vfs->list("", recursive, dirs); + Ogre::FileInfoList *res = new Ogre::FileInfoList; + + fill(*res, lst); + + return Ogre::FileInfoListPtr(res); +} + +// Find functions will only work if vfs->hasFind is set. +Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern, + bool recursive, + bool dirs) +{ + assert(vfs->hasFind); + FileInfoList lst = vfs->find(pattern, recursive, dirs); + Ogre::StringVector *res = new Ogre::StringVector; + + fill(*res, lst); + + return Ogre::StringVectorPtr(res); +} + +Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, + bool recursive, + bool dirs) +{ + assert(vfs->hasFind); + FileInfoList lst = vfs->find(pattern, recursive, dirs); + Ogre::FileInfoList *res = new Ogre::FileInfoList; + + fill(*res, lst); + + return Ogre::FileInfoListPtr(res); +} diff --git a/vfs/imp_client/ogre_archive.h b/vfs/imp_client/ogre_archive.h new file mode 100644 index 0000000000..f72398a2de --- /dev/null +++ b/vfs/imp_client/ogre_archive.h @@ -0,0 +1,53 @@ +#ifndef MANGLE_VFS_OGRECLIENT_H +#define MANGLE_VFS_OGRECLIENT_H + +#include +#include +#include "wrapper.h" + +namespace Mangle { +namespace VFS { + +/** An OGRE Archive implementation that wraps a Mangle::VFS + filesystem. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class MangleArchive : public Ogre::Archive, _Wrapper +{ + public: + /// Constructor without name + MangleArchive(VFS *vfs, const std::string &name, + const std::string &archType = "Mangle", + bool autoDel=false) + : _Wrapper(vfs, autoDel), Ogre::Archive(name, archType) {} + + bool isCaseSensitive() const { return vfs->isCaseSensitive; } + + // These do nothing. You have to load / unload the archive manually. + void load() {} + void unload() {} + + bool exists(const Ogre::String& filename) + { return vfs->isFile(filename); } + + time_t getModifiedTime(const Ogre::String& filename) + { return vfs->stat(filename).time; } + + Ogre::DataStreamPtr open(const Ogre::String& filename) const; + + Ogre::StringVectorPtr list(bool recursive = true, bool dirs = false); + Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false); + + // Find functions will only work if vfs->hasFind is set. + Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true, + bool dirs = false); + Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, + bool recursive = true, + bool dirs = false); +}; + +}} // namespaces +#endif diff --git a/vfs/imp_client/wrapper.h b/vfs/imp_client/wrapper.h new file mode 100644 index 0000000000..357bc8b4f4 --- /dev/null +++ b/vfs/imp_client/wrapper.h @@ -0,0 +1,30 @@ +#ifndef MANGLE_VFS_WRAPPER_H +#define MANGLE_VFS_WRAPPER_H + +#include "../vfs.h" +#include + +namespace Mangle { +namespace VFS { + +/** A generic wrapper class for a VFS::VFS object. + + This is used by other implementations. + */ +class _Wrapper +{ + private: + bool autoDel; + + protected: + VFS *vfs; + + public: + _Wrapper(VFS *_vfs, bool _autoDel = false) + : vfs(_vfs), autoDel(_autoDel) { assert(vfs != NULL); } + + virtual ~_Wrapper() { if(autoDel) delete vfs; } +}; + +}} // namespaces +#endif diff --git a/vfs/tests/.gitignore b/vfs/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/vfs/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile new file mode 100644 index 0000000000..b1fb55f13d --- /dev/null +++ b/vfs/tests/Makefile @@ -0,0 +1,15 @@ +GCC=g++ -I../ -I../imp_client/ + +all: dummy_test ogre_client_test + +I_OGRE=$(shell pkg-config --cflags OGRE) +L_OGRE=$(shell pkg-config --libs OGRE) + +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp + $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) + +dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h + $(GCC) $< -o $@ + +clean: + rm *_test diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp new file mode 100644 index 0000000000..e5659d62f6 --- /dev/null +++ b/vfs/tests/dummy_test.cpp @@ -0,0 +1,40 @@ +#include "dummy_vfs.cpp" + +#include +#include + +using namespace std; + +void print(FileInfo inf) +{ + cout << "name: " << inf.name << endl; + cout << "basename: " << inf.basename << endl; + cout << "isDir: " << inf.isDir << endl; + cout << "size: " << inf.size << endl; + cout << "time: " << inf.time << endl; +} + +void print(FileInfoList lst) +{ + for(int i=0; isize() << endl; + + return 0; +} diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp new file mode 100644 index 0000000000..d8b259f28b --- /dev/null +++ b/vfs/tests/dummy_vfs.cpp @@ -0,0 +1,115 @@ +// This file is shared between several test programs +#include "vfs.h" +#include +#include + +#include "../../stream/tests/dummy_input.cpp" + +using namespace Mangle::VFS; + +class DummyVFS : public VFS +{ +public: + DummyVFS() + { + hasFind = false; + isCaseSensitive = true; + } + + // We only support opening 'file1' at the moment. + Mangle::Stream::InputStream *open(const std::string &name) + { + assert(name == "file1"); + return new DummyInput(); + } + + bool isFile(const std::string &name) const + { + return (name == "file1" || + name == "dir/file2"); + } + + bool isDir(const std::string &name) const + { + return name == "dir"; + } + + /// Get info about a single file + FileInfo stat(const std::string &name) const + { + FileInfo fi; + fi.name = name; + fi.time = 0; + + if(isFile(name)) + { + if(name == "dir/file2") + { + fi.basename = "file2"; + fi.size = 2; + } + else + { + fi.basename = "file1"; + fi.size = 1; + } + fi.isDir = false; + } + else if(isDir(name)) + { + fi.basename = "dir"; + fi.isDir = true; + fi.size = 0; + } + else assert(0); + + return fi; + } + + /// List all entries in a given directory. A blank dir should be + /// interpreted as a the root/current directory of the archive. If + /// dirs is true, list directories instead of files. + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const + { + assert(dir == ""); + + FileInfoList fl; + + FileInfo fi; + + if(!dirs) + { + fi.name = "file1"; + fi.basename = "file1"; + fi.isDir = false; + fi.size = 1; + fi.time = 0; + fl.push_back(fi); + + if(recurse) + { + fi.name = "dir/file2"; + fi.basename = "file2"; + fi.size = 2; + fl.push_back(fi); + } + } + else + { + fi.name = "dir"; + fi.basename = "dir"; + fi.isDir = true; + fi.size = 0; + fi.time = 0; + fl.push_back(fi); + } + return fl; + } + + FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const + { assert(0); return FileInfoList(); } +}; diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp new file mode 100644 index 0000000000..92cb73eaa8 --- /dev/null +++ b/vfs/tests/ogre_client_test.cpp @@ -0,0 +1,39 @@ +#include "dummy_vfs.cpp" +#include "ogre_archive.h" +#include + +using namespace Ogre; +using namespace std; + +void print(StringVectorPtr lst) +{ + int s = lst->size(); + + for(int i=0; isize() << endl; + cout << "contents: " << file->getAsString() << endl; + + return 0; +} diff --git a/vfs/vfs.h b/vfs/vfs.h new file mode 100644 index 0000000000..345b4ccaef --- /dev/null +++ b/vfs/vfs.h @@ -0,0 +1,79 @@ +#ifndef MANGLE_VFS_H +#define MANGLE_VFS_H + +#include "../stream/input.h" +#include +#include + +namespace Mangle { +namespace VFS { + +/// Generic file info structure +struct FileInfo +{ + /// Full name, including path + std::string name; + + /// Base name, not including path + std::string basename; + + /// Is this a directory? + bool isDir; + + /// File size + size_t size; + + /// Last modification date + time_t time; +}; + +typedef std::vector FileInfoList; + +/** An interface to any file system or other provider of named data + streams +*/ +class VFS +{ + public: + // Feature options. These should be set in the constructor. + + /// If true, the find*() functions work + bool hasFind; + + /// If true, the file system is case sensitive + bool isCaseSensitive; + + /// Virtual destructor + virtual ~VFS() {} + + /// Open a new data stream. Deleting the object should be enough to + /// close it. + virtual Stream::InputStream *open(const std::string &name) = 0; + + /// Check for the existence of a file + virtual bool isFile(const std::string &name) const = 0; + + /// Check for the existence of a directory + virtual bool isDir(const std::string &name) const = 0; + + /// Get info about a single file + virtual FileInfo stat(const std::string &name) const = 0; + + /// List all entries in a given directory. A blank dir should be + /// interpreted as a the root/current directory of the archive. If + /// dirs is true, list directories instead of files. + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const = 0; + + /// Find files after a given pattern. Wildcards (*) are + /// supported. Only valid if 'hasFind' is true. Don't implement your + /// own pattern matching here if the backend doesn't support it + /// natively; use a filter instead. + virtual FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const = 0; +}; + +}} // namespaces +#endif From dc0b7368461361db21ec7ca94b4f380f7c6dcee6 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 21 Dec 2009 21:51:57 +0100 Subject: [PATCH 006/269] Created separate Audiere sound input class --- sound/imp/input_audiere.cpp | 119 ++++++++++++++++++++++++++++ sound/imp/input_audiere.h | 52 ++++++++++++ sound/imp/openal_audiere.h | 29 +++++++ sound/imp/openal_ffmpeg.h | 2 +- sound/tests/Makefile | 6 +- sound/tests/openal_audiere_test.cpp | 7 ++ 6 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 sound/imp/input_audiere.cpp create mode 100644 sound/imp/input_audiere.h create mode 100644 sound/imp/openal_audiere.h create mode 100644 sound/tests/openal_audiere_test.cpp diff --git a/sound/imp/input_audiere.cpp b/sound/imp/input_audiere.cpp new file mode 100644 index 0000000000..5ca85ffd9b --- /dev/null +++ b/sound/imp/input_audiere.cpp @@ -0,0 +1,119 @@ +#include "input_audiere.h" +#include + +// Exception handling +class Audiere_Exception : public std::exception +{ + std::string msg; + + public: + + Audiere_Exception(const std::string &m) : msg(m) {} + ~Audiere_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw Audiere_Exception("Audiere exception: " + msg); +} + +using namespace audiere; +using namespace Mangle::Sound; + +// --- InputManager --- + +InputSource *AudiereInput::load(const std::string &file) +{ return new AudiereSource(file); } + +// --- InputSource --- + +AudiereSource::AudiereSource(const std::string &file) +{ + SampleSourcePtr sample = OpenSampleSource(file.c_str()); + if(!sample) + fail("Couldn't load file " + file); + + buf = CreateSampleBuffer(sample); +} + +InputStream *AudiereSource::getStream() +{ + return new AudiereStream(buf->openStream()); +} + +// --- InputStream --- + +AudiereStream::AudiereStream(SampleSourcePtr _sample) + : sample(_sample), pullSize(0) +{ + assert(sample); + + SampleFormat fmt; + int channels, rate; + sample->getFormat(channels, rate, fmt); + + // Calculate the size of one frame + frameSize = GetSampleSize(fmt) * channels; + + // Make sure that our pullover hack will work. Increase this size if + // this doesn't work in all cases. + assert(frameSize <= PSIZE); +} + +void AudiereStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + SampleFormat fmt; + sample->getFormat(*channels, *rate, fmt); + if(fmt == SF_U8) + *bits = 8; + else if(fmt == SF_S16) + *bits = 16; + else assert(0); +} + +/* + Get data. Since Audiere operates with frames, not bytes, there's a + little conversion magic going on here. We need to make sure we're + reading a whole number of frames - if not, we need to store the + remainding part of the last frame and remember it for the next read + operation. + + */ +uint32_t AudiereStream::getData(void *_data, uint32_t length) +{ + char *data = (char*)_data; + + // Move the remains from the last operation first + if(pullSize) + { + // pullSize is how much was stored the last time, so skip that. + memcpy(data, pullOver+pullSize, PSIZE-pullSize); + length -= pullSize; + data += pullSize; + } + + // Determine the overshoot up front + pullSize = length % frameSize; + + // Number of whole frames + int frames = length / frameSize; + + // Read the data + int res = sample->read(frames, data); + + // Are we missing data? If resread(1, pullOver) != 0)) + { + // Now, move as much of it as we can fit into the output + // data + memcpy(data+length-pullSize, pullOver, pullSize); + } + else pullSize = 0; + + // Return the total number of bytes stored + return frameSize*res + pullSize; +} diff --git a/sound/imp/input_audiere.h b/sound/imp/input_audiere.h new file mode 100644 index 0000000000..5acc1a73d6 --- /dev/null +++ b/sound/imp/input_audiere.h @@ -0,0 +1,52 @@ +#ifndef MANGLE_SOUND_AUDIERE_INPUT_H +#define MANGLE_SOUND_AUDIERE_INPUT_H + +#include "../input.h" + +#include + +namespace Mangle { +namespace Sound { + +/// Implementation of Sound::InputManager for Audiere +class AudiereInput : public InputManager +{ + public: + InputSource *load(const std::string &file); +}; + +/// Audiere InputSource implementation +class AudiereSource : public InputSource +{ + audiere::SampleBufferPtr buf; + + public: + AudiereSource(const std::string &file); + InputStream *getStream(); + void drop() { delete this; } +}; + +/// Audiere InputStream implementation +class AudiereStream : public InputStream +{ + audiere::SampleSourcePtr sample; + int frameSize; // Size of one frame, in bytes + + static const int PSIZE = 10; + + // Temporary storage for unevenly read samples. See the comment for + // getData() in the .cpp file. + char pullOver[PSIZE]; + // How much of the above buffer is in use + int pullSize; + + public: + AudiereStream(audiere::SampleSourcePtr _sample); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + uint32_t getData(void *data, uint32_t length); + void drop() { delete this; } +}; + +}} // Namespace +#endif diff --git a/sound/imp/openal_audiere.h b/sound/imp/openal_audiere.h new file mode 100644 index 0000000000..65947b22fc --- /dev/null +++ b/sound/imp/openal_audiere.h @@ -0,0 +1,29 @@ +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H + +#include "input_filter.h" +#include "input_audiere.h" +#include "output_openal.h" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds audiere decoding to OpenAL. Audiere has +/// it's own output, but OpenAL sports 3D and other advanced features. +class OpenAL_Audiere_Manager : public InputFilter +{ + public: + OpenAL_Audiere_Manager() + { + set(new OpenAL_Manager, + new AudiereInput); + } + ~OpenAL_Audiere_Manager() + { + delete snd; + delete inp; + } +}; + +}} +#endif diff --git a/sound/imp/openal_ffmpeg.h b/sound/imp/openal_ffmpeg.h index 80882d0f52..179d3cb702 100644 --- a/sound/imp/openal_ffmpeg.h +++ b/sound/imp/openal_ffmpeg.h @@ -8,7 +8,7 @@ namespace Mangle { namespace Sound { -/// A PairManager filter that adds FFmpeg decoding to OpenAL +/// An InputFilter that adds FFmpeg decoding to OpenAL class OpenAL_FFM_Manager : public InputFilter { public: diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 4cdf5ab58e..22da35432f 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,15 +1,17 @@ GCC=g++ -I../ -I../imp/ -all: audiere_test ffmpeg_openal_test +all: audiere_test ffmpeg_openal_test openal_audiere_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) - L_AUDIERE=-laudiere ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) +openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) + audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp $(GCC) $^ -o $@ $(L_AUDIERE) diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp new file mode 100644 index 0000000000..c9011f48e2 --- /dev/null +++ b/sound/tests/openal_audiere_test.cpp @@ -0,0 +1,7 @@ +#include "openal_audiere.h" + +using namespace Mangle::Sound; + +OpenAL_Audiere_Manager mg; + +#include "common.cpp" From 721f3b139bd4779230fa01d453b0871a8076e5f7 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 22 Dec 2009 13:04:44 +0100 Subject: [PATCH 007/269] Added Stream input method to sound interfaces (not implemented, NOT TESTED YET) --- sound/imp/audiere_imp.cpp | 1 + sound/imp/audiere_imp.h | 4 ++++ sound/imp/input_ffmpeg.cpp | 2 ++ sound/imp/input_ffmpeg.h | 3 +++ sound/imp/input_filter.h | 33 +++++++++++++++++++-------------- sound/imp/output_openal.cpp | 3 +++ sound/imp/output_openal.h | 1 + sound/input.h | 8 ++++++++ sound/sound.h | 15 +++++++++++++++ 9 files changed, 56 insertions(+), 14 deletions(-) diff --git a/sound/imp/audiere_imp.cpp b/sound/imp/audiere_imp.cpp index 266fcb2e25..ecdb0580ad 100644 --- a/sound/imp/audiere_imp.cpp +++ b/sound/imp/audiere_imp.cpp @@ -27,6 +27,7 @@ AudiereManager::AudiereManager() canRepeatStream = true; canLoadFile = true; canLoadSource = false; + canLoadStream = false; device = OpenDevice(""); diff --git a/sound/imp/audiere_imp.h b/sound/imp/audiere_imp.h index 083e33258c..480611ce3d 100644 --- a/sound/imp/audiere_imp.h +++ b/sound/imp/audiere_imp.h @@ -19,6 +19,10 @@ class AudiereManager : public Manager virtual Sound *load(const std::string &file, bool stream=false); + /// not implemented yet + virtual Sound *load(Stream::InputStream *input, bool stream=false) + { assert(0); } + /// disabled virtual Sound *load(InputSource *input, bool stream=false) { assert(0); } diff --git a/sound/imp/input_ffmpeg.cpp b/sound/imp/input_ffmpeg.cpp index 9ce822444b..f29ddfdb64 100644 --- a/sound/imp/input_ffmpeg.cpp +++ b/sound/imp/input_ffmpeg.cpp @@ -32,6 +32,8 @@ FFM_InputManager::FFM_InputManager() av_log_set_level(AV_LOG_ERROR); init = true; } + + canLoadStream = false; } InputSource *FFM_InputManager::load(const std::string &file) diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h index 8c19d381e1..cc8feffd40 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/imp/input_ffmpeg.h @@ -34,6 +34,9 @@ class FFM_InputManager : public InputManager public: FFM_InputManager(); virtual InputSource *load(const std::string &file); + + /// not supported + virtual InputSource *load(Stream::InputStream *input) { assert(0); } }; /// FFMpeg implementation of InputSource diff --git a/sound/imp/input_filter.h b/sound/imp/input_filter.h index f81f1e8a5e..c3da8599d2 100644 --- a/sound/imp/input_filter.h +++ b/sound/imp/input_filter.h @@ -43,26 +43,31 @@ class InputFilter : public Manager /// Assign an input manager and a sound manager to this object void set(Manager *_snd, InputManager *_inp) - { - inp = _inp; - snd = _snd; + { + inp = _inp; + snd = _snd; - needsUpdate = snd->needsUpdate; - has3D = snd->has3D; - canRepeatStream = snd->canRepeatStream; + // Set capabilities + needsUpdate = snd->needsUpdate; + has3D = snd->has3D; + canRepeatStream = snd->canRepeatStream; + canLoadStream = inp->canLoadStream; - // Both these should be true, or the use of this class is pretty - // pointless - canLoadSource = snd->canLoadSource; - canLoadFile = canLoadSource; - assert(canLoadSource && canLoadFile); - } + // Both these should be true, or the use of this class is pretty + // pointless + canLoadSource = snd->canLoadSource; + canLoadFile = canLoadSource; + assert(canLoadSource && canLoadFile); + } virtual Sound *load(const std::string &file, bool stream=false) - { return load(inp->load(file), stream); } + { return load(inp->load(file), stream); } + + virtual Sound *load(Stream::InputStream *input, bool stream=false) + { return load(inp->load(input), stream); } virtual Sound *load(InputSource *input, bool stream=false) - { return snd->load(input, stream); } + { return snd->load(input, stream); } virtual void update() { snd->update(); } virtual void setListenerPos(float x, float y, float z, diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp index c56e8c3798..190d0638d3 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/imp/output_openal.cpp @@ -97,6 +97,9 @@ OpenAL_Manager::~OpenAL_Manager() Sound *OpenAL_Manager::load(const std::string &file, bool stream) { assert(0 && "OpenAL cannot decode files"); } +Sound *OpenAL_Manager::load(Stream::InputStream*,bool) +{ assert(0 && "OpenAL cannot decode streams"); } + Sound *OpenAL_Manager::load(InputSource *source, bool stream) { return new OpenAL_Sound(source, this, stream); } diff --git a/sound/imp/output_openal.h b/sound/imp/output_openal.h index 41f801e5a6..bf92197df2 100644 --- a/sound/imp/output_openal.h +++ b/sound/imp/output_openal.h @@ -26,6 +26,7 @@ public: void remove_stream(LST::iterator); virtual Sound *load(const std::string &file, bool stream=false); + virtual Sound *load(Stream::InputStream *input, bool stream=false); virtual Sound *load(InputSource* input, bool stream=false); virtual void update(); virtual void setListenerPos(float x, float y, float z, diff --git a/sound/input.h b/sound/input.h index b030885fb4..d27de97a63 100644 --- a/sound/input.h +++ b/sound/input.h @@ -4,6 +4,8 @@ #include #include +#include "../stream/input.h" + namespace Mangle { namespace Sound { @@ -64,9 +66,15 @@ class InputSource class InputManager { public: + /// If true, the stream version of load() works + bool canLoadStream; + /// Load a sound input source from file virtual InputSource *load(const std::string &file) = 0; + /// Load a sound input source from stream (if canLoadStream is true) + virtual InputSource *load(Stream::InputStream *input) = 0; + /// Virtual destructor virtual ~InputManager() {} }; diff --git a/sound/sound.h b/sound/sound.h index 0d1aecf6e9..90407141ed 100644 --- a/sound/sound.h +++ b/sound/sound.h @@ -4,6 +4,8 @@ #include #include "input.h" +#include "../stream/input.h" + namespace Mangle { namespace Sound { @@ -115,6 +117,9 @@ class Manager /// true if we can load sounds from an InputSource bool canLoadSource; + /// If true, we can lound sound files from a Stream + bool canLoadStream; + /** @brief Load a sound from an input source. Only valid if canLoadSource is true. @@ -132,6 +137,16 @@ class Manager */ virtual Sound *load(InputSource *input, bool stream=false) = 0; + /** + @brief Load a sound directly from file. Only valid if canLoadStream + is true. + + @param input audio file stream + @param stream true if the file should be streamed + @see load(InputSource*,bool) + */ + virtual Sound *load(Stream::InputStream *input, bool stream=false) = 0; + /** @brief Load a sound directly from file. Only valid if canLoadFile is true. From 985e3847e0861bb39dfc277d8fb1afebf3190c89 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 22 Dec 2009 13:22:32 +0100 Subject: [PATCH 008/269] Added audiere File client. NOT TESTED. --- stream/imp_client/audiere_file.cpp | 39 ++++++++++++++++++++++++++++++ stream/imp_client/audiere_file.h | 35 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 stream/imp_client/audiere_file.cpp create mode 100644 stream/imp_client/audiere_file.h diff --git a/stream/imp_client/audiere_file.cpp b/stream/imp_client/audiere_file.cpp new file mode 100644 index 0000000000..ea8142eb49 --- /dev/null +++ b/stream/imp_client/audiere_file.cpp @@ -0,0 +1,39 @@ +#include "audiere_file.h" + +using namespace audiere; +using namespace Mangle::Stream; + +bool AudiereFile::seek(int pos, SeekMode mode) +{ + assert(inp->isSeekable); + assert(inp->hasPosition); + + if(mode == BEGIN) + { + // Absolute position + inp->seek(pos); + return inp->tell() == pos; + } + if(mode == CURRENT) + { + // Current position + int cpos = inp->tell(); + + // Seek to a elative position + inp->seek(cpos + pos); + return inp->tell() == (pos+cpos); + } + if(mode == END) + { + // Seeking from the end. This requires that we're able to get + // the entire size of the file. The pos also has to be + // non-positive. + assert(inp->hasSize); + assert(pos <= 0); + + size_t epos = inp->size(); + inp->seek(epos + pos); + return inp->tell() == (epos+pos); + } + assert(0 && "invalid seek mode"); +} diff --git a/stream/imp_client/audiere_file.h b/stream/imp_client/audiere_file.h new file mode 100644 index 0000000000..201a7629ac --- /dev/null +++ b/stream/imp_client/audiere_file.h @@ -0,0 +1,35 @@ +#ifndef MANGLE_STREAM_AUDIERECLIENT_H +#define MANGLE_STREAM_AUDIERECLIENT_H + +#include +#include +#include "iwrapper.h" + +namespace Mangle { +namespace Stream { + +/** @brief An Audiere::File that wraps a Mangle::Stream input. + + This lets Audiere read sound files from any generic archive or + file manager that supports Mangle streams. + */ +class AudiereFile : public audiere::File, _IWrapper +{ + public: + AudiereFile(InputStream *inp, bool autoDel=false) + : _IWrapper(inp, autoDel) {} + + /// Read 'count' bytes, return bytes successfully read + int read(void *buf, int count) + { return inp->read(buf,count); } + + /// Seek, relative to specified seek mode. Returns true if successful. + bool seek(int pos, audiere::SeekMode mode); + + /// Get current position + int tell() + { assert(inp->hasPosition); return inp->tell(); } +}; + +}} // namespaces +#endif From 008b4c64aaf348b34224f69bc248df27267144fe Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 22 Dec 2009 16:58:19 +0100 Subject: [PATCH 009/269] Added OGRE server prototype - NOT DONE --- stream/imp_server/ogre_datastream.h | 35 ++++++++++++++++ vfs/imp_client/ogre_archive.h | 1 - vfs/imp_server/ogre_vfs.h | 62 +++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 stream/imp_server/ogre_datastream.h create mode 100644 vfs/imp_server/ogre_vfs.h diff --git a/stream/imp_server/ogre_datastream.h b/stream/imp_server/ogre_datastream.h new file mode 100644 index 0000000000..ef922fa7f8 --- /dev/null +++ b/stream/imp_server/ogre_datastream.h @@ -0,0 +1,35 @@ +#ifndef MANGLE_STREAM_OGRESERVER_H +#define MANGLE_STREAM_OGRESERVER_H + +#include + +namespace Mangle { +namespace Stream { + +/** A Stream wrapping an OGRE DataStream. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class OgreStream : public InputStream +{ + Ogre::DataStreamPtr inp; + + public: + OgreStream(Ogre::DataStreamPtr _inp) : inp(_inp) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void *buf, size_t count) { return inp->read(buf,count); } + void seek(size_t pos) { inp->seek(pos); } + size_t tell() const { return inp->tell(); } + size_t size() const { return inp->size(); } + bool eof() const { return inp->eof(); } +}; + +}} // namespaces +#endif diff --git a/vfs/imp_client/ogre_archive.h b/vfs/imp_client/ogre_archive.h index f72398a2de..70e6045c4e 100644 --- a/vfs/imp_client/ogre_archive.h +++ b/vfs/imp_client/ogre_archive.h @@ -18,7 +18,6 @@ namespace VFS { class MangleArchive : public Ogre::Archive, _Wrapper { public: - /// Constructor without name MangleArchive(VFS *vfs, const std::string &name, const std::string &archType = "Mangle", bool autoDel=false) diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/imp_server/ogre_vfs.h new file mode 100644 index 0000000000..98cf5da79b --- /dev/null +++ b/vfs/imp_server/ogre_vfs.h @@ -0,0 +1,62 @@ +#ifndef MANGLE_VFS_OGRECLIENT_H +#define MANGLE_VFS_OGRECLIENT_H + +#include + +namespace Mangle { +namespace VFS { + +/** @brief An interface into the OGRE VFS system. + + This class does not wrap a single Ogre::Archive, but rather the + entire resource set available to Ogre. You can use this class to + tap into all paths, Zip files, custom archives on so on that have + been inserted into Ogre as resource locations. + + This class is currently a work in progres, it will not compile as + it stands. + + This has been built and tested against OGRE 1.6.2. You might have + to make your own modifications if you're working with newer (or + older) versions. + */ +class OgreVFS : public VFS +{ + public: + OgreVFS() + { + hasFind = true; + isCaseSensitive = true; + } + + /// Open a new data stream. Deleting the object should be enough to + /// close it. + virtual Stream::InputStream *open(const std::string &name); + + /// Check for the existence of a file + virtual bool isFile(const std::string &name) const; + + /// Check for the existence of a directory + virtual bool isDir(const std::string &name) const; + + /// Get info about a single file + virtual FileInfo stat(const std::string &name) const; + + /// List all entries in a given directory. A blank dir should be + /// interpreted as a the root/current directory of the archive. If + /// dirs is true, list directories instead of files. + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const; + + /// Find files after a given pattern. Wildcards (*) are + /// supported. Only valid if 'hasFind' is true. Don't implement your + /// own pattern matching here if the backend doesn't support it + /// natively; use a filter instead (not implemented yet.) + virtual FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const; +}; + +}} // namespaces +#endif From c54301fc1c2e15010d21d98a7dfb45b9d492686f Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 24 Dec 2009 11:08:18 +0100 Subject: [PATCH 010/269] Finished merge, created AudiereFile test --- sound/imp/input_audiere.cpp | 8 +++++++ sound/imp/input_audiere.h | 6 +++++ sound/imp/input_ffmpeg.h | 1 + sound/imp/output_openal.cpp | 1 + stream/imp_client/audiere_file.h | 4 ++-- stream/tests/Makefile | 6 ++++- stream/tests/audiere_client_test.cpp | 33 ++++++++++++++++++++++++++++ 7 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 stream/tests/audiere_client_test.cpp diff --git a/sound/imp/input_audiere.cpp b/sound/imp/input_audiere.cpp index 5ca85ffd9b..624b2050ba 100644 --- a/sound/imp/input_audiere.cpp +++ b/sound/imp/input_audiere.cpp @@ -23,9 +23,17 @@ using namespace Mangle::Sound; // --- InputManager --- +AudiereInput::AudiereInput() +{ + canLoadStream = false; +} + InputSource *AudiereInput::load(const std::string &file) { return new AudiereSource(file); } +InputSource *AudiereInput::load(Stream::InputStream *input) +{ assert(0 && "not implemented yet"); } + // --- InputSource --- AudiereSource::AudiereSource(const std::string &file) diff --git a/sound/imp/input_audiere.h b/sound/imp/input_audiere.h index 5acc1a73d6..344c077357 100644 --- a/sound/imp/input_audiere.h +++ b/sound/imp/input_audiere.h @@ -12,7 +12,13 @@ namespace Sound { class AudiereInput : public InputManager { public: + AudiereInput(); + + /// Load a source from a file InputSource *load(const std::string &file); + + /// Load a source from a stream + virtual InputSource *load(Stream::InputStream *input); }; /// Audiere InputSource implementation diff --git a/sound/imp/input_ffmpeg.h b/sound/imp/input_ffmpeg.h index cc8feffd40..ae25732b57 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/imp/input_ffmpeg.h @@ -4,6 +4,7 @@ #include "../input.h" #include #include +#include extern "C" { diff --git a/sound/imp/output_openal.cpp b/sound/imp/output_openal.cpp index 190d0638d3..e369818360 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/imp/output_openal.cpp @@ -75,6 +75,7 @@ OpenAL_Manager::OpenAL_Manager() canRepeatStream = false; canLoadFile = false; canLoadSource = true; + canLoadStream = false; // Set up sound system Device = alcOpenDevice(NULL); diff --git a/stream/imp_client/audiere_file.h b/stream/imp_client/audiere_file.h index 201a7629ac..a1910d274c 100644 --- a/stream/imp_client/audiere_file.h +++ b/stream/imp_client/audiere_file.h @@ -13,7 +13,7 @@ namespace Stream { This lets Audiere read sound files from any generic archive or file manager that supports Mangle streams. */ -class AudiereFile : public audiere::File, _IWrapper +class AudiereFile : public audiere::RefImplementation, _IWrapper { public: AudiereFile(InputStream *inp, bool autoDel=false) @@ -24,7 +24,7 @@ class AudiereFile : public audiere::File, _IWrapper { return inp->read(buf,count); } /// Seek, relative to specified seek mode. Returns true if successful. - bool seek(int pos, audiere::SeekMode mode); + bool seek(int pos, audiere::File::SeekMode mode); /// Get current position int tell() diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 7d70414699..3d2a730b8c 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,13 +1,17 @@ GCC=g++ -I../ -I../imp_client/ -all: ogre_client_test dummy_test +all: ogre_client_test dummy_test audiere_client_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) +L_AUDIERE=-laudiere ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) +audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/audiere_file.h ../imp_client/audiere_file.cpp + $(GCC) $< -o $@ ../imp_client/audiere_file.cpp $(L_AUDIERE) + dummy_test: dummy_test.cpp dummy_input.cpp ../input.h $(GCC) $< -o $@ diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp new file mode 100644 index 0000000000..7a5b5afc24 --- /dev/null +++ b/stream/tests/audiere_client_test.cpp @@ -0,0 +1,33 @@ +#include "dummy_input.cpp" +#include "../imp_client/audiere_file.h" +#include +#include + +using namespace audiere; +using namespace std; + +int main() +{ + char str[12]; + memset(str, 0, 12); + InputStream *inp = new DummyInput(); + FilePtr p(new AudiereFile(inp, true)); + cout << "pos=" << p->tell() << endl; + p->read(str, 2); + cout << "2 bytes: " << str << endl; + cout << "pos=" << p->tell() << endl; + p->seek(4, File::BEGIN); + cout << "pos=" << p->tell() << endl; + p->read(str, 3); + cout << "3 bytes: " << str << endl; + p->seek(-1, File::CURRENT); + cout << "pos=" << p->tell() << endl; + p->seek(-4, File::END); + cout << "pos=" << p->tell() << endl; + p->read(str, 4); + cout << "last 4 bytes: " << str << endl; + p->seek(0, File::BEGIN); + p->read(str, 11); + cout << "entire stream: " << str << endl; + return 0; +} From b601cdff629ddd53a236ab388f57837672f5fe24 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 10:52:10 +0100 Subject: [PATCH 011/269] Minor changes to VFS. Updated ogre_resource test --- stream/imp_client/audiere_file.cpp | 35 +++++------- vfs/imp_client/ogre_archive.cpp | 2 + vfs/imp_server/ogre_vfs.h | 3 +- vfs/tests/Makefile | 5 +- vfs/tests/ogre_resource_test.cpp | 87 ++++++++++++++++++++++++++++++ vfs/vfs.h | 5 +- 6 files changed, 113 insertions(+), 24 deletions(-) create mode 100644 vfs/tests/ogre_resource_test.cpp diff --git a/stream/imp_client/audiere_file.cpp b/stream/imp_client/audiere_file.cpp index ea8142eb49..53638781e3 100644 --- a/stream/imp_client/audiere_file.cpp +++ b/stream/imp_client/audiere_file.cpp @@ -8,32 +8,25 @@ bool AudiereFile::seek(int pos, SeekMode mode) assert(inp->isSeekable); assert(inp->hasPosition); - if(mode == BEGIN) - { - // Absolute position - inp->seek(pos); - return inp->tell() == pos; - } - if(mode == CURRENT) - { - // Current position - int cpos = inp->tell(); + int newPos; - // Seek to a elative position - inp->seek(cpos + pos); - return inp->tell() == (pos+cpos); - } - if(mode == END) + switch(mode) { + case BEGIN: newPos = pos; break; + case CURRENT: newPos = pos+tell(); break; + case END: // Seeking from the end. This requires that we're able to get - // the entire size of the file. The pos also has to be + // the entire size of the stream. The pos also has to be // non-positive. assert(inp->hasSize); assert(pos <= 0); - - size_t epos = inp->size(); - inp->seek(epos + pos); - return inp->tell() == (epos+pos); + newPos = inp->size() + pos; + break; + default: + assert(0 && "invalid seek mode"); } - assert(0 && "invalid seek mode"); + + inp->seek(newPos); + return inp->tell() == newPos; + } diff --git a/vfs/imp_client/ogre_archive.cpp b/vfs/imp_client/ogre_archive.cpp index c9dccab902..d7adaa07d2 100644 --- a/vfs/imp_client/ogre_archive.cpp +++ b/vfs/imp_client/ogre_archive.cpp @@ -37,6 +37,7 @@ static void fill(Ogre::StringVector &out, FileInfoList &in) Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) { + assert(vfs->hasList); FileInfoList lst = vfs->list("", recursive, dirs); Ogre::StringVector *res = new Ogre::StringVector; @@ -47,6 +48,7 @@ Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs) { + assert(vfs->hasList); FileInfoList lst = vfs->list("", recursive, dirs); Ogre::FileInfoList *res = new Ogre::FileInfoList; diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/imp_server/ogre_vfs.h index 98cf5da79b..0812114337 100644 --- a/vfs/imp_server/ogre_vfs.h +++ b/vfs/imp_server/ogre_vfs.h @@ -25,7 +25,8 @@ class OgreVFS : public VFS public: OgreVFS() { - hasFind = true; + hasList = false; + hasFind = false; isCaseSensitive = true; } diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index b1fb55f13d..135df10c65 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -I../imp_client/ -all: dummy_test ogre_client_test +all: dummy_test ogre_client_test ogre_resource_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -8,6 +8,9 @@ L_OGRE=$(shell pkg-config --libs OGRE) ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) +ogre_resource_test: ogre_resource_test.cpp + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) + dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h $(GCC) $< -o $@ diff --git a/vfs/tests/ogre_resource_test.cpp b/vfs/tests/ogre_resource_test.cpp new file mode 100644 index 0000000000..36f84c241e --- /dev/null +++ b/vfs/tests/ogre_resource_test.cpp @@ -0,0 +1,87 @@ +#include +#include +#include + +/* + This isn't really a test of our implementation, but a test of using + the Ogre resource system to find files. If the Ogre interface + changes and you have to change this test, you will have to change + the ogre_vfs.cpp implementation equivalently. + + */ + +using namespace std; +using namespace Ogre; + +ResourceGroupManager *gm; +String group; + +void find(const std::string &fileName) +{ + cout << "\nFile: " << fileName << endl; + + if(!gm->resourceExists(group, fileName)) + { + cout << "Does not exist\n"; + return; + } + + DataStreamPtr data = gm->openResource(fileName, group); + + cout << "Size: " << data->size() << endl; + cout << "First line: " << data->getLine() << "\n"; + + + // Alternative - not used / fixed yet + + /* This won't work, since we don't have access to Ogre + internals. That's a shame. + + LocationList::iterator li, liend; + liend = grp->locationList.end(); + for (li = grp->locationList.begin(); li != liend; ++li) + { + Archive* arch = (*li)->archive; + + // The rest is client code - using an archive. We might make a + // shared implementation, or possibly convert the archives into + // a vfs list at load time (although that isn't very flexible.) + + // Do we perform these searches in each function? I guess we + // have to. + if (arch->exists(resourceName)) + { + DataStreamPtr ptr = arch->open(resourceName); + return ptr; + } + } + */ +} + +int main() +{ + // Disable logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + // Set up Ogre + Root root("","",""); + + root.addResourceLocation("./", "FileSystem", "General"); + + gm = ResourceGroupManager::getSingletonPtr(); + group = gm->getWorldResourceGroupName(); + + find("Makefile"); + find("ogre_resource_test.cpp"); + find("bleh"); + + cout << "\nAll source files:\n"; + FileInfoListPtr list = gm->findResourceFileInfo(group, "*.cpp"); + FileInfoList::iterator it, end; + it = list->begin(); + end = list->end(); + for(; it != end; it++) + cout << " " << it->filename << endl; +} diff --git a/vfs/vfs.h b/vfs/vfs.h index 345b4ccaef..a9ffb6b622 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.h @@ -37,7 +37,10 @@ class VFS public: // Feature options. These should be set in the constructor. - /// If true, the find*() functions work + /// If true, the list() function work + bool hasList; + + /// If true, the find() function work bool hasFind; /// If true, the file system is case sensitive From 262cb9255cc54808c17aabb24fc9b230e23f5a59 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 12:13:31 +0100 Subject: [PATCH 012/269] Completed OGRE VFS server +test --- vfs/imp_server/ogre_vfs.cpp | 60 +++++++++++++++++++++++++++++ vfs/imp_server/ogre_vfs.h | 57 ++++++++++++++++------------ vfs/tests/Makefile | 7 +++- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_resource_test.cpp | 26 ------------- vfs/tests/ogre_server_test.cpp | 63 +++++++++++++++++++++++++++++++ vfs/tests/test.zip | Bin 0 -> 169 bytes 7 files changed, 161 insertions(+), 54 deletions(-) create mode 100644 vfs/imp_server/ogre_vfs.cpp create mode 100644 vfs/tests/ogre_server_test.cpp create mode 100644 vfs/tests/test.zip diff --git a/vfs/imp_server/ogre_vfs.cpp b/vfs/imp_server/ogre_vfs.cpp new file mode 100644 index 0000000000..9ca1c4b8c8 --- /dev/null +++ b/vfs/imp_server/ogre_vfs.cpp @@ -0,0 +1,60 @@ +#include "ogre_vfs.h" +#include "../../stream/imp_server/ogre_datastream.h" + +using namespace Mangle::VFS; + +OgreVFS::OgreVFS(const std::string &_group) + : group(_group) +{ + hasList = true; + hasFind = true; + isCaseSensitive = true; + + // Get the group manager once + gm = Ogre::ResourceGroupManager::getSingletonPtr(); + + // Use the default group if none was specified + if(group.empty()) + group = gm->getWorldResourceGroupName(); +} + +Mangle::Stream::InputStream *OgreVFS::open(const std::string &name) +{ + Ogre::DataStreamPtr data = gm->openResource(name, group); + return new Stream::OgreStream(data); +} + +static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) +{ + int size = in.size(); + out.resize(size); + + for(int i=0; ilistResourceFileInfo(group, dirs); + FileInfoList res; + fill(res, *olist, dirs); + return res; +} + +FileInfoList OgreVFS::find(const std::string& pattern, + bool recursive, + bool dirs) const +{ + Ogre::FileInfoListPtr olist = gm->findResourceFileInfo(group, pattern, dirs); + FileInfoList res; + fill(res, *olist, dirs); + return res; +} diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/imp_server/ogre_vfs.h index 0812114337..9a01c1786a 100644 --- a/vfs/imp_server/ogre_vfs.h +++ b/vfs/imp_server/ogre_vfs.h @@ -1,20 +1,18 @@ -#ifndef MANGLE_VFS_OGRECLIENT_H -#define MANGLE_VFS_OGRECLIENT_H +#ifndef MANGLE_VFS_OGRESERVER_H +#define MANGLE_VFS_OGRESERVER_H -#include +#include "../vfs.h" +#include namespace Mangle { namespace VFS { /** @brief An interface into the OGRE VFS system. - This class does not wrap a single Ogre::Archive, but rather the - entire resource set available to Ogre. You can use this class to - tap into all paths, Zip files, custom archives on so on that have - been inserted into Ogre as resource locations. - - This class is currently a work in progres, it will not compile as - it stands. + This class does NOT wrap a single Ogre::Archive, but rather an + entire resource group in Ogre. You can use this class to tap into + all paths, Zip files, custom archives on so on that have been + inserted into Ogre as resource locations. This has been built and tested against OGRE 1.6.2. You might have to make your own modifications if you're working with newer (or @@ -22,38 +20,47 @@ namespace VFS { */ class OgreVFS : public VFS { + std::string group; + Ogre::ResourceGroupManager *gm; + public: - OgreVFS() - { - hasList = false; - hasFind = false; - isCaseSensitive = true; - } + /** @brief Constructor + + OGRE must be initialized (ie. you must have created an + Ogre::Root object somewhere) before calling this. + + @param group Optional resource group name. If none is given, + OGRE's default (or 'World') resource group is used. + */ + OgreVFS(const std::string &_group = ""); /// Open a new data stream. Deleting the object should be enough to /// close it. virtual Stream::InputStream *open(const std::string &name); /// Check for the existence of a file - virtual bool isFile(const std::string &name) const; + virtual bool isFile(const std::string &name) const + { return gm->resourceExists(group, name); } - /// Check for the existence of a directory - virtual bool isDir(const std::string &name) const; + /// This doesn't work, always returns false. + virtual bool isDir(const std::string &name) const + { return false; } - /// Get info about a single file - virtual FileInfo stat(const std::string &name) const; + /// This doesn't work. + virtual FileInfo stat(const std::string &name) const + { return FileInfo(); } /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If - /// dirs is true, list directories instead of files. + /// dirs is true, list directories instead of files. OGRE note: The + /// ogre resource systemd does not support recursive listing of + /// files. We might make a separate filter for this later. virtual FileInfoList list(const std::string& dir = "", bool recurse=true, bool dirs=false) const; /// Find files after a given pattern. Wildcards (*) are - /// supported. Only valid if 'hasFind' is true. Don't implement your - /// own pattern matching here if the backend doesn't support it - /// natively; use a filter instead (not implemented yet.) + /// supported. virtual FileInfoList find(const std::string& pattern, bool recursive=true, bool dirs=false) const; diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 135df10c65..77e02f9274 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,6 +1,6 @@ -GCC=g++ -I../ -I../imp_client/ +GCC=g++ -I../ -all: dummy_test ogre_client_test ogre_resource_test +all: dummy_test ogre_client_test ogre_resource_test ogre_server_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -11,6 +11,9 @@ ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrap ogre_resource_test: ogre_resource_test.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) +ogre_server_test: ogre_server_test.cpp ../vfs.h ../imp_server/ogre_vfs.h ../imp_server/ogre_vfs.cpp + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../imp_server/ogre_vfs.cpp + dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h $(GCC) $< -o $@ diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index 92cb73eaa8..a236c28f09 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_vfs.cpp" -#include "ogre_archive.h" +#include "../imp_client/ogre_archive.h" #include using namespace Ogre; diff --git a/vfs/tests/ogre_resource_test.cpp b/vfs/tests/ogre_resource_test.cpp index 36f84c241e..eadb1153f8 100644 --- a/vfs/tests/ogre_resource_test.cpp +++ b/vfs/tests/ogre_resource_test.cpp @@ -30,32 +30,6 @@ void find(const std::string &fileName) cout << "Size: " << data->size() << endl; cout << "First line: " << data->getLine() << "\n"; - - - // Alternative - not used / fixed yet - - /* This won't work, since we don't have access to Ogre - internals. That's a shame. - - LocationList::iterator li, liend; - liend = grp->locationList.end(); - for (li = grp->locationList.begin(); li != liend; ++li) - { - Archive* arch = (*li)->archive; - - // The rest is client code - using an archive. We might make a - // shared implementation, or possibly convert the archives into - // a vfs list at load time (although that isn't very flexible.) - - // Do we perform these searches in each function? I guess we - // have to. - if (arch->exists(resourceName)) - { - DataStreamPtr ptr = arch->open(resourceName); - return ptr; - } - } - */ } int main() diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp new file mode 100644 index 0000000000..4193bda7f8 --- /dev/null +++ b/vfs/tests/ogre_server_test.cpp @@ -0,0 +1,63 @@ +#include "../imp_server/ogre_vfs.h" +#include + +#include + +using namespace std; +using namespace Mangle::VFS; +using namespace Mangle::Stream; + +Ogre::Root *root; + +void setupOgre() +{ + using namespace Ogre; + + // Disable logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + // Set up Root + root = new Root("","",""); + + // Add a zip file and the current directory + root->addResourceLocation("test.zip", "Zip", "General"); + root->addResourceLocation("./", "FileSystem", "General"); +} + +void find(VFS &vfs, const std::string &file) +{ + cout << "\nFile: " << file << endl; + + if(!vfs.isFile(file)) + { + cout << "File doesn't exist\n"; + return; + } + + InputStream *data = vfs.open(file); + + cout << "Size: " << data->size() << endl; + + char buf[13]; + buf[12] = 0; + data->read(buf, 12); + + cout << "First 12 bytes: " << buf << "\n"; +} + +int main() +{ + // Set up the engine + setupOgre(); + + // This is our entry point into the resource file system + OgreVFS vfs("General"); + + find(vfs, "Makefile"); // From the file system + find(vfs, "testfile.txt"); // From the zip + find(vfs, "blah_bleh"); // Doesn't exist + + return 0; +} diff --git a/vfs/tests/test.zip b/vfs/tests/test.zip new file mode 100644 index 0000000000000000000000000000000000000000..ec82f8bc6be46902264847237d30818665e89072 GIT binary patch literal 169 zcmWIWW@h1H0D;ZXv#htQzs|@DWP>mdgD68uYH>+gW=^VJNkvI$2qy#cq^G9dGk`d> zf`#D)^9$yT)SR4rh4TEOoD@Z_0B=Snab{emfy`uJUcwofH({Q*9Ik` literal 0 HcmV?d00001 From d763b9dbb62fefe0f0ab4c641f051762fb7b1ad9 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 13:35:34 +0100 Subject: [PATCH 013/269] Added PhysFS VFS server +tests --- stream/imp_server/phys_stream.h | 34 +++++++++++++++ vfs/imp_server/physfs_vfs.h | 71 ++++++++++++++++++++++++++++++++ vfs/tests/Makefile | 6 ++- vfs/tests/ogre_server_test.cpp | 31 ++------------ vfs/tests/physfs_server_test.cpp | 21 ++++++++++ vfs/tests/server_common.cpp | 33 +++++++++++++++ 6 files changed, 167 insertions(+), 29 deletions(-) create mode 100644 stream/imp_server/phys_stream.h create mode 100644 vfs/imp_server/physfs_vfs.h create mode 100644 vfs/tests/physfs_server_test.cpp create mode 100644 vfs/tests/server_common.cpp diff --git a/stream/imp_server/phys_stream.h b/stream/imp_server/phys_stream.h new file mode 100644 index 0000000000..9566194369 --- /dev/null +++ b/stream/imp_server/phys_stream.h @@ -0,0 +1,34 @@ +#ifndef MANGLE_STREAM_OGRESERVER_H +#define MANGLE_STREAM_OGRESERVER_H + +#include + +namespace Mangle { +namespace Stream { + +/// A Stream wrapping a PHYSFS_file stream from the PhysFS library. +class PhysFile : public InputStream +{ + PHYSFS_file *file; + + public: + PhysFile(PHYSFS_file *inp) : file(inp) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + ~PhysFile() { PHYSFS_close(file); } + + size_t read(void *buf, size_t count) + { return PHYSFS_read(file, buf, 1, count); } + + void seek(size_t pos) { PHYSFS_seek(file, pos); } + size_t tell() const { return PHYSFS_tell(file); } + size_t size() const { return PHYSFS_fileLength(file); } + bool eof() const { return PHYSFS_eof(file); } +}; + +}} // namespaces +#endif diff --git a/vfs/imp_server/physfs_vfs.h b/vfs/imp_server/physfs_vfs.h new file mode 100644 index 0000000000..ace3d84c96 --- /dev/null +++ b/vfs/imp_server/physfs_vfs.h @@ -0,0 +1,71 @@ +#ifndef MANGLE_VFS_PHYSFS_SERVER_H +#define MANGLE_VFS_PHYSFS_SERVER_H + +#include "../vfs.h" +#include "../../stream/imp_server/phys_stream.h" + +#include +#include + +namespace Mangle { +namespace VFS { + +/** @brief An interface into the PhysFS virtual file system library + + You have to set up PhysFS on your own before using this class. + */ +class PhysVFS : public VFS +{ + public: + PhysVFS() + { + hasList = true; + hasFind = false; + isCaseSensitive = true; + } + + /// Open a new data stream. Deleting the object should be enough to + /// close it. + virtual Stream::InputStream *open(const std::string &name) + { return new Stream::PhysFile(PHYSFS_openRead(name.c_str())); } + + /// Check for the existence of a file + virtual bool isFile(const std::string &name) const + { return PHYSFS_exists(name.c_str()); } + + /// Checks for a directory + virtual bool isDir(const std::string &name) const + { return PHYSFS_isDirectory(name.c_str()); } + + /// This doesn't work + virtual FileInfo stat(const std::string &name) const + { assert(0); return FileInfo(); } + + virtual FileInfoList list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const + { + char **files = PHYSFS_enumerateFiles(dir.c_str()); + FileInfoList lst; + + // Add all teh files + int i = 0; + while(files[i] != NULL) + { + FileInfo fi; + fi.name = files[i]; + fi.isDir = false; + + lst.push_back(fi); + } + return lst; + } + + virtual FileInfoList find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const + { assert(0); } +}; + +}} // namespaces +#endif diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 77e02f9274..2cf7225501 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,9 +1,10 @@ GCC=g++ -I../ -all: dummy_test ogre_client_test ogre_resource_test ogre_server_test +all: dummy_test ogre_client_test ogre_resource_test ogre_server_test physfs_server_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) +L_PHYSFS=-lphysfs ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) @@ -14,6 +15,9 @@ ogre_resource_test: ogre_resource_test.cpp ogre_server_test: ogre_server_test.cpp ../vfs.h ../imp_server/ogre_vfs.h ../imp_server/ogre_vfs.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../imp_server/ogre_vfs.cpp +physfs_server_test: physfs_server_test.cpp ../vfs.h ../imp_server/physfs_vfs.h + $(GCC) $< -o $@ $(L_PHYSFS) + dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h $(GCC) $< -o $@ diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp index 4193bda7f8..3e0fcbef4a 100644 --- a/vfs/tests/ogre_server_test.cpp +++ b/vfs/tests/ogre_server_test.cpp @@ -1,11 +1,8 @@ #include "../imp_server/ogre_vfs.h" -#include #include -using namespace std; -using namespace Mangle::VFS; -using namespace Mangle::Stream; +#include "server_common.cpp" Ogre::Root *root; @@ -26,27 +23,6 @@ void setupOgre() root->addResourceLocation("./", "FileSystem", "General"); } -void find(VFS &vfs, const std::string &file) -{ - cout << "\nFile: " << file << endl; - - if(!vfs.isFile(file)) - { - cout << "File doesn't exist\n"; - return; - } - - InputStream *data = vfs.open(file); - - cout << "Size: " << data->size() << endl; - - char buf[13]; - buf[12] = 0; - data->read(buf, 12); - - cout << "First 12 bytes: " << buf << "\n"; -} - int main() { // Set up the engine @@ -55,9 +31,8 @@ int main() // This is our entry point into the resource file system OgreVFS vfs("General"); - find(vfs, "Makefile"); // From the file system - find(vfs, "testfile.txt"); // From the zip - find(vfs, "blah_bleh"); // Doesn't exist + // Run the test + testAll(vfs); return 0; } diff --git a/vfs/tests/physfs_server_test.cpp b/vfs/tests/physfs_server_test.cpp new file mode 100644 index 0000000000..282566dd1b --- /dev/null +++ b/vfs/tests/physfs_server_test.cpp @@ -0,0 +1,21 @@ +#include "../imp_server/physfs_vfs.h" + +#include "server_common.cpp" + +#include + +int main() +{ + // Set up the library and paths + PHYSFS_init("blah"); + PHYSFS_addToSearchPath("test.zip", 1); + PHYSFS_addToSearchPath("./", 1); + + // Create our interface + PhysVFS vfs; + + // Run the test + testAll(vfs); + + return 0; +} diff --git a/vfs/tests/server_common.cpp b/vfs/tests/server_common.cpp new file mode 100644 index 0000000000..ddb85b0c3c --- /dev/null +++ b/vfs/tests/server_common.cpp @@ -0,0 +1,33 @@ +#include + +using namespace Mangle::VFS; +using namespace Mangle::Stream; +using namespace std; + +void find(VFS &vfs, const std::string &file) +{ + cout << "\nFile: " << file << endl; + + if(!vfs.isFile(file)) + { + cout << "File doesn't exist\n"; + return; + } + + InputStream *data = vfs.open(file); + + cout << "Size: " << data->size() << endl; + + char buf[13]; + buf[12] = 0; + data->read(buf, 12); + + cout << "First 12 bytes: " << buf << "\n"; +} + +void testAll(VFS &vfs) +{ + find(vfs, "Makefile"); // From the file system + find(vfs, "testfile.txt"); // From the zip + find(vfs, "blah_bleh"); // Doesn't exist +} From abee2689e362eeafeba8ebdcfa346e6fcff2ba38 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 14:49:43 +0100 Subject: [PATCH 014/269] Added stream capability to Audiere input --- sound/imp/input_audiere.cpp | 16 +++++++++-- sound/imp/input_audiere.h | 1 + sound/tests/Makefile | 2 +- sound/tests/common.cpp | 54 ++++++++++++++++++++++++++++++++++--- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/sound/imp/input_audiere.cpp b/sound/imp/input_audiere.cpp index 624b2050ba..b7a38ffd1e 100644 --- a/sound/imp/input_audiere.cpp +++ b/sound/imp/input_audiere.cpp @@ -1,6 +1,8 @@ #include "input_audiere.h" #include +#include "../../stream/imp_client/audiere_file.h" + // Exception handling class Audiere_Exception : public std::exception { @@ -25,14 +27,14 @@ using namespace Mangle::Sound; AudiereInput::AudiereInput() { - canLoadStream = false; + canLoadStream = true; } InputSource *AudiereInput::load(const std::string &file) { return new AudiereSource(file); } InputSource *AudiereInput::load(Stream::InputStream *input) -{ assert(0 && "not implemented yet"); } +{ return new AudiereSource(input); } // --- InputSource --- @@ -45,6 +47,16 @@ AudiereSource::AudiereSource(const std::string &file) buf = CreateSampleBuffer(sample); } +AudiereSource::AudiereSource(Stream::InputStream *input) +{ + SampleSourcePtr sample = OpenSampleSource + (new Stream::AudiereFile(input)); + if(!sample) + fail("Couldn't load stream"); + + buf = CreateSampleBuffer(sample); +} + InputStream *AudiereSource::getStream() { return new AudiereStream(buf->openStream()); diff --git a/sound/imp/input_audiere.h b/sound/imp/input_audiere.h index 344c077357..4bc42cbc61 100644 --- a/sound/imp/input_audiere.h +++ b/sound/imp/input_audiere.h @@ -28,6 +28,7 @@ class AudiereSource : public InputSource public: AudiereSource(const std::string &file); + AudiereSource(Stream::InputStream *input); InputStream *getStream(); void drop() { delete this; } }; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 22da35432f..5d38b4d1da 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -9,7 +9,7 @@ L_AUDIERE=-laudiere ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) -openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp +openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp ../../stream/imp_client/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp index 78dbce5b22..28c4705360 100644 --- a/sound/tests/common.cpp +++ b/sound/tests/common.cpp @@ -1,20 +1,67 @@ // This file is included directly into the test programs #include +#include #include using namespace std; -void play(const char* name, bool music=false) +class TestStream : public Mangle::Stream::InputStream { - cout << "Playing " << name << "\n"; + ifstream io; + +public: + + TestStream(const char* name) + { + io.open(name, ios::binary); + isSeekable = true; + hasPosition = true; + hasSize = false; + } + + size_t read(void* buf, size_t len) + { + io.read((char*)buf, len); + return io.gcount(); + } + + void seek(size_t pos) + { + io.seekg(pos); + } + + size_t tell() const + { return ((TestStream*)this)->io.tellg(); } + + size_t size() const + { return 0; } + + bool eof() const + { return io.eof(); } +}; + +void play(const char* name, bool music=false, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; Sound *snd = NULL; Instance *s = NULL; try { - snd = mg.load(name, music); + if(stream) + snd = mg.load(new TestStream(name), music); + else + snd = mg.load(name, music); + + s = snd->getInstance(false, false); s->play(); @@ -37,5 +84,6 @@ int main() { play("cow.wav"); play("owl.ogg", true); + play("cow.wav", false, true); return 0; } From eedf0c9e3b2083bcb9ad4e3cb0ab202ee4106eba Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 26 Dec 2009 22:13:06 +0100 Subject: [PATCH 015/269] Added composite test ogre+audiere+openal --- sound/imp/input_filter.h | 2 +- tests/.gitignore | 1 + tests/Makefile | 14 +++++++ tests/ogrevfs_audiere_openal_test.cpp | 52 ++++++++++++++++++++++++++ tests/sound.zip | Bin 0 -> 18159 bytes 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/.gitignore create mode 100644 tests/Makefile create mode 100644 tests/ogrevfs_audiere_openal_test.cpp create mode 100644 tests/sound.zip diff --git a/sound/imp/input_filter.h b/sound/imp/input_filter.h index c3da8599d2..4fcae320c2 100644 --- a/sound/imp/input_filter.h +++ b/sound/imp/input_filter.h @@ -1,7 +1,7 @@ #ifndef MANGLE_INPUT_FILTER_H #define MANGLE_INPUT_FILTER_H -#include "sound.h" +#include "../sound.h" #include diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000000..6aeaa5eb51 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,14 @@ +GCC=g++ -I../ + +all: ogrevfs_audiere_openal_test + +I_OGRE=$(shell pkg-config --cflags OGRE) +L_OGRE=$(shell pkg-config --libs OGRE) +L_OPENAL=$(shell pkg-config --libs openal) +L_AUDIERE=-laudiere + +ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/imp_server/ogre_vfs.cpp ../sound/imp/input_audiere.cpp ../sound/imp/output_openal.cpp ../stream/imp_client/audiere_file.cpp + $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) + +clean: + rm *_test diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp new file mode 100644 index 0000000000..7bc6131da9 --- /dev/null +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -0,0 +1,52 @@ +/* + This example combines: + + - the OGRE VFS system (to read from zip) + - Audiere (for decoding sound data) + - OpenAL (for sound playback) + + */ + +#include "sound/imp/openal_audiere.h" +#include "vfs/imp_server/ogre_vfs.h" +#include +#include + +using namespace Ogre; +using namespace Mangle; +using namespace std; + +int main() +{ + // Disable Ogre logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + // Set up Root + Root *root = new Root("","",""); + + // Add zip file with a sound in it + root->addResourceLocation("sound.zip", "Zip", "General"); + + // Ogre file system + VFS::OgreVFS vfs; + + Sound::OpenAL_Audiere_Manager mg; + Sound::Sound *snd = mg.load(vfs.open("owl.ogg")); + + Sound::Instance *s = snd->getInstance(false, false); + cout << "Playing 'owl.ogg' from 'sound.zip'\n"; + s->play(); + + while(s->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + + if(s) s->drop(); + if(snd) snd->drop(); + + return 0; +} diff --git a/tests/sound.zip b/tests/sound.zip new file mode 100644 index 0000000000000000000000000000000000000000..fd32b35299859ba2d92e702db719367110d8b994 GIT binary patch literal 18159 zcmV(wKAfrl z3^|OCtA~ZX=U)O0HuJxFD7gRYoeQJ(cO>9{j)Z}}D_^#G-mt#@r-0%9C4&sg(zAE7 zV^j06rF62l(D{c-sYuDi!NJeL$Hn!}BnbcNj3Xtj1C<2>Km*}X(GqC_Es--pAVvf- zOLaqeGRp-)IVvkG%@{?ZiU2jHOg-Q?Y>*zKESaXVreY>23tF?p(^R0+T;jAY@x)V# zQbeJ3Qfc%eP&&-D%;JS0iY1HkctUc+hZcbxLYbvf41J>z z&=G57oMBw=GO^|fYXqT^{UEZr=NWkM2hd@d&@zE@WOzrDA$%r9`>&n=33!-)bVdaL zzNn+gYJ~n}NFV}20eG_Rc%$y5@@elTVE^eN0r+cfhNNwVG#bS;EB!2M{j5sAtm@8M z_NPX+lg6{-E{uq%dU(J$0Dv%xDjK3VnWCAZNXMu>DTrKhiy^p&XEg^J$+1Y#m`>3| z<3!+$nqiE@T2K|NK%+*MRw%TZrc@F=+_E?jz#?v?2E;&TOOQ;U8cFd)10qs+Bq@HQ zl$jP}EX+^_GBkY3Qbb|RT%tZG$WR5+ReT}Bada4TUQ(B=h?UP2JasAyx z4CseIs)ULeHdFux@Zp_(UWI+BoqZmIe`R%TDMM}To?qorZe^{1X-#cqZC7JWZKIQ4 zk^Nt85o2vFwCPw|dlFacFi}~z)_zpmemdUj(%o9WUQ|2YUend;Qrqa<-RgAG$g&&L0bt$(UK zIc`55?`j-xJzsBcd}?iY(|y?0-Z1l-FfpZw5h$i6seu%y}1-* zo#S1|(FIkg{q&@k<)p9iWQ60Si=)2F`MI&-&DXkq(b;0<>An^3-)a{XsG||<(*4i}D z#fH}1Nb=QT{mc1Um)m438(PN&^Jvo>>BaWQ?M3ghy7dv)H(%%Yefj`^P#7erm*b90 z{AY_1sgUt4v6x0EBd|cIROMQnh*Xvsz!B06ETAG)#vGJk1$9BvbS$7kt44tC^OsvVw8>tR@13fWNb-_B04y^6!QjR6%1WHVtU;<0%*AS|On?L)0APk@nILJ0=P@bBmuJo!ZJMQ zW>*kGMOENgPD_3x^qiJtB!brHX(OM^20o{m3_Vh)LijNMQpf_0 zGm6p+B*cgvp$w$2NX0RD!)ToajH4ZyG>B~&T2Vl0h+N`wq#c>FkZTyyvtR-MPN52& z4oETZSkVGfkN^^YHJBE(#j=2gM=BmpcB3F2+0u^a_6C@cbJaM3-KFk<6 zKu}AXK5*Ju0)j?aS=eKc=LD0l;$sp@#qKM^vCVPUt@Tt;qm6_y0wUfNs@aw}N_9 z%0J%tmj)7o($s+{f7gHT5AAOah3a3&1G+kB?cdg+{V!U@f7depyN2_Rum02dUsC=V z#_-pj|EZzS{+FSt{)fo{p>TgAHO=9>vTQZ)$34V4$pFU}Ruups%l^u5Dm!tgEA)z@&xTc*N5+y+hCGsTu=yDhBANton$nDs84j%z<9uPCSree(22lugv*l5+swi8#=mz%Aj2qdHP zYX#d5c;C0Np6>Ufi!vM3hFiawWnPqZYT!s04U>PD1cQ>30|#)7o!y5DI7=5-R;275 z@aG{PTCw6d(6#qa<2Gp+Da5-PjsYG3*+SU&YkfCIy|0@TL)3J9uH?-zx8E*8bnUsN zZlh6P5JL4oUOjNKU9d0FSN66f+E?!M`1K@cU`1TK;AQ!qM)*JC5yld(_qC#qw05d|fc-T{(6^Z$-d)1+n=N{czg3udVj4h>l?tbH2Y!mzeD@Hj{FXNOprBWtYlco9XU_4 z0dZWrG~`7e16326e)vDC8PsTRHj7~v1Mql8C3S*I?F3D=-S8hRmbBEIabE%)xC*<; zw6>e5MI2P*myjo1Rj%anzy*80lFbisIB-$M$-^WzhJ>Pr1EnzPV$QlFH^G`M_a)$D zc^jz;GXMEJk2J2$H?5Xlh?bYJN&D4D<6M0EuYK&pqE69t9MKcGjZRCsYm9sjkffbc zW=fHbgp{NFeTebvSd+R3YWSu!R=;viG%a-rjG}DpJdM=)r6+%D)yPQek#NgdC1sv> z^f}Y&>v7%TITnW%e*T*~*`iM_CN~sq6MypPc-cBPFhg8Oa;}{Qe=t6~?D)A9iTK1u zFT*Ao>(AQ`2)C44!OETKl74s$I{o(RBACUL7J=Ty*7CUPW8>U}*<^Aw{V?dFKGJ*W z6wPSq;C#k(RPOaE3uP^=pD< zadC0P?fuN%!RJ(kn?39pRs&)44Fzodgu;cw*9Ye$CzWIW3Z z4w}SQhxTK)KR@N3vYY7`Qt4tb1b>=(ASRiN?SC9(ZZU)HTHGY^RZ<;3Fc7_~%-AN` z{#yU>7s^D5Zo>T1I};|nYbsj4)w<%|KJDd_r02;|Lc{nrG+w}O^)1)#;M^t4NJFK}T$3pwW?lHH-Qwajgzw2ono0 zD;kNK+_k6RLBc-W!PYjz-9u0k`t@+zFIPbT4n!B?={TkZ> z=RKg|N{Fv>4o-ejs7x1Cgd>Hfji&=8ZbKTmjTu*`bi2j_$rbC9A(8lmF!Ni@IB>4+ ziM-RhIbIL$ufZ#Vqp59S>!qw>$ba_ri1sRmzDHJx7}QE9+<1{Uk{kM28SDBKWbDqA zTXq?JruO-zvHT`$lLnYf@}(XS8|&VW^!~2ttRk`IeBMjaVzkG%o?B#NYQ6pYF7b}{ z<`{;g@zDC{nWY&$2wx!(oLLst=%~UUQw=YR#i^U5zl^6KmYUSfpCaPaj02{Y&Vp3% zglzf31R)3&<9mY;$VFI(mtK6;fD$S$=k!L*r#S{fCIaD(*c3jKs9$>pTb)-UBAtBj zHh_8tYzkuWZ40FSope_!7(g20Gzd+Y_+>OvK!Q1;8!a6mY6qM$A%6kHxonC$5G2hL z`dy9CPi*!MbMxFSSmZ{B)BMNJ;4f}RR6#+Mg^eD+HA!Sxd};scDd@ zk{oo8@lz(KGs9+y$&RhRPTQIy;9^P-S0!6ItO2ayX4+eYWaPxFKqCVU*9 z1tZyOS1NZ1D~W#DP#iZ*327HomNHoj?ACWfaxOZ56KlXnnFhHw>2a7@>6Jb*m{cvN zaW@*Z_P@%OisME;(qc4yB7OUUOg@#YNx&?dJSBnlEcLENd16XCHX%k7ML{OeMStb_ z1IHu+5=z4``v6|MTuF-k49HZbI-3zD+@xud;z5`i4V_FcP@Cs_S>DEJ#!mN&xJ$%O z5%MK))%W{3?arlPFq%p7B7tw@&JJM}e>&5gRG3fu!AilA=dJD+HKuY+zxOOd8-pV& z+uyp9-ri0b*zklyD8oa-Er2A%QId!fTDC)cwMe5QnQGDP4~PJEIE0qh=0k7hw<$`Zpz_48 zgY)h!V(Wcx9_~%t`KMlWc)nU`3r?@axaY;d(@b8;e}*dn>#REraea7dv_7^!=7}fE zGfsZzfRxQtxuEjo0`K_hTY4jVfe^O|r>XPXeG|_L`ZclZM_~>Snws~%Pbr_f&bDk! zSH>?tO-Atc80#w|19m1`$QXp<5+W+sMV8reXm83SmVZRVZYqeRc)^Hucl%$!;kqky>0(cswbhFBILtPS*c+yP|K4k_AEF&4K|G!_}4o z&O?`^lUfc+mOnVy;?t#3uo zfp&YjYGIX8zDGi&)6*pLS`vgtI}82!k<5vYjtM@V?8)mysr7mjs3_Dn+A$ii>S~D| zN}TNN+X=IUu4bIsU0|_y094JK}L zicFu6kg|%Kg%fAc;^_PP77bIIgyQ^BV}9>p@mD2Oa?z`!O9XDDFoke-?` zD~pNEbxxNHN&Ni$YNc%`H;7p>=4;YRuvT4TC5;5vubMq8Q??48%Q8KR!a9ieVT376Y2`#qL=YhXIgZqSfv&QM|zxKro!>%$40cB zxntd)+~Cr5O}URGp05qF%+|<}m3l)IHx9+$yi&``SN$_+<2W@4?@`=zEtk?3oC<$k&*GvXFj9cTcV9K~Yh{crgGpNu-FU;1-9{b07*ZDN3yU zDeO~7kU62HjeBhOS`ACe9ozHN#V>Z<-ie!0jZ;@A&HkKbpwcDP)A%LDL2eRz8c|zP zGl>^?CuYJe0x^XO{F}jo?h!=P0e6}35`XY~^+LMu{QT{zgH@LqXGEPBP^vWfB39cn zQws>h3-w%tvHazOzkHEBCjE40nXf_1LX>gO-xd@M?yk=y$%4=hlDplT`I>D23>W-E zN4~N43wqHatzTpzBK>(Zz>h2bRA_1zwfrcTFM_#xFfHG^9e*5k#6V3X;JRou7_G*d zq|{QDr-1ea2@7gLN_8eU0CH#v7wn_(+m$ES(*5s>ZUzEZFe?+@EzUTy<#!5#nr`m^ zplIuFl_uFEvMeAZi}TZ<856$bgq%vTJI_Y=tWC-c@tM~mEK01cpK+)Irw|T$mpY#B z2u9t8^U|r-iWTB;JU2PE4XwV0=&Q`=_0{W|C8X^6C}V}%>U=O_kH^eu5CozL03cj) zD-QJ#6Xn!r)D=@a*t$5;(WN>xfajaLSiRiarQ6fWC-iX**Mu*4eYu@wza^vHOK=)7 zD&7m}$o&dX&2TUCBVs;zm~E?*f=k=9{=^0c*dnIHL%1ZK8DXMY3qp;P5)?xy&2?C= zxl<$($xUWu7ce+hTz?Of>Kxm2Mxf}i8<4k1V%t16c})@{1f;4~QxW;Bh6s&i(DR$z zisPT18(ORLDHOkB?h%wh+m&(V#l|^Zz+s$fgIF^HpFjBD~FhTnl>isW9Fwi3#CVJVy&;q9g@bLZxF!F7r11_*fYwAWFJRDD3 zo#e2)Gn3{NBVb{cUq^Lt%eR0gW7fS4By;Q<8D-n$3u{SR0={@(XN3C|qLjay&2vG& z#ii>87wXbf<7<}eNK?@1WFOS&2W%`okNQ9KAWkg#kJ*_7r#oyi9QUAV2ER`xpqa2| zV+6_zvL$`d|Ey!@Y7=Jtq?^ujr4cXAcc&`PCK>y$1a9D;r?U=sD<7YW5mx2Mq+V`(Sm%jv!F~*kH*oNdBe=B}CYpwx3fyz-z zd-k`u`trzm=iK>mPyX|_tN8!EynutgQT(jYIQ8`nbqsV(jP!Mk4V8@zK>FGsSrs)k z6-8AweLX!*9W6CYke0EruBsYHTV2!8&>-p`#PL!_ER>BBb7*TBOU6)`Kirr&7a8cB zaeO>;LNDA`kV&Oh+vsMXQT4`t`QGMJJOPn$FKo3Usd8v6p^^PuOI|t9$%Ea%`?Mgz zbx6|rDft%u?i$JZc!FOSY-#o7$1^#m@Trr-x?W zYAjx3{?TU~MI+QjRi!oJLG3J(#MZ3WCA3`{kOGRv^-3dW?(DJm8? zfI?{b8dB1CK*Fv_C2tq^tJkN2LP2N-uP!)+clHiznSGLBK-8uzuh?Z!t)LDMtU7+YIg8{E zm#K`_+NWdhRG?xZRgb zWb$vVWPdQ4QfId4TrXT4gi`Li>dHN$SM$)JFUrbIRFSir84h;<=z9alAY%SFJ?BW*oB4uVLC=df zc+jkv8uVpaHx?eDSq7YKsxVnJ-gqi)UdyX?M{v{M+ugx@ej4@T;quvZCnseZYr?qP zZ+ddk<3z-AvnbA8tKr>uJJy6Ia&-M9!Y_q?B3w?FpA9cOk=+iXz9Lpz?GQC*)`sZXKnl<{|vte^cm=iJ{8Zm2M6Mpm? zXqX9C+@MMISVF$%_3?@0`|61G`)@mYjxbQn&-e^Kx{Uh?aM-stnmUsVdA$N*aYu= zg9;6-scw2s4QL-yz33fifVJ$!LR*AYVWNR{ygBcDNQjsp)$B?yJ!hcv+7 z_!ScV@s$y|6ng-N0TCx98m)3lhxfy{EPYhogYV;RoD7Hkb+pPsISat`T&%oP*hyfm zWWu9<-h10fBrn^)=X6qT{~mI*cNHvU&^rBbnjwfF2~s9!+%nrT%3XVg{~X_a&!#4( z^0Tiharb8P>pEqRY@I1~0_557#3utA5Soi2(3||Rr7>}^wYS5uZhY=rPvK$0SG--; z^p2{)YMrmq>?m(4i@O`&q4JpV!6j{4YW+d^qc3Y=-xxMoO9=RFbH854)z2p0_x|Ih z0I5;`tJs2~JY7@0t%68hRxUJ&eaYK2Y>9pn>DKD|H5oI&^JtjDtX~Y9o-vfu@F%gg zd$D)d#4^DFCeI{!*ct#BL_+%j`>R_65RYtNdq1~>otzvDFnQ{JY|5aP7fl+`P5#O0 zLoC72{3DYl=l-Z7#!-Sf1y76f?L8O=RljOw++fTe8p&zGpm+w}d!`Kb^P*%DKf0Ce zPF;wHA>S-ZFl_Ua)+Wd8FIg!3W(W^^2OE4m)NsYRRdP* zEkX(EXwVSTAaJx3h}{zmtWTSou>_g@>;z8i2B7Vu%cgNMSLDcUUb2meRV2diJA z1NE(o=5NNT>nA(&IP?B=TeyT&I-lL`hh*o3h2fctyy?0uNkFTPFZVaupWLj8Hei(z z`O5xw!8r7$v6ZN7caM08eUvl(B^d2*uEY=6auFhm3Pt76Kv!q(<33g0S+Hd_MY@E;RuM+ zZL`wu5DL#}Jop6kJPd(ct5It#g z&(0VTFaTG72eSvSBF&cj%yEXCVTL@kZ5o^>Ue7LayHT*pKsIA_OPci5OlMSmfUsZd zJ=~~Bj5^4RB&*vEYNEo{I!iv)K)HWG%YseAqlQxVNK4pQ% zx4OvSwn~UF3c|qC7(H<9-9$jI9pwA6Q>Ukl8KID1R3e#5u>Tvunmz2R!cvS;o-~MM zMDW%hdgW%U&>&NUa826VTc_xu>>1@>=ZUHC0EPh>*W5%F8>X@M`}5qL8C*cdR>@-JuHzqVWH}I95b;5%n;Bfp}ev{ zU$15P&1MK_qQ0eelcv?B!!T}~sMFTN|4Z?W;J45T`^T?_PEc}Zs@Htn994VA%ryKw_C}qh&mg)gv4wOzAllb0r(G-xw1JSw zvyCao_6s+gDDIlOu1=m4cbEx!XwyUwLsrLX?Zg1_dTE4!p;iV{ z+XeWh#d=%aw|2?&q-mcFsf*9OkuJCGppTx%m8EKg{_-2vb~3bGubim!52oXlu+UaVqB@4mj!E8Ew;ug8nX#s z|EkX9Sqo#OW=d=4w7#gYML}01n{&0918qFNsXu4!(dTzpK4JW6LhMR@e{q^@%TneIiu+ zc-7v0eMd3#!9`lcm#V%N9At*DNq~C|X_ZIt0X!C#nenIV%G*9Mir zQSmi8>-{m`^ZpKIY`pqIg+r7c*2N-`eJLP!$(Grl9+1Dh@nLi@de7kwM}U;*AN{&T zejEebYMbHmDM>Sa79K9(iGDTF0jbi5Yc|)gSv3O_76X$(zFO%)m@r8uaI4~jpTu(W zRoEmh;mVlI6&po5={CPcCD%!F;EJC)_e62VB-i63!&_j-nU`x3oF~$gH6 zd|2M&I<2MGkR@N&{PS3nRFC%vdrY%D>gT75et0O5gA2E%Kiov%OS4K4!>1H>p&TtuD%;T%xOGX|-vBC_ShshkL+~no zmrT~F2&mYDbymOa;An~Ub3X$lZPKK_z*oVg0;0)-;7AKxKiKYG`u4SVm*++=l9l%WK$G31RUT~6gx=*m%f6mUnzI^KzWG8X?x(hkM>?}dE^!zX>GHFN$ z@GA1Y0TWM?X7MFKMs{=QN{vu$TXiA%S;#ntG2dBF2AHK@GQ>@1q7-35(I&0P>m-5&UtnDy%?b`RYGO64lOcL9ele5%2 z-Nvic0F(XNFRoJ?f*_={%?c@T_16#rQf^ZOCv7Flq5Q&VXY(1O#!ceSjbbkwaKqeQ zLho6V_d|biY)Jb}XJuz(kt&kDv9xX)-<-+H@OR0o8VBM<)T_QVFoh;Fgfa4mTgKV4 zAqHjSVKoIaD_(BypZmiW6X-MdDPaDRI%m1mfZU+)ll{tXr&ujg+3LQc9m^L-{3cGc zUo3^RbH3G|{e-DlR4i(+jGKp<=SPNr=v7=tSn|HW zes4fVQ%vTO-847(?r2#h ztn{0nSC}vgfTjLHUY9ATGts8%5VCmQ`TM7UhagvS;Ap=u#uV_W`*0w34_i_!AX;UwRrkno!M=d0=G&iT zL`{|cnIBQh;V`%CXcO$6Jv-Qu|9u_N^WWDI@PGfsqWqmUr>>qMNLy1&6J)5Tr=zb0 z($?06K0vzK209wrn%a6G9W8A`U2SzuO4aiPpVIeu6%7FcS5jE|z%!t3p-5tKrr^6@T~WSAlPw)_ z8v|TfGXMaK1BII-IvV~;r_0(VxZfAeKk`$Zrt$OBpS`xX^e49-tVyypRT^N`rHIt} z{OC$-<^8RRvVN4o)JT)5*djkvZ0#)sop_@%RWot)4q){kh#G|$6ktGrd zc+IX3ZtEAsT|P64i7p&j1U-Zw3)<~kAoJa^@wR289KjxIYq43_@DpRoIOsQ#Pqnt= zmAdgi_L=$;_*8mi!5xi-+;_r>|s8{t}zi%;kR61R(h?K{~>1@W{!6AfL~8z0tLl{@rQQ8AmTI zzMyf^tH=fqPGpvp&Y8ed+%s;QH9>p+)*d{WE=9lbi%nGk9UonGc<5?cMn0I<95kPB z-%BO;{sb;+6Aqi={i{5O3?Q`j?gK(Vp7T4B?;Ubd&phuJZ+`h2upbw_jXav|#qibk z@N|yPC0DV0T$byh`hm=N&9FL@W~EMgy{#ex89%QQkfhk>bwZ4Sz%kmui8qEby#1mJ z3)}jEnL8kxL%^v>g#YmQAb@*$W)25tu_sfd@JEB_kV%&;zNQ8Tst6Efmpn&=)`A16Js{s8~~3CRF=}gFTAJpM93;V#XPW z4x-H0t0zSKA{B{WFq4SV$nBh%u46&EK?N2;Z=$m^Yhv>^z-r16|6qG#*V9^QPQQcO zW2_8%;G3$N=?--CO(9KDhkrtnHj3K|yFy5Qz`sSQl8k)9@14 z+Dw!ob*>~1f!qtf(DNqdmD(?Q(mjA-~iwLFd!^kT;Box?0df0-KnSj z5P2?Zs<;2DafqJxQD$$c4i1Nm>)Lcy+Ag(|!KTNl$PV|-W`_#n&^S8vy1|*q>X!s` zF+yb*ARul7pgoSbPSGX9$?oBeN5`?W!hGNruJ&y39CL){ zi#FRQ$7>EVf0r`R^OoD*8OSwGHeHiexX5MD0V_N4|+F zTS9*_vkr2F&Q_j16|Y^yq=(byVAkUvLTlgrgyw%>vtzT`ETD|XmtOSRUVM0x6 zQ-mG};dO>jsavFycwqVT<9OQERoYag_{!w2pVJ1Rc;$B$=_eQ8hVPaY7>@N~`ok%U zYD~5Ro>SX|4uk~RqB4q{`wta)4qd!I(8m?Y;DR@c<-Q|6309E-c$uh?9hUohlkAk1 zZ^rGT-IVIDF=t5uvUUdNf;a?Ui|UW$gcLLCk_aB3E5<=mg+X(mTgai!A#m}_#& zq>o%xm82{4npuhFI2cqW5<;Vu1dT1+&9c;o!P+E?QP8hyM1NbWAS(f;6gPCXXA$g9 zWqH&j$oZ1Kl$K@y7ntoM;aJ=X(x0;XmM+dl5Ysodb1v_mgb)*Vu9YXBZ?vdO2$-jv zaN(N(8quxHc6b2da7U`_>7OOy(OdZ^Z?D!AM}gIktO1G@c&^B8I$spt3?38W$rI~m ztVxTSjuXRLrZ!hiKY&%VQrSWpUCwOv>V!f}<(utT?@{AS=Vitb?_5@OVd`XA;Un%; z`$d9fZRATtmWkMxrGK;#&`aQnlA2T4Xykw60_bDWZ=!SGk9%k>b%M98AhTBP)c&|j zYLk}%Q3{m{N5bfI=bAh2x@w6qKAXjK!CS{$yvrOgbxk~}Ne}$pHw=;4g%{w`{c&&m zeMQ3wyk?X)Xa0pApDM60teLMLa+7Rbz{mB8>0|0^%Eh9N$D5oVB#Jh`gE+QK?hm2u z`19c#1Bl~@QNohwTEoetD84|ot{<<)EcyPRSM0vA$26vJB8KKla%P7d+7x=T!~Ls7}c?L$^M2w#p=X<_KW~7YXu=A(ESa3oT`!rAoD_nIW$v z%dgr1?oOUvZf=?WfGsu~Sts^r2T_IPDJg>wiJGj+uV&x&cKI*7wIoU~R;!i1a5h;b zF?h!-a1|$ZXP>_eA7wg^Z~dXMpI`>9PfBWVx(<`;D>&J{ppQTzU-&oVRghMq9Lt~F ze+=(D!%%LWexYHT*b`F)#1u_V<;qZWG4c|9LF1t|M$}-ErH;8q8W^Y#-ThedZ^t<> zNTsDdZsrraapsrnN3DnNORiQ0UJ}w^KD8hFuNOT_=aF?wU%6#;Kjcsp%fz0pX8e?U zuwsH7CFh?qL#6e|HWZ`DK91I2CL6&4{i@Q}`mF1;C!**|uzjeQfoYy`@-<<0xNz@_tDjd; z$1W-iux-?~)uOW;wy&3~s&-KEcJzZ{Qs{4<2QV3iZgUPTYA|cs@}_2LyOVD>WQSSf zkERWPa|Jlrc>+%nr{=MPtWD^_n-a0)r+&yz0LmNuc`xJ-7aGj9*A<Y=z&-#Y2@##DP9|_JM zGEmFogf)Q_TB_rZTLD%UYuO&p456#68fI>S&@P1w0}9>*(gkCHst@Q&AS;YFx=tMY zj#n#~#3PR*&W=-9_*MQ_q#~S5eHX%CIiGu!0$9*}iaKS3C_jF28H3ehxuOZfy1g?0 z@sKT2V49fobDDo5-P#^jyD+z}vr%0BK?=>`vh*S`NbryI6%xS-PugnIZCgtAVAKz5yaf zfQ-g<7@_(7pUA0#_q8|IOGQkw5Y8)12&rNeCum=1qv3%?<_hT5Iuyj&=HG#y&{lqc z>a$5H=U*&8#s_D?AfB>G-N~xkN6h{lb)9tkVh!ohu z(q|{>bL04Od)>IAYox;E!KadHOoVH(SS-D%LsDX20OwPL0E(vSjfJg`E1d_5q@80L ziL6+PiQ5eBKL5Nj!pCZ^Y7z=Y!yS*_Fb^I1?e4gGmh=sjSdcI-97!V^uI;~;gkGc` ztKj-v<$15H3gchcnU>(#2pcM?VgFqq`9n((vv}nTI3YxLe%9!f93)YbMdBDttwyuq z*mKk5X%o&t`+4SD{hyi!u4ucmi%*};w8s^RfWX-V0!;T+56u{N41cIOtv)2Fbs?a| z;S5XJXwSucnYh(H8rpG~HS|iw0fZKyd;QUsYoDAd5Btb=NWeIlOhug~ROnSl{!PNd zJJc+l+oo{v%eR`+f<-|^qG=RtpTbV<>*5Ve=P<;$QjAhYr+H2C{N=UY%|++iTGjy9 zY$$SfuV{5q54uuB>4^AoM*nuF>2QetV?$HKzWS9ycYw~5ND#XZBYjH5g2-qD zpCpWb(JKC`#0&o_PTblY4>gIeb~3Bnc>r6c2B<1}b?kenlq5W@1rO1TG|R`|6(?0m z`@RlZuUzhqr`8Qb{;Eh$WB7tqS=~176^C^z(M2g<%)etOi4Xu#by3MbQ`>W;n6)rL zSi=*lULQu9e7nAn{*trq%YKbOey?#Ftt+-f)8 zi~1B+j(<4wOu;U9Ee2&od`g^2u1oyF{pTdV@vba(MArV~#SiQPH+jm6n*TfycZ2B4) zp0~lm-3R`XiVaQhPiEu*e^b#4q@uTo5^!>T770$Ze#b|B$ZH{%LB9ng%|_1Q9I0jH ztlsaoUu%A5L&DBwyyo_;`bpfeRUPLceHk`u)g3!=AqF{3%(NlsDj? z3krB&R+#^~pg@4W*WL$vc<35vXq)QlYJ;@3bhNeg^z}4#wDmxGx(2$sn!1{Le}Ckm zudQS7fAO1;|6TDZ6r`p#W1EwkUP`X5eYZ5?x+@S7b!{QS)Urv>-$dEX#YvGC%~+u# zBb}TeI8J(g7*2h#pzj((6niSMB%KIvTTuIX)<#y6@8u?Dy>i#VBA?JO2;Qdw$A~w#4V6-v3eU839XgM!p{;jH-Q=d`9oQ?)p1gDqu<95fF>OEl-GhO3C(tg|~ zxh%JC2ke>v%@ms{!Kvz~kQezxQA3y=GntCcKrUMtpCX&uuluKd3TEnoH(vD~>yOsr zJVhJ1AMck3ER-1<@k2!(5+Mw|$?t}D;f*C*?PA)Vr8+HK@!uil?nDpi>OBD&YH2aY zyRT%bvW|+%1ECgxPn;4Imc8m0PNbd9Pjk$5KYE~_Sh-|rY_P%e4?zz0I2BeH$>tBD ze}n1wB5}Li9b>I?ub|xZIohXf+`TC9g5a$nzOrJx(q`XJ-{;7DG!c(NK}y=0@AO@I zL_y$L)a@SM#6-Qq!RueoIzQ$Eg$nb#Q{~Y$VvfQ_DMd9Hs8_faZIs!bh$aP}#)Zpsk0Ukv*HJGPElvTXQDK)Ir#(6%r z*N}3p?w2}DrsJZ|CxGJx`!OXD_|H~>tL$~_>v439noAIACQ9TxfMrJexljQ&KE=*q z>&AtHoKuSUo@K++Bbz7Gww*9Rlp)v)+%pmy8T-;<95+PchhFQ8Os|Is(>|No{9Fw( zb+c9z3-Wn5qmMe11mLRVM6uHpCRrU&gi368y zwKyh9&0H);eMN73@^gG*TD65$fMmJOQRi?&!;w&-bg4)_8IM`-j*Gu#Ol(N~9P-g^ z5!clZVZ$lr*6e2>u06MS^9=<);u*z)5!3b8brf*E4|}QqXTA0O;L$qQLApEkW|9PM zOa4zb7aM*o7>^_wA!L)@1E>1{PdU|ZB{V6CDg-Er|3 zb@%R_8X}~0VIB(nH=lbY&{Iw7&&|{$0}hM=CnrMo+{;W7$==Lqq;#uG?XU5O}Vs^-48 zeL_=Z&X4Es7qZhwL#JFxOsCl?$bt1JqRT{@%t6F512fpP#&mXYH^viR<%HQ#!v=H& z&`A9bKOhv^*%cc3!Hv&zO*uXAL2zqU=A6a2x@uU@ln=@oG}a{wZO5VDCx3H>OCqoj^}5312|rrLN_Ck-5hMlTh_GA^J$nI)A)htCDoM#epUK`C8~UOmBM*7qVjHcVAB4@&JPZc)o0R^pKLaVyd2LWArTD?&hLI;-V3A7W@xGNmL<`G3o{#AX zhnllm<_Q#HO<}{`A^?-`YP^u8{e#o#G$5nXUy+^DIN?oUMZ>lW!P%c!r}jL2Vn507 zW@n#|&kB5IY`NAQ!==A$CX@#?BgK6-nJe~);PxR_j>Jn;m>WjD=Y-YMk-xtPob7;) ze$q~NyLUSjUF6D2dLd$NTxOiYMN*t=rytEKwZt_Xi~-pOCi(ng4<3;s@gQSPL4S~L zZ5d^9h_}e>!V76aFZAzll(JRKH6FLOxT<|ILxK3EUCGK#sDY=Z>0IW8n?1dIt0wc@ z-p9EdHiC>fxNo_|$hntQ7wpOWQr(M;Ro@6vxfI$+@r6H>ndPhF(Fz=j^7YC`1nk*b zi7~-8I@E-=a~r=y5y)GF zpQtxerIqBX#Ps~zZ*UE)9E|4N(j%0@@<-?ijBvU6<+7%{U8&f>2tEZO6&CRnK&=c$ zUVpWbj$o5F-Zy)%g-Wu;pK{t<_8lmKl>+4rcO-cMkQ>3G?{!z1(~9n*KCsGaOh)i3X?6aG z-}gK{k?381BZCWc!@$Kr9cEDny5_ zrB)wB>-<~_Thq`31>KlQpl+a_vx1rYCaL?tLJ4l^c`IxM8(iF3v6ZR&(1EstA2=6A z;TY_nKP5OlAz5T4j9{l%{=lgfHi9D`EK)~YBuc+(mzRa)Q}B(JSn@(4;j;$KKyrCHda+tHvCf8z<}x3v~7nw#F}$kvvx~Acv{P z(mn>(s0B>?(ORWg|4e=QBUXXm#)HgRq?x;jR3a3p@hP#?ZSif#e4&!P8*VtA<&D#Y zv(=>e?GLq&*02HW?gsJ;v%7n9!-P@I+jLPvF73rkcx1ufIv&#^HRZc*!9eAy$ib6{+{1r?*B(8jz&qRhH;nhHy0mQ(tCgp=^zo?GN4Pg zp*5fE$WX^>=$Io6-6hD!({d&OfW`;khEnHyRL3kuf^J5E6QRL946P^{$AyEsH z9IiHuf?`~$3m%m``!{y|&3|@WJg3Ibi@AJAMDW);=`Jnlt<`WReIIL0*X`(C7YSz|Ewm?D@idR_c2;LL zNMN(Ba=ItP0$hl#wOhh&t6AbeS8iNSuT4MbU)lok6MkE8hEb1#Sr%sJqZhq>2|N?1 z=BSx|r+t!Nj@-oHd1Rh1bDvc}@4&&hn>kuT*e%wvEt(DD^0rt)_8}!AcS8^cNN8wj z=m(qRTQaXDE$wzU+lx9JGmtC2v-|FyFriu*CpXCRCxg~j&rhx*A-Be4Uyk^B0hhS@ z{dSQeH@JE3^2X-&Hy`{*gE^3-$C)!QlUA~_Se&pxW_fU7461bVIfGpSw3UL#wz1&q-&~bAQ z&i=u^x??NXx*4xPu{4n8Ea|X z?qh$Z{S=Gr8<&NyVfFc9{VIph zNM{=J#Y)BYQJ=$M%KA5NVFF-|0a&K{ZDgl#L1sbtZ${sACv(b)TRveexextj>1iS$ zifNmQyh4q(Q4f+Yq)IBzf42MEnIuL z|1|LUJ)mUz?PmpJNI!0+@SB2(lni>t&Mi}=IlTXipLPH5%KryYO928G0~7!N00;of zteQKiI=P}1^@s600962073u&0GCDp00027KSfyp literal 0 HcmV?d00001 From 69e8f9c9dbc13580115d89d34ebad115f5c53161 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 28 Dec 2009 14:09:17 +0100 Subject: [PATCH 016/269] Renamed several dirs, files and classes. NOT TESTED. - renamed imp_client/ to clients/ and ditto for servers/ - renamed imp/ to servers/ - renamed stream/input.h to stream/stream.h - renamed Stream::InputStream to Stream::Stream - updated various tests and makefiles - NOT TESTED YET --- sound/input.h | 4 ++-- sound/{imp => servers}/.gitignore | 0 sound/{imp => servers}/audiere_imp.cpp | 0 sound/{imp => servers}/audiere_imp.h | 2 +- sound/{imp => servers}/input_audiere.cpp | 6 +++--- sound/{imp => servers}/input_audiere.h | 4 ++-- sound/{imp => servers}/input_ffmpeg.cpp | 0 sound/{imp => servers}/input_ffmpeg.h | 2 +- sound/{imp => servers}/input_filter.h | 2 +- sound/{imp => servers}/openal_audiere.h | 0 sound/{imp => servers}/openal_ffmpeg.h | 0 sound/{imp => servers}/output_openal.cpp | 2 +- sound/{imp => servers}/output_openal.h | 2 +- sound/sound.h | 4 ++-- sound/tests/Makefile | 8 ++++---- sound/tests/common.cpp | 2 +- stream/{imp_client => clients}/audiere_file.cpp | 0 stream/{imp_client => clients}/audiere_file.h | 6 +++--- stream/{imp_client => clients}/iwrapper.h | 12 ++++++------ stream/{imp_client => clients}/ogre_datastream.h | 10 +++++----- stream/{imp_server => servers}/ogre_datastream.h | 2 +- stream/{imp_server => servers}/phys_stream.h | 2 +- stream/{input.h => stream.h} | 4 ++-- stream/tests/Makefile | 10 +++++----- stream/tests/audiere_client_test.cpp | 4 ++-- stream/tests/dummy_input.cpp | 4 ++-- stream/tests/dummy_test.cpp | 2 +- stream/tests/ogre_client_test.cpp | 2 +- tests/Makefile | 2 +- tests/ogrevfs_audiere_openal_test.cpp | 4 ++-- vfs/{imp_client => clients}/ogre_archive.cpp | 2 +- vfs/{imp_client => clients}/ogre_archive.h | 0 vfs/{imp_client => clients}/wrapper.h | 0 vfs/{imp_server => servers}/ogre_vfs.cpp | 4 ++-- vfs/{imp_server => servers}/ogre_vfs.h | 2 +- vfs/{imp_server => servers}/physfs_vfs.h | 4 ++-- vfs/tests/Makefile | 10 +++++----- vfs/tests/dummy_test.cpp | 2 +- vfs/tests/dummy_vfs.cpp | 2 +- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_server_test.cpp | 2 +- vfs/tests/physfs_server_test.cpp | 2 +- vfs/tests/server_common.cpp | 2 +- vfs/vfs.h | 4 ++-- 44 files changed, 70 insertions(+), 70 deletions(-) rename sound/{imp => servers}/.gitignore (100%) rename sound/{imp => servers}/audiere_imp.cpp (100%) rename sound/{imp => servers}/audiere_imp.h (95%) rename sound/{imp => servers}/input_audiere.cpp (94%) rename sound/{imp => servers}/input_audiere.h (92%) rename sound/{imp => servers}/input_ffmpeg.cpp (100%) rename sound/{imp => servers}/input_ffmpeg.h (95%) rename sound/{imp => servers}/input_filter.h (96%) rename sound/{imp => servers}/openal_audiere.h (100%) rename sound/{imp => servers}/openal_ffmpeg.h (100%) rename sound/{imp => servers}/output_openal.cpp (99%) rename sound/{imp => servers}/output_openal.h (97%) rename stream/{imp_client => clients}/audiere_file.cpp (100%) rename stream/{imp_client => clients}/audiere_file.h (88%) rename stream/{imp_client => clients}/iwrapper.h (58%) rename stream/{imp_client => clients}/ogre_datastream.h (78%) rename stream/{imp_server => servers}/ogre_datastream.h (95%) rename stream/{imp_server => servers}/phys_stream.h (95%) rename stream/{input.h => stream.h} (96%) rename vfs/{imp_client => clients}/ogre_archive.cpp (97%) rename vfs/{imp_client => clients}/ogre_archive.h (100%) rename vfs/{imp_client => clients}/wrapper.h (100%) rename vfs/{imp_server => servers}/ogre_vfs.cpp (91%) rename vfs/{imp_server => servers}/ogre_vfs.h (97%) rename vfs/{imp_server => servers}/physfs_vfs.h (93%) diff --git a/sound/input.h b/sound/input.h index d27de97a63..f61a029ffe 100644 --- a/sound/input.h +++ b/sound/input.h @@ -4,7 +4,7 @@ #include #include -#include "../stream/input.h" +#include "../stream/stream.h" namespace Mangle { namespace Sound { @@ -73,7 +73,7 @@ class InputManager virtual InputSource *load(const std::string &file) = 0; /// Load a sound input source from stream (if canLoadStream is true) - virtual InputSource *load(Stream::InputStream *input) = 0; + virtual InputSource *load(Stream::Stream *input) = 0; /// Virtual destructor virtual ~InputManager() {} diff --git a/sound/imp/.gitignore b/sound/servers/.gitignore similarity index 100% rename from sound/imp/.gitignore rename to sound/servers/.gitignore diff --git a/sound/imp/audiere_imp.cpp b/sound/servers/audiere_imp.cpp similarity index 100% rename from sound/imp/audiere_imp.cpp rename to sound/servers/audiere_imp.cpp diff --git a/sound/imp/audiere_imp.h b/sound/servers/audiere_imp.h similarity index 95% rename from sound/imp/audiere_imp.h rename to sound/servers/audiere_imp.h index 480611ce3d..5ebb812bdb 100644 --- a/sound/imp/audiere_imp.h +++ b/sound/servers/audiere_imp.h @@ -20,7 +20,7 @@ class AudiereManager : public Manager virtual Sound *load(const std::string &file, bool stream=false); /// not implemented yet - virtual Sound *load(Stream::InputStream *input, bool stream=false) + virtual Sound *load(Stream::Stream *input, bool stream=false) { assert(0); } /// disabled diff --git a/sound/imp/input_audiere.cpp b/sound/servers/input_audiere.cpp similarity index 94% rename from sound/imp/input_audiere.cpp rename to sound/servers/input_audiere.cpp index b7a38ffd1e..c48f45013b 100644 --- a/sound/imp/input_audiere.cpp +++ b/sound/servers/input_audiere.cpp @@ -1,7 +1,7 @@ #include "input_audiere.h" #include -#include "../../stream/imp_client/audiere_file.h" +#include "../../stream/clients/audiere_file.h" // Exception handling class Audiere_Exception : public std::exception @@ -33,7 +33,7 @@ AudiereInput::AudiereInput() InputSource *AudiereInput::load(const std::string &file) { return new AudiereSource(file); } -InputSource *AudiereInput::load(Stream::InputStream *input) +InputSource *AudiereInput::load(Stream::Stream *input) { return new AudiereSource(input); } // --- InputSource --- @@ -47,7 +47,7 @@ AudiereSource::AudiereSource(const std::string &file) buf = CreateSampleBuffer(sample); } -AudiereSource::AudiereSource(Stream::InputStream *input) +AudiereSource::AudiereSource(Stream::Stream *input) { SampleSourcePtr sample = OpenSampleSource (new Stream::AudiereFile(input)); diff --git a/sound/imp/input_audiere.h b/sound/servers/input_audiere.h similarity index 92% rename from sound/imp/input_audiere.h rename to sound/servers/input_audiere.h index 4bc42cbc61..e753b0174c 100644 --- a/sound/imp/input_audiere.h +++ b/sound/servers/input_audiere.h @@ -18,7 +18,7 @@ class AudiereInput : public InputManager InputSource *load(const std::string &file); /// Load a source from a stream - virtual InputSource *load(Stream::InputStream *input); + virtual InputSource *load(Stream::Stream *input); }; /// Audiere InputSource implementation @@ -28,7 +28,7 @@ class AudiereSource : public InputSource public: AudiereSource(const std::string &file); - AudiereSource(Stream::InputStream *input); + AudiereSource(Stream::Stream *input); InputStream *getStream(); void drop() { delete this; } }; diff --git a/sound/imp/input_ffmpeg.cpp b/sound/servers/input_ffmpeg.cpp similarity index 100% rename from sound/imp/input_ffmpeg.cpp rename to sound/servers/input_ffmpeg.cpp diff --git a/sound/imp/input_ffmpeg.h b/sound/servers/input_ffmpeg.h similarity index 95% rename from sound/imp/input_ffmpeg.h rename to sound/servers/input_ffmpeg.h index ae25732b57..b744a75856 100644 --- a/sound/imp/input_ffmpeg.h +++ b/sound/servers/input_ffmpeg.h @@ -37,7 +37,7 @@ class FFM_InputManager : public InputManager virtual InputSource *load(const std::string &file); /// not supported - virtual InputSource *load(Stream::InputStream *input) { assert(0); } + virtual InputSource *load(Stream::Stream *input) { assert(0); } }; /// FFMpeg implementation of InputSource diff --git a/sound/imp/input_filter.h b/sound/servers/input_filter.h similarity index 96% rename from sound/imp/input_filter.h rename to sound/servers/input_filter.h index 4fcae320c2..455a60e14f 100644 --- a/sound/imp/input_filter.h +++ b/sound/servers/input_filter.h @@ -63,7 +63,7 @@ class InputFilter : public Manager virtual Sound *load(const std::string &file, bool stream=false) { return load(inp->load(file), stream); } - virtual Sound *load(Stream::InputStream *input, bool stream=false) + virtual Sound *load(Stream::Stream *input, bool stream=false) { return load(inp->load(input), stream); } virtual Sound *load(InputSource *input, bool stream=false) diff --git a/sound/imp/openal_audiere.h b/sound/servers/openal_audiere.h similarity index 100% rename from sound/imp/openal_audiere.h rename to sound/servers/openal_audiere.h diff --git a/sound/imp/openal_ffmpeg.h b/sound/servers/openal_ffmpeg.h similarity index 100% rename from sound/imp/openal_ffmpeg.h rename to sound/servers/openal_ffmpeg.h diff --git a/sound/imp/output_openal.cpp b/sound/servers/output_openal.cpp similarity index 99% rename from sound/imp/output_openal.cpp rename to sound/servers/output_openal.cpp index e369818360..06a8edca80 100644 --- a/sound/imp/output_openal.cpp +++ b/sound/servers/output_openal.cpp @@ -98,7 +98,7 @@ OpenAL_Manager::~OpenAL_Manager() Sound *OpenAL_Manager::load(const std::string &file, bool stream) { assert(0 && "OpenAL cannot decode files"); } -Sound *OpenAL_Manager::load(Stream::InputStream*,bool) +Sound *OpenAL_Manager::load(Stream::Stream*,bool) { assert(0 && "OpenAL cannot decode streams"); } Sound *OpenAL_Manager::load(InputSource *source, bool stream) diff --git a/sound/imp/output_openal.h b/sound/servers/output_openal.h similarity index 97% rename from sound/imp/output_openal.h rename to sound/servers/output_openal.h index bf92197df2..53226c32f3 100644 --- a/sound/imp/output_openal.h +++ b/sound/servers/output_openal.h @@ -26,7 +26,7 @@ public: void remove_stream(LST::iterator); virtual Sound *load(const std::string &file, bool stream=false); - virtual Sound *load(Stream::InputStream *input, bool stream=false); + virtual Sound *load(Stream::Stream *input, bool stream=false); virtual Sound *load(InputSource* input, bool stream=false); virtual void update(); virtual void setListenerPos(float x, float y, float z, diff --git a/sound/sound.h b/sound/sound.h index 90407141ed..0a51e9f939 100644 --- a/sound/sound.h +++ b/sound/sound.h @@ -4,7 +4,7 @@ #include #include "input.h" -#include "../stream/input.h" +#include "../stream/stream.h" namespace Mangle { namespace Sound { @@ -145,7 +145,7 @@ class Manager @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(Stream::InputStream *input, bool stream=false) = 0; + virtual Sound *load(Stream::Stream *input, bool stream=false) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 5d38b4d1da..8a9c748124 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ -I../imp/ +GCC=g++ -I../ all: audiere_test ffmpeg_openal_test openal_audiere_test @@ -6,13 +6,13 @@ L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ffmpeg_openal_test: ffmpeg_openal_test.cpp ../imp/input_ffmpeg.cpp ../imp/output_openal.cpp +ffmpeg_openal_test: ffmpeg_openal_test.cpp ../servers/input_ffmpeg.cpp ../servers/output_openal.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) -openal_audiere_test: openal_audiere_test.cpp ../imp/input_audiere.cpp ../imp/output_openal.cpp ../../stream/imp_client/audiere_file.cpp +openal_audiere_test: openal_audiere_test.cpp ../servers/input_audiere.cpp ../servers/output_openal.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -audiere_test: audiere_test.cpp ../imp/audiere_imp.cpp +audiere_test: audiere_test.cpp ../servers/audiere_imp.cpp $(GCC) $^ -o $@ $(L_AUDIERE) clean: diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp index 28c4705360..818d1645f4 100644 --- a/sound/tests/common.cpp +++ b/sound/tests/common.cpp @@ -6,7 +6,7 @@ using namespace std; -class TestStream : public Mangle::Stream::InputStream +class TestStream : public Mangle::Stream::Stream { ifstream io; diff --git a/stream/imp_client/audiere_file.cpp b/stream/clients/audiere_file.cpp similarity index 100% rename from stream/imp_client/audiere_file.cpp rename to stream/clients/audiere_file.cpp diff --git a/stream/imp_client/audiere_file.h b/stream/clients/audiere_file.h similarity index 88% rename from stream/imp_client/audiere_file.h rename to stream/clients/audiere_file.h index a1910d274c..87833e6b31 100644 --- a/stream/imp_client/audiere_file.h +++ b/stream/clients/audiere_file.h @@ -13,11 +13,11 @@ namespace Stream { This lets Audiere read sound files from any generic archive or file manager that supports Mangle streams. */ -class AudiereFile : public audiere::RefImplementation, _IWrapper +class AudiereFile : public audiere::RefImplementation, _SWrapper { public: - AudiereFile(InputStream *inp, bool autoDel=false) - : _IWrapper(inp, autoDel) {} + AudiereFile(Stream *inp, bool autoDel=false) + : _SWrapper(inp, autoDel) {} /// Read 'count' bytes, return bytes successfully read int read(void *buf, int count) diff --git a/stream/imp_client/iwrapper.h b/stream/clients/iwrapper.h similarity index 58% rename from stream/imp_client/iwrapper.h rename to stream/clients/iwrapper.h index ba2d1fb1fb..cc12d5b7e2 100644 --- a/stream/imp_client/iwrapper.h +++ b/stream/clients/iwrapper.h @@ -1,29 +1,29 @@ #ifndef MANGLE_STREAM_IWRAPPER_H #define MANGLE_STREAM_IWRAPPER_H -#include "../input.h" +#include "../stream.h" #include namespace Mangle { namespace Stream { -/** A generic wrapper class for a Stream::Input object. +/** A generic wrapper class for a Stream::Stream object. This is used by other implementations. */ -class _IWrapper +class _SWrapper { private: bool autoDel; protected: - InputStream *inp; + Stream *inp; public: - _IWrapper(InputStream *_inp, bool _autoDel = false) + _SWrapper(Stream *_inp, bool _autoDel = false) : inp(_inp), autoDel(_autoDel) { assert(inp != NULL); } - virtual ~_IWrapper() { if(autoDel) delete inp; } + virtual ~_SWrapper() { if(autoDel) delete inp; } }; }} // namespaces diff --git a/stream/imp_client/ogre_datastream.h b/stream/clients/ogre_datastream.h similarity index 78% rename from stream/imp_client/ogre_datastream.h rename to stream/clients/ogre_datastream.h index 63ca66e204..6d09799307 100644 --- a/stream/imp_client/ogre_datastream.h +++ b/stream/clients/ogre_datastream.h @@ -14,7 +14,7 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class MangleDataStream : public Ogre::DataStream, _IWrapper +class MangleDataStream : public Ogre::DataStream, _SWrapper { void init() { @@ -25,12 +25,12 @@ class MangleDataStream : public Ogre::DataStream, _IWrapper public: /// Constructor without name - MangleDataStream(InputStream *inp, bool autoDel=false) - : _IWrapper(inp, autoDel) { init(); } + MangleDataStream(Stream *inp, bool autoDel=false) + : _SWrapper(inp, autoDel) { init(); } /// Constructor for a named data stream - MangleDataStream(const Ogre::String &name, InputStream *inp, bool autoDel=false) - : _IWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } + MangleDataStream(const Ogre::String &name, Stream *inp, bool autoDel=false) + : _SWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/imp_server/ogre_datastream.h b/stream/servers/ogre_datastream.h similarity index 95% rename from stream/imp_server/ogre_datastream.h rename to stream/servers/ogre_datastream.h index ef922fa7f8..184fa1668f 100644 --- a/stream/imp_server/ogre_datastream.h +++ b/stream/servers/ogre_datastream.h @@ -12,7 +12,7 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class OgreStream : public InputStream +class OgreStream : public Stream { Ogre::DataStreamPtr inp; diff --git a/stream/imp_server/phys_stream.h b/stream/servers/phys_stream.h similarity index 95% rename from stream/imp_server/phys_stream.h rename to stream/servers/phys_stream.h index 9566194369..3660391fda 100644 --- a/stream/imp_server/phys_stream.h +++ b/stream/servers/phys_stream.h @@ -7,7 +7,7 @@ namespace Mangle { namespace Stream { /// A Stream wrapping a PHYSFS_file stream from the PhysFS library. -class PhysFile : public InputStream +class PhysFile : public Stream { PHYSFS_file *file; diff --git a/stream/input.h b/stream/stream.h similarity index 96% rename from stream/input.h rename to stream/stream.h index 0a178d8fc6..7116b8e2a8 100644 --- a/stream/input.h +++ b/stream/stream.h @@ -7,7 +7,7 @@ namespace Mangle { namespace Stream { /// An abstract interface for a stream data. -class InputStream +class Stream { public: // Feature options. These should be set in the constructor. @@ -22,7 +22,7 @@ class InputStream bool hasSize; /// Virtual destructor - virtual ~InputStream() {} + virtual ~Stream() {} /** Read a given number of bytes from the stream. Returns the actual number read. If the return value is less than count, then the diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 3d2a730b8c..84ec228cf1 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ -I../imp_client/ +GCC=g++ -I../ all: ogre_client_test dummy_test audiere_client_test @@ -6,13 +6,13 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../input.h ../imp_client/iwrapper.h ../imp_client/audiere_file.h ../imp_client/audiere_file.cpp - $(GCC) $< -o $@ ../imp_client/audiere_file.cpp $(L_AUDIERE) +audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp + $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -dummy_test: dummy_test.cpp dummy_input.cpp ../input.h +dummy_test: dummy_test.cpp dummy_input.cpp ../stream.h $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index 7a5b5afc24..f64de0649b 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_input.cpp" -#include "../imp_client/audiere_file.h" +#include "../clients/audiere_file.h" #include #include @@ -10,7 +10,7 @@ int main() { char str[12]; memset(str, 0, 12); - InputStream *inp = new DummyInput(); + Stream *inp = new DummyInput(); FilePtr p(new AudiereFile(inp, true)); cout << "pos=" << p->tell() << endl; p->read(str, 2); diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp index 0bfe5ad52a..c93f421602 100644 --- a/stream/tests/dummy_input.cpp +++ b/stream/tests/dummy_input.cpp @@ -1,5 +1,5 @@ // This file is shared between several test programs -#include "../input.h" +#include "../stream.h" #include #include @@ -8,7 +8,7 @@ using namespace Mangle::Stream; // A simple dummy stream const char _data[12] = "hello world"; -class DummyInput : public InputStream +class DummyInput : public Stream { private: int pos; diff --git a/stream/tests/dummy_test.cpp b/stream/tests/dummy_test.cpp index 64dd8f19b8..cd8f64e3c3 100644 --- a/stream/tests/dummy_test.cpp +++ b/stream/tests/dummy_test.cpp @@ -7,7 +7,7 @@ using namespace std; int main() { - InputStream *inp = new DummyInput(); + Stream *inp = new DummyInput(); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index b6a46ad3bb..a2bae1c8e6 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -7,7 +7,7 @@ using namespace std; int main() { - InputStream *inp = new DummyInput(); + Stream *inp = new DummyInput(); DataStreamPtr p(new MangleDataStream("hello", inp, true)); cout << "Name: " << p->getName() << endl; cout << "As string: " << p->getAsString() << endl; diff --git a/tests/Makefile b/tests/Makefile index 6aeaa5eb51..ed680f3dba 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,7 @@ L_OGRE=$(shell pkg-config --libs OGRE) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/imp_server/ogre_vfs.cpp ../sound/imp/input_audiere.cpp ../sound/imp/output_openal.cpp ../stream/imp_client/audiere_file.cpp +ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/servers/input_audiere.cpp ../sound/servers/output_openal.cpp ../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) clean: diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp index 7bc6131da9..810fd635e4 100644 --- a/tests/ogrevfs_audiere_openal_test.cpp +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -7,8 +7,8 @@ */ -#include "sound/imp/openal_audiere.h" -#include "vfs/imp_server/ogre_vfs.h" +#include "sound/servers/openal_audiere.h" +#include "vfs/servers/ogre_vfs.h" #include #include diff --git a/vfs/imp_client/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp similarity index 97% rename from vfs/imp_client/ogre_archive.cpp rename to vfs/clients/ogre_archive.cpp index d7adaa07d2..936eda9b6b 100644 --- a/vfs/imp_client/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -1,6 +1,6 @@ #include "ogre_archive.h" -#include "../../stream/imp_client/ogre_datastream.h" +#include "../../stream/clients/ogre_datastream.h" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/imp_client/ogre_archive.h b/vfs/clients/ogre_archive.h similarity index 100% rename from vfs/imp_client/ogre_archive.h rename to vfs/clients/ogre_archive.h diff --git a/vfs/imp_client/wrapper.h b/vfs/clients/wrapper.h similarity index 100% rename from vfs/imp_client/wrapper.h rename to vfs/clients/wrapper.h diff --git a/vfs/imp_server/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp similarity index 91% rename from vfs/imp_server/ogre_vfs.cpp rename to vfs/servers/ogre_vfs.cpp index 9ca1c4b8c8..af1f7c9632 100644 --- a/vfs/imp_server/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -1,5 +1,5 @@ #include "ogre_vfs.h" -#include "../../stream/imp_server/ogre_datastream.h" +#include "../../stream/servers/ogre_datastream.h" using namespace Mangle::VFS; @@ -18,7 +18,7 @@ OgreVFS::OgreVFS(const std::string &_group) group = gm->getWorldResourceGroupName(); } -Mangle::Stream::InputStream *OgreVFS::open(const std::string &name) +Mangle::Stream::Stream *OgreVFS::open(const std::string &name) { Ogre::DataStreamPtr data = gm->openResource(name, group); return new Stream::OgreStream(data); diff --git a/vfs/imp_server/ogre_vfs.h b/vfs/servers/ogre_vfs.h similarity index 97% rename from vfs/imp_server/ogre_vfs.h rename to vfs/servers/ogre_vfs.h index 9a01c1786a..8fe2cca1cd 100644 --- a/vfs/imp_server/ogre_vfs.h +++ b/vfs/servers/ogre_vfs.h @@ -36,7 +36,7 @@ class OgreVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::InputStream *open(const std::string &name); + virtual Stream::Stream *open(const std::string &name); /// Check for the existence of a file virtual bool isFile(const std::string &name) const diff --git a/vfs/imp_server/physfs_vfs.h b/vfs/servers/physfs_vfs.h similarity index 93% rename from vfs/imp_server/physfs_vfs.h rename to vfs/servers/physfs_vfs.h index ace3d84c96..c25a0035cd 100644 --- a/vfs/imp_server/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.h @@ -2,7 +2,7 @@ #define MANGLE_VFS_PHYSFS_SERVER_H #include "../vfs.h" -#include "../../stream/imp_server/phys_stream.h" +#include "../../stream/servers/phys_stream.h" #include #include @@ -26,7 +26,7 @@ class PhysVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::InputStream *open(const std::string &name) + virtual Stream::Stream *open(const std::string &name) { return new Stream::PhysFile(PHYSFS_openRead(name.c_str())); } /// Check for the existence of a file diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 2cf7225501..4595c82eac 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -6,16 +6,16 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_PHYSFS=-lphysfs -ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../imp_client/wrapper.h ../imp_client/ogre_archive.h ../imp_client/ogre_archive.cpp - $(GCC) $< ../imp_client/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/wrapper.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp + $(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) ogre_resource_test: ogre_resource_test.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -ogre_server_test: ogre_server_test.cpp ../vfs.h ../imp_server/ogre_vfs.h ../imp_server/ogre_vfs.cpp - $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../imp_server/ogre_vfs.cpp +ogre_server_test: ogre_server_test.cpp ../vfs.h ../servers/ogre_vfs.h ../servers/ogre_vfs.cpp + $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../servers/ogre_vfs.cpp -physfs_server_test: physfs_server_test.cpp ../vfs.h ../imp_server/physfs_vfs.h +physfs_server_test: physfs_server_test.cpp ../vfs.h ../servers/physfs_vfs.h $(GCC) $< -o $@ $(L_PHYSFS) dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp index e5659d62f6..b65f383358 100644 --- a/vfs/tests/dummy_test.cpp +++ b/vfs/tests/dummy_test.cpp @@ -33,7 +33,7 @@ int main() cout << endl; print(vfs.stat("dir")); - InputStream *inp = vfs.open("file1"); + Stream *inp = vfs.open("file1"); cout << "filesize: " << inp->size() << endl; return 0; diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp index d8b259f28b..e7c77a7533 100644 --- a/vfs/tests/dummy_vfs.cpp +++ b/vfs/tests/dummy_vfs.cpp @@ -17,7 +17,7 @@ public: } // We only support opening 'file1' at the moment. - Mangle::Stream::InputStream *open(const std::string &name) + Mangle::Stream::Stream *open(const std::string &name) { assert(name == "file1"); return new DummyInput(); diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index a236c28f09..d38add4da8 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_vfs.cpp" -#include "../imp_client/ogre_archive.h" +#include "../clients/ogre_archive.h" #include using namespace Ogre; diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp index 3e0fcbef4a..cb46248942 100644 --- a/vfs/tests/ogre_server_test.cpp +++ b/vfs/tests/ogre_server_test.cpp @@ -1,4 +1,4 @@ -#include "../imp_server/ogre_vfs.h" +#include "../servers/ogre_vfs.h" #include diff --git a/vfs/tests/physfs_server_test.cpp b/vfs/tests/physfs_server_test.cpp index 282566dd1b..ae42083fdb 100644 --- a/vfs/tests/physfs_server_test.cpp +++ b/vfs/tests/physfs_server_test.cpp @@ -1,4 +1,4 @@ -#include "../imp_server/physfs_vfs.h" +#include "../servers/physfs_vfs.h" #include "server_common.cpp" diff --git a/vfs/tests/server_common.cpp b/vfs/tests/server_common.cpp index ddb85b0c3c..ff16ac7f2c 100644 --- a/vfs/tests/server_common.cpp +++ b/vfs/tests/server_common.cpp @@ -14,7 +14,7 @@ void find(VFS &vfs, const std::string &file) return; } - InputStream *data = vfs.open(file); + Stream *data = vfs.open(file); cout << "Size: " << data->size() << endl; diff --git a/vfs/vfs.h b/vfs/vfs.h index a9ffb6b622..4054c6069d 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.h @@ -1,7 +1,7 @@ #ifndef MANGLE_VFS_H #define MANGLE_VFS_H -#include "../stream/input.h" +#include "../stream/stream.h" #include #include @@ -51,7 +51,7 @@ class VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::InputStream *open(const std::string &name) = 0; + virtual Stream::Stream *open(const std::string &name) = 0; /// Check for the existence of a file virtual bool isFile(const std::string &name) const = 0; From cb638cd44e71d0601734eac935484273f508b1a1 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 28 Dec 2009 17:15:52 +0100 Subject: [PATCH 017/269] Started reworking the sound system. Added sources/ - WIP --- sound/source.h | 63 ++++++++ sound/sources/audiere_source.cpp | 139 ++++++++++++++++++ sound/sources/audiere_source.h | 51 +++++++ sound/sources/ffmpeg_source.cpp | 238 +++++++++++++++++++++++++++++++ sound/sources/ffmpeg_source.h | 47 ++++++ sound/sources/loadertemplate.h | 26 ++++ 6 files changed, 564 insertions(+) create mode 100644 sound/source.h create mode 100644 sound/sources/audiere_source.cpp create mode 100644 sound/sources/audiere_source.h create mode 100644 sound/sources/ffmpeg_source.cpp create mode 100644 sound/sources/ffmpeg_source.h create mode 100644 sound/sources/loadertemplate.h diff --git a/sound/source.h b/sound/source.h new file mode 100644 index 0000000000..7361fb1182 --- /dev/null +++ b/sound/source.h @@ -0,0 +1,63 @@ +#ifndef MANGLE_SOUND_SOURCE_H +#define MANGLE_SOUND_SOURCE_H + +#include +#include +#include + +#include "../stream/stream.h" + +namespace Mangle { +namespace Sound { + +/// A stream containing raw sound data and information about the format +class SampleSource : public Stream::Stream +{ + protected: + bool isEof; + + public: + SampleSource() + { + // These are usually not needed for sound data + isSeekable = false; + hasPosition = false; + hasSize = false; + + isEof = false; + } + + /// Get the sample rate, number of channels, and bits per + /// sample. NULL parameters are ignored. + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) const = 0; + + bool eof() const { return isEof; } + + // Disabled functions + void seek(size_t pos) const { assert(0); } + size_t tell() const { assert(0); } + size_t size() const { assert(0); } +}; + +/// A factory interface for loading SampleSources from file or stream +class SampleSourceLoader +{ + public: + /// If true, the stream version of load() works + bool canLoadStream; + + /// If true, the file version of load() works + bool canLoadFile; + + /// Load a sound input source from file (if canLoadFile is true) + virtual SampleSource *load(const std::string &file) = 0; + + /// Load a sound input source from stream (if canLoadStream is true) + virtual SampleSource *load(Stream::Stream *input) = 0; + + /// Virtual destructor + virtual ~SampleSourceLoader() {} +}; + +}} // namespaces +#endif diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp new file mode 100644 index 0000000000..6ebc4f8813 --- /dev/null +++ b/sound/sources/audiere_source.cpp @@ -0,0 +1,139 @@ +#include "audiere_source.h" + +#include "../../stream/clients/audiere_file.h" + +// Exception handling +class Audiere_Exception : public std::exception +{ + std::string msg; + + public: + + Audiere_Exception(const std::string &m) : msg(m) {} + ~Audiere_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw Audiere_Exception("Audiere exception: " + msg); +} + +using namespace audiere; +using namespace Mangle::Sound; + +// --- SampleSource --- + +void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + SampleFormat fmt; + sample->getFormat(*channels, *rate, fmt); + if(fmt == SF_U8) + *bits = 8; + else if(fmt == SF_S16) + *bits = 16; + else assert(0); +} + +/* + Get data. Since Audiere operates with frames, not bytes, there's a + little conversion magic going on here. We need to make sure we're + reading a whole number of frames - if not, we need to store the + remainding part of the last frame and remember it for the next read + operation. + */ +size_t AudiereSource::read(void *_data, size_t length) +{ + if(isEof) return 0; + + char *data = (char*)_data; + + // Move the remains from the last operation first + if(pullSize) + { + // pullSize is how much was stored the last time, so skip that. + memcpy(data, pullOver+pullSize, PSIZE-pullSize); + length -= pullSize; + data += pullSize; + } + + // Determine the overshoot up front + pullSize = length % frameSize; + + // Number of whole frames + int frames = length / frameSize; + + // Read the data + int res = sample->read(frames, data); + + if(res < frames) + isEof = true; + + // Are we missing data? If we're at the end of the stream, then this + // doesn't apply. + if(!isEof && pullSize) + { + // Read one more sample + if(sample->read(1, pullOver) != 0) + { + // Then, move as much of it as we can fit into the output + // data + memcpy(data+length-pullSize, pullOver, pullSize); + } + else + // Failed reading, we're out of data + isEof = true; + } + + // If we're at the end of the stream, then no data remains to be + // pulled over + if(isEof) + pullSize = 0; + + // Return the total number of bytes stored + return frameSize*res + pullSize; +} + +// --- Constructors --- + +AudiereSource::AudiereSource(const std::string &file) +{ + sample = OpenSampleSource(file.c_str()); + + if(!sample) + fail("Couldn't load file " + file); + + getFormat(); +} + +AudiereSource::AudiereSource(Stream::Stream *input) +{ + // Use our Stream::AudiereFile implementation to convert a Mangle + // 'Stream' to an Audiere 'File' + sample = OpenSampleSource(new Stream::AudiereFile(input)); + if(!sample) + fail("Couldn't load stream"); + + getFormat(); +} + +AudiereSource::AudiereSource(audiere::SampleSourcePtr src) + : sample(src) +{ assert(sample); getFormat(); } + +// Common function called from all constructors +AudiereSource::getFormat() +{ + assert(sample); + + SampleFormat fmt; + int channels, rate; + sample->getFormat(channels, rate, fmt); + + // Calculate the size of one frame + frameSize = GetSampleSize(fmt) * channels; + + // Make sure that our pullover hack will work. Increase this size if + // this doesn't work in all cases. + assert(frameSize <= PSIZE); +} diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h new file mode 100644 index 0000000000..12ae81bce5 --- /dev/null +++ b/sound/sources/audiere_source.h @@ -0,0 +1,51 @@ +#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H +#define MANGLE_SOUND_AUDIERE_SOURCE_H + +#include "../source.h" + +#include + +namespace Mangle { +namespace Sound { + +/// A sample source that decodes files using Audiere +class AudiereSource : public SampleSource +{ + audiere::SampleSourcePtr sample; + + // Number of bytes we cache between reads. This should correspond to + // the maximum possible value of frameSize. + static const int PSIZE = 10; + + // Size of one frame, in bytes + int frameSize; + + // Temporary storage for unevenly read samples. See the comment for + // read() in the .cpp file. + char pullOver[PSIZE]; + // How much of the above buffer is in use + int pullSize; + + void getFormat(); + + public: + /// Decode the given sound file + AudiereSource(const std::string &file); + + /// Decode the given sound stream + AudiereSource(Stream::Stream *src); + + /// Read directly from an existing audiere::SampleSource + AudiereSource(audiere::SampleSourcePtr src); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); +}; + +#include "loadertemplate.h" + +/// A factory that loads AudiereSources from file and stream +typedef SSL_Template AudiereLoader; + +}} // Namespace +#endif diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp new file mode 100644 index 0000000000..d53b1ced35 --- /dev/null +++ b/sound/sources/ffmpeg_source.cpp @@ -0,0 +1,238 @@ +// NOT UPDATED - WIP + +#include "input_ffmpeg.h" +#include + +using namespace Mangle::Sound; + +// Static output buffer. Not thread safe, but supports multiple +// streams operated from the same thread. +static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; +bool FFM_InputManager::init = false; + +/// FFmpeg exception. +class FFM_Exception : public std::exception +{ + std::string msg; + + public: + + FFM_Exception(const std::string &m); + ~FFM_Exception() throw(); + virtual const char* what() const throw(); +}; + +FFM_Exception::FFM_Exception(const std::string &m) + : msg(m) {} + +const char* FFM_Exception::what() const throw() +{ return msg.c_str(); } + +FFM_Exception::~FFM_Exception() throw() {} + +static void fail(const std::string &msg) +{ + throw FFM_Exception("FFMpeg exception: " + msg); +} + + +// --- Manager --- + +FFM_InputManager::FFM_InputManager() +{ + if(!init) + { + av_register_all(); + av_log_set_level(AV_LOG_ERROR); + init = true; + } + + canLoadStream = false; +} + +InputSource *FFM_InputManager::load(const std::string &file) +{ return new FFM_InputSource(file); } + + +// --- Source --- + +FFM_InputSource::FFM_InputSource(const std::string &file) +{ + // FFmpeg doesn't handle several instances from one source. So we + // just store the filename. + name = file; +} + +InputStream *FFM_InputSource::getStream() +{ return new FFM_InputStream(name); } + +void FFM_InputSource::drop() +{ delete this; } + + +// --- Stream --- + +FFM_InputStream::FFM_InputStream(const std::string &file) +{ + std::string msg; + AVCodec *codec; + + empty = false; + + if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) + fail("Error loading audio file " + file); + + if(av_find_stream_info(FmtCtx) < 0) + { + msg = "Error in file stream " + file; + goto err; + } + + // Pick the first audio stream, if any + for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) + { + // Pick the first audio stream + if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) + break; + } + + if(StreamNum == FmtCtx->nb_streams) + fail("File " + file + " didn't contain any audio streams"); + + // Open the decoder + CodecCtx = FmtCtx->streams[StreamNum]->codec; + codec = avcodec_find_decoder(CodecCtx->codec_id); + + if(!codec || avcodec_open(CodecCtx, codec) < 0) + { + msg = "Error loading " + file + ": "; + if(codec) + msg += "coded error"; + else + msg += "no codec found"; + goto err; + } + + // No errors, we're done + return; + + // Handle errors + err: + av_close_input_file(FmtCtx); + fail(msg); +} + +FFM_InputStream::~FFM_InputStream() +{ + avcodec_close(CodecCtx); + av_close_input_file(FmtCtx); +} + +void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +{ + if(rate) *rate = CodecCtx->sample_rate; + if(channels) *channels = CodecCtx->channels; + if(bits) *bits = 16; +} + +uint32_t FFM_InputStream::getData(void *data, uint32_t length) +{ + if(empty) return 0; + + uint32_t left = length; + uint8_t *outPtr = (uint8_t*)data; + + // First, copy over any stored data we might be sitting on + { + int s = storage.size(); + int copy = s; + if(s) + { + // Make sure there's room + if(copy > left) + copy = left; + + // Copy + memcpy(outPtr, &storage[0], copy); + outPtr += copy; + left -= copy; + + // Is there anything left in the storage? + s -= copy; + if(s) + { + assert(left == 0); + + // Move it to the start and resize + memmove(&storage[0], &storage[copy], s); + storage.resize(s); + } + } + } + + // Next, get more input data from stream, and decode it + while(left) + { + AVPacket packet; + + // Get the next packet, if any + if(av_read_frame(FmtCtx, &packet) < 0) + break; + + // We only allow one stream per file at the moment + assert(StreamNum == packet.stream_index); + + // Decode the packet + int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; + int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, + &len, packet.data, packet.size); + assert(tmp < 0 || tmp == packet.size); + + // We don't need the input packet any longer + av_free_packet(&packet); + + if(tmp < 0) + fail("Error decoding audio stream"); + + // Copy whatever data we got, and advance the pointer + if(len > 0) + { + // copy = how many bytes do we copy now + int copy = len; + if(copy > left) + copy = left; + + // len = how many bytes are left uncopied + len -= copy; + + // copy data + memcpy(outPtr, outBuf, copy); + + // left = how much space is left in the caller output + // buffer + left -= copy; + outPtr += copy; + assert(left >= 0); + + if(len > 0) + { + // There were uncopied bytes. Store them for later. + assert(left == 0); + storage.resize(len); + memcpy(&storage[0], outBuf, len); + } + } + } + + // End of loop. Return the number of bytes copied. + assert(left <= length); + + // If we're returning less than asked for, then we're done + if(left > 0) + empty = true; + + return length - left; +} + +void FFM_InputStream::drop() +{ delete this; } diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h new file mode 100644 index 0000000000..bf32084fa2 --- /dev/null +++ b/sound/sources/ffmpeg_source.h @@ -0,0 +1,47 @@ +#ifndef MANGLE_SOUND_FFMPEG_H +#define MANGLE_SOUND_FFMPEG_H + +#include "../input.h" +#include +#include +#include + +extern "C" +{ +#include +#include +} + +namespace Mangle { +namespace Sound { + +class FFMpegSource : public SampleSource +{ + AVFormatContext *FmtCtx; + AVCodecContext *CodecCtx; + int StreamNum; + bool empty; + + std::vector storage; + + public: + /// Decode the given sound file + FFMpegSource(const std::string &file); + + /// Decode the given sound stream (not supported by FFmpeg) + FFMpegSource(Stream::Stream *src) { assert(0); } + + ~FFMpegSource(); + + // Overrides + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); +}; + +#include "loadertemplate.h" + +/// A factory that loads FFMpegSources from file +typedef SSL_Template AudiereLoader; + +}} // namespaces +#endif diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h new file mode 100644 index 0000000000..95d0e798ca --- /dev/null +++ b/sound/sources/loadertemplate.h @@ -0,0 +1,26 @@ +#ifndef SSL_TEMPL_H +#define SSL_TEMPL_H + +template +class SSL_Template : public SampleSourceLoader +{ + SSL_Template() + { + canLoadStream = stream; + canLoadFile = file; + } + + SampleSource *load(const std::string &file) + { + assert(canLoadFile); + return new X(file); + } + + SampleSource *load(Stream::Stream *input) + { + assert(canLoadStream); + return new X(input); + } +}; + +#endif From 6281685f732f799f25565be8e431f1fab7ee9730 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 10:34:40 +0100 Subject: [PATCH 018/269] Finished source/ --- sound/sources/ffmpeg_source.cpp | 56 +++++++++------------------------ sound/sources/ffmpeg_source.h | 10 ++++-- 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index d53b1ced35..af785eaa35 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,14 +1,10 @@ -// NOT UPDATED - WIP - #include "input_ffmpeg.h" -#include using namespace Mangle::Sound; // Static output buffer. Not thread safe, but supports multiple // streams operated from the same thread. static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; -bool FFM_InputManager::init = false; /// FFmpeg exception. class FFM_Exception : public std::exception @@ -35,44 +31,23 @@ static void fail(const std::string &msg) throw FFM_Exception("FFMpeg exception: " + msg); } +// --- Loader --- -// --- Manager --- +static bool init = false; -FFM_InputManager::FFM_InputManager() +FFMpegLoader::FFMpegLoader(bool setup) { - if(!init) + if(setup && !init) { av_register_all(); av_log_set_level(AV_LOG_ERROR); init = true; } - - canLoadStream = false; } -InputSource *FFM_InputManager::load(const std::string &file) -{ return new FFM_InputSource(file); } - - // --- Source --- -FFM_InputSource::FFM_InputSource(const std::string &file) -{ - // FFmpeg doesn't handle several instances from one source. So we - // just store the filename. - name = file; -} - -InputStream *FFM_InputSource::getStream() -{ return new FFM_InputStream(name); } - -void FFM_InputSource::drop() -{ delete this; } - - -// --- Stream --- - -FFM_InputStream::FFM_InputStream(const std::string &file) +FFMpegSource::FFMpegSource(const std::string &file) { std::string msg; AVCodec *codec; @@ -97,7 +72,7 @@ FFM_InputStream::FFM_InputStream(const std::string &file) } if(StreamNum == FmtCtx->nb_streams) - fail("File " + file + " didn't contain any audio streams"); + fail("File '" + file + "' didn't contain any audio streams"); // Open the decoder CodecCtx = FmtCtx->streams[StreamNum]->codec; @@ -105,7 +80,7 @@ FFM_InputStream::FFM_InputStream(const std::string &file) if(!codec || avcodec_open(CodecCtx, codec) < 0) { - msg = "Error loading " + file + ": "; + msg = "Error loading '" + file + "': "; if(codec) msg += "coded error"; else @@ -122,24 +97,24 @@ FFM_InputStream::FFM_InputStream(const std::string &file) fail(msg); } -FFM_InputStream::~FFM_InputStream() +FFMpegSource::~FFMpegSource() { avcodec_close(CodecCtx); av_close_input_file(FmtCtx); } -void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +void FFMpegSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) { if(rate) *rate = CodecCtx->sample_rate; if(channels) *channels = CodecCtx->channels; if(bits) *bits = 16; } -uint32_t FFM_InputStream::getData(void *data, uint32_t length) +size_t FFMpegSource::read(void *data, size_t length) { - if(empty) return 0; + if(isEof) return 0; - uint32_t left = length; + size_t left = length; uint8_t *outPtr = (uint8_t*)data; // First, copy over any stored data we might be sitting on @@ -209,7 +184,7 @@ uint32_t FFM_InputStream::getData(void *data, uint32_t length) memcpy(outPtr, outBuf, copy); // left = how much space is left in the caller output - // buffer + // buffer. This loop repeats as long left is > 0 left -= copy; outPtr += copy; assert(left >= 0); @@ -229,10 +204,7 @@ uint32_t FFM_InputStream::getData(void *data, uint32_t length) // If we're returning less than asked for, then we're done if(left > 0) - empty = true; + isEof = true; return length - left; } - -void FFM_InputStream::drop() -{ delete this; } diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h index bf32084fa2..5317c11c02 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.h @@ -20,7 +20,6 @@ class FFMpegSource : public SampleSource AVFormatContext *FmtCtx; AVCodecContext *CodecCtx; int StreamNum; - bool empty; std::vector storage; @@ -41,7 +40,14 @@ class FFMpegSource : public SampleSource #include "loadertemplate.h" /// A factory that loads FFMpegSources from file -typedef SSL_Template AudiereLoader; +class FFMpegLoader : public SSL_Template +{ + public: + + /// Sets up the libavcodec library. If you want to do your own + /// setup, send a setup=false parameter. + FFMpegLoader(bool setup=true); +}; }} // namespaces #endif From 4ee198d66c8b54527fa35e1eb55e127f33c1a8a4 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 11:56:14 +0100 Subject: [PATCH 019/269] Added SampleBuffer (experimental) --- sound/buffer.h | 57 ++++++++++++++++++++++++++++++++++ sound/sources/loadertemplate.h | 6 ++-- sound/sources/memsource.h | 49 +++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 sound/buffer.h create mode 100644 sound/sources/memsource.h diff --git a/sound/buffer.h b/sound/buffer.h new file mode 100644 index 0000000000..9cb27bd9cb --- /dev/null +++ b/sound/buffer.h @@ -0,0 +1,57 @@ +#ifndef MANGLE_SOUND_BUFFER_H +#define MANGLE_SOUND_BUFFER_H + +#include "source.h" +#include "sources/memsource.h" +#include +#include + +namespace Mangle { +namespace Sound { + +/** A sample buffer is a factory that creates SampleSources from one + single sound source. It is helpful when you have many instances of + one sound and want to use one shared memory buffer. + + This is just a helper class - you don't have to include it in your + program if you don't need it. +*/ +class SampleBuffer +{ + std::vector buffer; + + public: + /// Reads the source into a memory buffer. Not heavily optimized. + SampleBuffer(SampleSource *source) + { + size_t final = 0; + + while(!source->eof()) + { + const int add = 16*1024; + + // Allocate more memory + size_t newSize = final + add; + buffer.resize(newSize); + + // Fill in data + size_t read = source->read(&buffer[final], add); + + // If we couldn't read enough data, we should be at the end + // of the stream + assert(read == add || source->eof()); + + final += read; + } + + // Downsize the buffer to the actual length + buffer.resize(final); + } + + /// Get a new source + SampleSource *get() + { return new MemorySource(&buffer[0], buffer.size()); } +}; + +}} +#endif diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h index 95d0e798ca..0b668fffa6 100644 --- a/sound/sources/loadertemplate.h +++ b/sound/sources/loadertemplate.h @@ -1,7 +1,7 @@ #ifndef SSL_TEMPL_H #define SSL_TEMPL_H -template +template class SSL_Template : public SampleSourceLoader { SSL_Template() @@ -13,13 +13,13 @@ class SSL_Template : public SampleSourceLoader SampleSource *load(const std::string &file) { assert(canLoadFile); - return new X(file); + return new SourceT(file); } SampleSource *load(Stream::Stream *input) { assert(canLoadStream); - return new X(input); + return new SourceT(input); } }; diff --git a/sound/sources/memsource.h b/sound/sources/memsource.h new file mode 100644 index 0000000000..1e38d90524 --- /dev/null +++ b/sound/sources/memsource.h @@ -0,0 +1,49 @@ +#ifndef MANGLE_SOUND_MEMSOURCE_H +#define MANGLE_SOUND_MEMSOURCE_H + +#include "../source.h" + +namespace Mangle { +namespace Sound { + +/// A sample source reading directly from a memory buffer +class MemorySource : public SampleSource +{ + char *buf; + size_t len; + size_t pos; + + int32_t rate, channels, bits; + + public: + MemorySource(void *_buf, size_t _len, int32_t _rate, int32_t _channels, int32_t _bits) + : len(_len), pos(0), rate(_rate), channels(_channels), bits(_bits) + { buf = (char*)_buf; } + + /// Get the sample rate, number of channels, and bits per + /// sample. NULL parameters are ignored. + void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) const + { + if(_rate) *_rate = rate; + if(_channels) *_channels = channels; + if(_bits) *_bits = bits; + } + + bool eof() const { return pos == len; } + + size_t read(void *out, size_t count) + { + assert(len >= pos); + + if(count > (len-pos)) + count = len-pos; + + if(count) memcpy(out, buf+pos, count); + pos += count; + + return count; + } +}; + +}} // namespaces +#endif From fb88d9ef0e3ec4750cd2f8eca1c01a3db13f7377 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 13:12:48 +0100 Subject: [PATCH 020/269] Deleted a bunch of files, started on output --- sound/input.h | 83 -------- sound/{sound.h => output.h} | 107 +++------- sound/servers/.gitignore | 1 - sound/servers/audiere_imp.cpp | 95 --------- sound/servers/audiere_imp.h | 77 ------- sound/servers/input_audiere.cpp | 139 ------------- sound/servers/input_audiere.h | 59 ------ sound/servers/input_ffmpeg.cpp | 224 -------------------- sound/servers/input_ffmpeg.h | 75 ------- sound/servers/input_filter.h | 80 -------- sound/servers/openal_audiere.h | 29 --- sound/servers/openal_ffmpeg.h | 28 --- sound/servers/output_openal.cpp | 353 -------------------------------- sound/servers/output_openal.h | 125 ----------- 14 files changed, 34 insertions(+), 1441 deletions(-) delete mode 100644 sound/input.h rename sound/{sound.h => output.h} (52%) delete mode 100644 sound/servers/.gitignore delete mode 100644 sound/servers/audiere_imp.cpp delete mode 100644 sound/servers/audiere_imp.h delete mode 100644 sound/servers/input_audiere.cpp delete mode 100644 sound/servers/input_audiere.h delete mode 100644 sound/servers/input_ffmpeg.cpp delete mode 100644 sound/servers/input_ffmpeg.h delete mode 100644 sound/servers/input_filter.h delete mode 100644 sound/servers/openal_audiere.h delete mode 100644 sound/servers/openal_ffmpeg.h delete mode 100644 sound/servers/output_openal.cpp delete mode 100644 sound/servers/output_openal.h diff --git a/sound/input.h b/sound/input.h deleted file mode 100644 index f61a029ffe..0000000000 --- a/sound/input.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef MANGLE_SOUND_INPUT_H -#define MANGLE_SOUND_INPUT_H - -#include -#include - -#include "../stream/stream.h" - -namespace Mangle { -namespace Sound { - -/// An abstract interface for a read-once stream of audio data. -/** All instances of this is created through InputSource. Objects - should be manually deleted through a call to drop() when they are - no longer needed. -*/ -class InputStream -{ - public: - /// Get the sample rate, number of channels, and bits per - /// sample. NULL parameters are ignored. - virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0; - - /// Get decoded sound data from the stream. - /** Stores 'length' bytes (or less) in the buffer pointed to by - 'output'. Returns the number of bytes written. The function will - only return less than 'length' at the end of the stream. When - the stream is empty, all subsequent calls will return zero. - - @param output where to store data - @param length number of bytes to get - @return number of bytes actually written - */ - virtual uint32_t getData(void *output, uint32_t length) = 0; - - /// Kill this object - virtual void drop() = 0; - - /// Virtual destructor - virtual ~InputStream() {} -}; - -/// Abstract interface representing one sound source. -/** A sound source may represent one sound file or buffer, and is a - factory for producing InputStream objects from that - sound. Instances of this class are created by an InputManager. All - instances should be deleted through drop() when they are no longer - needed. - */ -class InputSource -{ - public: - /// Create a stream from this sound - virtual InputStream *getStream() = 0; - - /// Kill this object - virtual void drop() = 0; - - /// Virtual destructor - virtual ~InputSource() {} -}; - -/// Main interface to a sound decoder backend. -/** An input manager is a factory of InputSource objects. - */ -class InputManager -{ - public: - /// If true, the stream version of load() works - bool canLoadStream; - - /// Load a sound input source from file - virtual InputSource *load(const std::string &file) = 0; - - /// Load a sound input source from stream (if canLoadStream is true) - virtual InputSource *load(Stream::Stream *input) = 0; - - /// Virtual destructor - virtual ~InputManager() {} -}; - -}} // namespaces -#endif diff --git a/sound/sound.h b/sound/output.h similarity index 52% rename from sound/sound.h rename to sound/output.h index 0a51e9f939..4cdac7bed5 100644 --- a/sound/sound.h +++ b/sound/output.h @@ -1,21 +1,24 @@ -#ifndef MANGLE_SOUND_SOUND_H -#define MANGLE_SOUND_SOUND_H +#ifndef MANGLE_SOUND_OUTPUT_H +#define MANGLE_SOUND_OUTPUT_H #include -#include "input.h" +#include "source.h" #include "../stream/stream.h" namespace Mangle { namespace Sound { -/// Abstract interface for sound instances -/** This class represents one sound instance, which may be played, - stopped, paused and so on. Instances are created from the Sound - class. All instances must be terminated manually using the drop() - function when they are no longer in use. +/// Abstract interface for a single playable sound +/** This class represents one sound outlet, which may be played, + stopped, paused and so on. + + Sound instances are created from the SoundFactory class. Sounds + may be connected to a SampleSource or read directly from a file, + and they may support 3d sounds, looping and other features + depending on the capabilities of the backend system. */ -class Instance +class Sound { public: /// Play or resume the sound @@ -36,63 +39,26 @@ class Instance /// Set the position. May not have any effect on 2D sounds. virtual void setPos(float x, float y, float z) = 0; - /// Kill the current object - virtual void drop() = 0; - - /// Virtual destructor - virtual ~Instance() {} -}; - -/// Abstract interface for sound files or sources -/** This class acts as a factory for sound Instance objects. - Implementations may choose to store shared sound buffers or other - optimizations in subclasses of Sound. Objects of this class are - created through the Manager class. All objects of this class - should be terminated manually using the drop() function when they - are no longer in use. -*/ -class Sound -{ - public: - /** - @brief Create an instance of this sound - - See also the capability flags in the Manager class. - - @param is3d true if this the sound is to be 3d enabled - @param repeat true if the sound should loop - @return new Instance object - */ - virtual Instance *getInstance(bool is3d, bool repeat) = 0; - - // Some prefab functions - - /// Shortcut for creating 3D instances - Instance *get3D(bool loop=false) - { return getInstance(true, loop); } - /// Shortcut for creating 2D instances - Instance *get2D(bool loop=false) - { return getInstance(false, loop); } - - /// Kill the current object - virtual void drop() = 0; - /// Virtual destructor virtual ~Sound() {} }; -/// Abstract interface for the main sound manager class -/** The sound manager is used to load sound files and is a factory for - Sound objects. It is the main entry point to a given sound system - implementation. +/// Factory interface for creating Sound objects +/** The SoundFactory is the main entry point to a given sound output + system. It is used to create Sound objects, which may be connected + to a sound file or stream, and which may be individually played, + paused, and so on. The class also contains a set of public bools which describe the capabilities the particular system. These should be set by implementations (base classes) in their respective constructors. */ -class Manager +class SoundFactory { public: + /// Virtual destructor + virtual ~SoundFactory() {} + /** @brief If set to true, you should call update() regularly (every frame or so) on this sound manager. If false, update() should not be called. @@ -111,23 +77,21 @@ class Manager */ bool canRepeatStream; - /// true if we can load sounds directly from file + /// true if we can load sounds directly from file (containing encoded data) bool canLoadFile; - /// true if we can load sounds from an InputSource - bool canLoadSource; - - /// If true, we can lound sound files from a Stream + /// If true, we can lound sound files from a Stream (containing encoded data) bool canLoadStream; + /// true if we can load sounds from a SampleSource (containing raw data) + bool canLoadSource; + /** - @brief Load a sound from an input source. Only valid if + @brief Load a sound from a sample source. Only valid if canLoadSource is true. This function loads a sound from a given stream as defined by - InputSource and InputStream. The InputSource and all streams - created from it will be dropped when drop() is called on the - owning sound / instance. + SampleSource. @param input the input source @param stream true if the file should be streamed. @@ -135,10 +99,10 @@ class Manager large files, but they are not required to. @return a new Sound object */ - virtual Sound *load(InputSource *input, bool stream=false) = 0; + virtual Sound *load(SampleSource *input, bool stream=false) = 0; /** - @brief Load a sound directly from file. Only valid if canLoadStream + @brief Load a sound file from stream. Only valid if canLoadStream is true. @param input audio file stream @@ -159,10 +123,10 @@ class Manager /// Call this every frame if needsUpdate is true /** - Update function that should be called regularly (about every - frame in a normal game setting.) Implementions may use this to - fill streaming buffers and similar. Implementations that do not - need this should set needsUpdate to false. + This should be called regularly (about every frame in a normal + game setting.) Implementions may use this for filling streaming + buffers and similar tasks. Implementations that do not need this + should set needsUpdate to false. */ virtual void update() = 0; @@ -177,9 +141,6 @@ class Manager virtual void setListenerPos(float x, float y, float z, float fx, float fy, float fz, float ux, float uy, float uz) = 0; - - /// Virtual destructor - virtual ~Manager() {} }; }} // Namespaces diff --git a/sound/servers/.gitignore b/sound/servers/.gitignore deleted file mode 100644 index 8b13789179..0000000000 --- a/sound/servers/.gitignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/sound/servers/audiere_imp.cpp b/sound/servers/audiere_imp.cpp deleted file mode 100644 index ecdb0580ad..0000000000 --- a/sound/servers/audiere_imp.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "audiere_imp.h" - -// Exception handling -class Audiere_Exception : public std::exception -{ - std::string msg; - - public: - - Audiere_Exception(const std::string &m) : msg(m) {} - ~Audiere_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - -static void fail(const std::string &msg) -{ - throw Audiere_Exception("Audiere exception: " + msg); -} - -using namespace audiere; -using namespace Mangle::Sound; - -AudiereManager::AudiereManager() -{ - needsUpdate = false; - has3D = false; - canRepeatStream = true; - canLoadFile = true; - canLoadSource = false; - canLoadStream = false; - - device = OpenDevice(""); - - if(device == NULL) - fail("Failed to open device"); -} - -// --- Manager --- - -Sound *AudiereManager::load(const std::string &file, bool stream) -{ return new AudiereSound(file, device, stream); } - - -// --- Sound --- - -AudiereSound::AudiereSound(const std::string &file, - AudioDevicePtr _device, - bool _stream) - : device(_device), stream(_stream) -{ - sample = OpenSampleSource(file.c_str()); - if(!sample) - fail("Couldn't load file " + file); - - buf = CreateSampleBuffer(sample); -} - -Instance *AudiereSound::getInstance(bool is3d, bool repeat) -{ - // Ignore is3d. Audiere doesn't implement 3d sound. We could make a - // hack software 3D implementation later, but it's not that - // important. - - SampleSourcePtr sample = buf->openStream(); - if(!sample) - fail("Failed to open sample stream"); - - OutputStreamPtr sound = OpenSound(device, sample, stream); - - if(repeat) - sound->setRepeat(true); - - return new AudiereInstance(sound); -} - - -// --- Instance --- - -AudiereInstance::AudiereInstance(OutputStreamPtr _sound) - : sound(_sound) {} - -void AudiereInstance::play() -{ sound->play(); } - -void AudiereInstance::stop() -{ sound->stop(); } - -void AudiereInstance::pause() -{ stop(); } - -bool AudiereInstance::isPlaying() -{ return sound->isPlaying(); } - -void AudiereInstance::setVolume(float vol) -{ sound->setVolume(vol); } diff --git a/sound/servers/audiere_imp.h b/sound/servers/audiere_imp.h deleted file mode 100644 index 5ebb812bdb..0000000000 --- a/sound/servers/audiere_imp.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef MANGLE_SOUND_AUDIERE_H -#define MANGLE_SOUND_AUDIERE_H - -#include "../sound.h" - -#include -#include - -namespace Mangle { -namespace Sound { - -/// Implementation of Sound::Manager for Audiere -class AudiereManager : public Manager -{ - audiere::AudioDevicePtr device; - - public: - AudiereManager(); - - virtual Sound *load(const std::string &file, bool stream=false); - - /// not implemented yet - virtual Sound *load(Stream::Stream *input, bool stream=false) - { assert(0); } - - /// disabled - virtual Sound *load(InputSource *input, bool stream=false) - { assert(0); } - /// disabled - virtual void update() { assert(0); } - /// disabled - virtual void setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) - { assert(0); }; -}; - -/// Audiere Sound implementation -class AudiereSound : public Sound -{ - audiere::AudioDevicePtr device; - audiere::SampleSourcePtr sample; - audiere::SampleBufferPtr buf; - - bool stream; - - public: - virtual Instance *getInstance(bool is3d, bool repeat); - virtual void drop() - { delete this; } - - AudiereSound(const std::string &file, audiere::AudioDevicePtr device, - bool stream); -}; - -/// Audiere Instance implementation -class AudiereInstance : public Instance -{ - audiere::OutputStreamPtr sound; - - public: - virtual void play(); - virtual void stop(); - virtual void pause(); - virtual bool isPlaying(); - virtual void setVolume(float); - /// disabled - virtual void setPos(float x, float y, float z) - { assert(0); } - virtual void drop() - { delete this; } - - AudiereInstance(audiere::OutputStreamPtr); -}; - -}} // Namespace -#endif diff --git a/sound/servers/input_audiere.cpp b/sound/servers/input_audiere.cpp deleted file mode 100644 index c48f45013b..0000000000 --- a/sound/servers/input_audiere.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "input_audiere.h" -#include - -#include "../../stream/clients/audiere_file.h" - -// Exception handling -class Audiere_Exception : public std::exception -{ - std::string msg; - - public: - - Audiere_Exception(const std::string &m) : msg(m) {} - ~Audiere_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - -static void fail(const std::string &msg) -{ - throw Audiere_Exception("Audiere exception: " + msg); -} - -using namespace audiere; -using namespace Mangle::Sound; - -// --- InputManager --- - -AudiereInput::AudiereInput() -{ - canLoadStream = true; -} - -InputSource *AudiereInput::load(const std::string &file) -{ return new AudiereSource(file); } - -InputSource *AudiereInput::load(Stream::Stream *input) -{ return new AudiereSource(input); } - -// --- InputSource --- - -AudiereSource::AudiereSource(const std::string &file) -{ - SampleSourcePtr sample = OpenSampleSource(file.c_str()); - if(!sample) - fail("Couldn't load file " + file); - - buf = CreateSampleBuffer(sample); -} - -AudiereSource::AudiereSource(Stream::Stream *input) -{ - SampleSourcePtr sample = OpenSampleSource - (new Stream::AudiereFile(input)); - if(!sample) - fail("Couldn't load stream"); - - buf = CreateSampleBuffer(sample); -} - -InputStream *AudiereSource::getStream() -{ - return new AudiereStream(buf->openStream()); -} - -// --- InputStream --- - -AudiereStream::AudiereStream(SampleSourcePtr _sample) - : sample(_sample), pullSize(0) -{ - assert(sample); - - SampleFormat fmt; - int channels, rate; - sample->getFormat(channels, rate, fmt); - - // Calculate the size of one frame - frameSize = GetSampleSize(fmt) * channels; - - // Make sure that our pullover hack will work. Increase this size if - // this doesn't work in all cases. - assert(frameSize <= PSIZE); -} - -void AudiereStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) -{ - SampleFormat fmt; - sample->getFormat(*channels, *rate, fmt); - if(fmt == SF_U8) - *bits = 8; - else if(fmt == SF_S16) - *bits = 16; - else assert(0); -} - -/* - Get data. Since Audiere operates with frames, not bytes, there's a - little conversion magic going on here. We need to make sure we're - reading a whole number of frames - if not, we need to store the - remainding part of the last frame and remember it for the next read - operation. - - */ -uint32_t AudiereStream::getData(void *_data, uint32_t length) -{ - char *data = (char*)_data; - - // Move the remains from the last operation first - if(pullSize) - { - // pullSize is how much was stored the last time, so skip that. - memcpy(data, pullOver+pullSize, PSIZE-pullSize); - length -= pullSize; - data += pullSize; - } - - // Determine the overshoot up front - pullSize = length % frameSize; - - // Number of whole frames - int frames = length / frameSize; - - // Read the data - int res = sample->read(frames, data); - - // Are we missing data? If resread(1, pullOver) != 0)) - { - // Now, move as much of it as we can fit into the output - // data - memcpy(data+length-pullSize, pullOver, pullSize); - } - else pullSize = 0; - - // Return the total number of bytes stored - return frameSize*res + pullSize; -} diff --git a/sound/servers/input_audiere.h b/sound/servers/input_audiere.h deleted file mode 100644 index e753b0174c..0000000000 --- a/sound/servers/input_audiere.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef MANGLE_SOUND_AUDIERE_INPUT_H -#define MANGLE_SOUND_AUDIERE_INPUT_H - -#include "../input.h" - -#include - -namespace Mangle { -namespace Sound { - -/// Implementation of Sound::InputManager for Audiere -class AudiereInput : public InputManager -{ - public: - AudiereInput(); - - /// Load a source from a file - InputSource *load(const std::string &file); - - /// Load a source from a stream - virtual InputSource *load(Stream::Stream *input); -}; - -/// Audiere InputSource implementation -class AudiereSource : public InputSource -{ - audiere::SampleBufferPtr buf; - - public: - AudiereSource(const std::string &file); - AudiereSource(Stream::Stream *input); - InputStream *getStream(); - void drop() { delete this; } -}; - -/// Audiere InputStream implementation -class AudiereStream : public InputStream -{ - audiere::SampleSourcePtr sample; - int frameSize; // Size of one frame, in bytes - - static const int PSIZE = 10; - - // Temporary storage for unevenly read samples. See the comment for - // getData() in the .cpp file. - char pullOver[PSIZE]; - // How much of the above buffer is in use - int pullSize; - - public: - AudiereStream(audiere::SampleSourcePtr _sample); - - void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); - uint32_t getData(void *data, uint32_t length); - void drop() { delete this; } -}; - -}} // Namespace -#endif diff --git a/sound/servers/input_ffmpeg.cpp b/sound/servers/input_ffmpeg.cpp deleted file mode 100644 index f29ddfdb64..0000000000 --- a/sound/servers/input_ffmpeg.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include "input_ffmpeg.h" -#include - -using namespace Mangle::Sound; - -// Static output buffer. Not thread safe, but supports multiple -// streams operated from the same thread. -static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; -bool FFM_InputManager::init = false; - -FFM_Exception::FFM_Exception(const std::string &m) - : msg(m) {} - -const char* FFM_Exception::what() const throw() -{ return msg.c_str(); } - -FFM_Exception::~FFM_Exception() throw() {} - -static void fail(const std::string &msg) -{ - throw FFM_Exception("FFMpeg exception: " + msg); -} - - -// --- Manager --- - -FFM_InputManager::FFM_InputManager() -{ - if(!init) - { - av_register_all(); - av_log_set_level(AV_LOG_ERROR); - init = true; - } - - canLoadStream = false; -} - -InputSource *FFM_InputManager::load(const std::string &file) -{ return new FFM_InputSource(file); } - - -// --- Source --- - -FFM_InputSource::FFM_InputSource(const std::string &file) -{ - // FFmpeg doesn't handle several instances from one source. So we - // just store the filename. - name = file; -} - -InputStream *FFM_InputSource::getStream() -{ return new FFM_InputStream(name); } - -void FFM_InputSource::drop() -{ delete this; } - - -// --- Stream --- - -FFM_InputStream::FFM_InputStream(const std::string &file) -{ - std::string msg; - AVCodec *codec; - - empty = false; - - if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) - fail("Error loading audio file " + file); - - if(av_find_stream_info(FmtCtx) < 0) - { - msg = "Error in file stream " + file; - goto err; - } - - // Pick the first audio stream, if any - for(StreamNum = 0; StreamNum < FmtCtx->nb_streams; StreamNum++) - { - // Pick the first audio stream - if(FmtCtx->streams[StreamNum]->codec->codec_type == CODEC_TYPE_AUDIO) - break; - } - - if(StreamNum == FmtCtx->nb_streams) - fail("File " + file + " didn't contain any audio streams"); - - // Open the decoder - CodecCtx = FmtCtx->streams[StreamNum]->codec; - codec = avcodec_find_decoder(CodecCtx->codec_id); - - if(!codec || avcodec_open(CodecCtx, codec) < 0) - { - msg = "Error loading " + file + ": "; - if(codec) - msg += "coded error"; - else - msg += "no codec found"; - goto err; - } - - // No errors, we're done - return; - - // Handle errors - err: - av_close_input_file(FmtCtx); - fail(msg); -} - -FFM_InputStream::~FFM_InputStream() -{ - avcodec_close(CodecCtx); - av_close_input_file(FmtCtx); -} - -void FFM_InputStream::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) -{ - if(rate) *rate = CodecCtx->sample_rate; - if(channels) *channels = CodecCtx->channels; - if(bits) *bits = 16; -} - -uint32_t FFM_InputStream::getData(void *data, uint32_t length) -{ - if(empty) return 0; - - uint32_t left = length; - uint8_t *outPtr = (uint8_t*)data; - - // First, copy over any stored data we might be sitting on - { - int s = storage.size(); - int copy = s; - if(s) - { - // Make sure there's room - if(copy > left) - copy = left; - - // Copy - memcpy(outPtr, &storage[0], copy); - outPtr += copy; - left -= copy; - - // Is there anything left in the storage? - s -= copy; - if(s) - { - assert(left == 0); - - // Move it to the start and resize - memmove(&storage[0], &storage[copy], s); - storage.resize(s); - } - } - } - - // Next, get more input data from stream, and decode it - while(left) - { - AVPacket packet; - - // Get the next packet, if any - if(av_read_frame(FmtCtx, &packet) < 0) - break; - - // We only allow one stream per file at the moment - assert(StreamNum == packet.stream_index); - - // Decode the packet - int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; - int tmp = avcodec_decode_audio2(CodecCtx, (int16_t*)outBuf, - &len, packet.data, packet.size); - assert(tmp < 0 || tmp == packet.size); - - // We don't need the input packet any longer - av_free_packet(&packet); - - if(tmp < 0) - fail("Error decoding audio stream"); - - // Copy whatever data we got, and advance the pointer - if(len > 0) - { - // copy = how many bytes do we copy now - int copy = len; - if(copy > left) - copy = left; - - // len = how many bytes are left uncopied - len -= copy; - - // copy data - memcpy(outPtr, outBuf, copy); - - // left = how much space is left in the caller output - // buffer - left -= copy; - outPtr += copy; - assert(left >= 0); - - if(len > 0) - { - // There were uncopied bytes. Store them for later. - assert(left == 0); - storage.resize(len); - memcpy(&storage[0], outBuf, len); - } - } - } - - // End of loop. Return the number of bytes copied. - assert(left <= length); - - // If we're returning less than asked for, then we're done - if(left > 0) - empty = true; - - return length - left; -} - -void FFM_InputStream::drop() -{ delete this; } diff --git a/sound/servers/input_ffmpeg.h b/sound/servers/input_ffmpeg.h deleted file mode 100644 index b744a75856..0000000000 --- a/sound/servers/input_ffmpeg.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef MANGLE_SOUND_FFMPEG_H -#define MANGLE_SOUND_FFMPEG_H - -#include "../input.h" -#include -#include -#include - -extern "C" -{ -#include -#include -} - -namespace Mangle { -namespace Sound { - -/// FFmpeg exception -class FFM_Exception : public std::exception -{ - std::string msg; - - public: - - FFM_Exception(const std::string &m); - ~FFM_Exception() throw(); - virtual const char* what() const throw(); -}; - -/// FFMpeg implementation of InputManager -class FFM_InputManager : public InputManager -{ - static bool init; - - public: - FFM_InputManager(); - virtual InputSource *load(const std::string &file); - - /// not supported - virtual InputSource *load(Stream::Stream *input) { assert(0); } -}; - -/// FFMpeg implementation of InputSource -class FFM_InputSource : public InputSource -{ - std::string name; - - public: - FFM_InputSource(const std::string &file); - - virtual InputStream *getStream(); - virtual void drop(); -}; - -/// FFMpeg implementation of InputStream -class FFM_InputStream : public InputStream -{ - AVFormatContext *FmtCtx; - AVCodecContext *CodecCtx; - int StreamNum; - bool empty; - - std::vector storage; - - public: - FFM_InputStream(const std::string &file); - ~FFM_InputStream(); - - virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); - virtual uint32_t getData(void *data, uint32_t length); - virtual void drop(); -}; - -}} // namespaces -#endif diff --git a/sound/servers/input_filter.h b/sound/servers/input_filter.h deleted file mode 100644 index 455a60e14f..0000000000 --- a/sound/servers/input_filter.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef MANGLE_INPUT_FILTER_H -#define MANGLE_INPUT_FILTER_H - -#include "../sound.h" - -#include - -namespace Mangle { -namespace Sound { - -/** - @brief This filter class adds file loading capabilities to a - Sound::Manager class, by associating an InputManager with it. - - The class takes an existing Manager able to load streams, and - associates an InputManager with it. The combined class is able to - load files directly. - - Example: - \code - - // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot - // decode sound files on its own. - InputFilter mg(new OpenAL_Manager, new FFM_InputManager); - - // We can now load filenames directly. - mg.load("file1.mp3"); - \endcode -*/ -class InputFilter : public Manager -{ - protected: - Manager *snd; - InputManager *inp; - - public: - /// Empty constructor - InputFilter() {} - - /// Assign an input manager and a sound manager to this object - InputFilter(Manager *_snd, InputManager *_inp) - { set(_snd, _inp); } - - /// Assign an input manager and a sound manager to this object - void set(Manager *_snd, InputManager *_inp) - { - inp = _inp; - snd = _snd; - - // Set capabilities - needsUpdate = snd->needsUpdate; - has3D = snd->has3D; - canRepeatStream = snd->canRepeatStream; - canLoadStream = inp->canLoadStream; - - // Both these should be true, or the use of this class is pretty - // pointless - canLoadSource = snd->canLoadSource; - canLoadFile = canLoadSource; - assert(canLoadSource && canLoadFile); - } - - virtual Sound *load(const std::string &file, bool stream=false) - { return load(inp->load(file), stream); } - - virtual Sound *load(Stream::Stream *input, bool stream=false) - { return load(inp->load(input), stream); } - - virtual Sound *load(InputSource *input, bool stream=false) - { return snd->load(input, stream); } - - virtual void update() { snd->update(); } - virtual void setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) - { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } -}; - -}} -#endif diff --git a/sound/servers/openal_audiere.h b/sound/servers/openal_audiere.h deleted file mode 100644 index 65947b22fc..0000000000 --- a/sound/servers/openal_audiere.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MANGLE_FFMPEG_OPENAL_H -#define MANGLE_FFMPEG_OPENAL_H - -#include "input_filter.h" -#include "input_audiere.h" -#include "output_openal.h" - -namespace Mangle { -namespace Sound { - -/// A InputFilter that adds audiere decoding to OpenAL. Audiere has -/// it's own output, but OpenAL sports 3D and other advanced features. -class OpenAL_Audiere_Manager : public InputFilter -{ - public: - OpenAL_Audiere_Manager() - { - set(new OpenAL_Manager, - new AudiereInput); - } - ~OpenAL_Audiere_Manager() - { - delete snd; - delete inp; - } -}; - -}} -#endif diff --git a/sound/servers/openal_ffmpeg.h b/sound/servers/openal_ffmpeg.h deleted file mode 100644 index 179d3cb702..0000000000 --- a/sound/servers/openal_ffmpeg.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MANGLE_FFMPEG_OPENAL_H -#define MANGLE_FFMPEG_OPENAL_H - -#include "input_filter.h" -#include "input_ffmpeg.h" -#include "output_openal.h" - -namespace Mangle { -namespace Sound { - -/// An InputFilter that adds FFmpeg decoding to OpenAL -class OpenAL_FFM_Manager : public InputFilter -{ - public: - OpenAL_FFM_Manager() - { - set(new OpenAL_Manager, - new FFM_InputManager); - } - ~OpenAL_FFM_Manager() - { - delete snd; - delete inp; - } -}; - -}} -#endif diff --git a/sound/servers/output_openal.cpp b/sound/servers/output_openal.cpp deleted file mode 100644 index 06a8edca80..0000000000 --- a/sound/servers/output_openal.cpp +++ /dev/null @@ -1,353 +0,0 @@ -#include "output_openal.h" -#include - -#include - -using namespace Mangle::Sound; - - -// ---- Helper functions and classes ---- - -class OpenAL_Exception : public std::exception -{ - std::string msg; - - public: - - OpenAL_Exception(const std::string &m) : msg(m) {} - ~OpenAL_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - -static void fail(const std::string &msg) -{ - throw OpenAL_Exception("OpenAL exception: " + msg); -} - -static void checkALError(const std::string &msg) -{ - ALenum err = alGetError(); - if(err != AL_NO_ERROR) - fail("\"" + std::string(alGetString(err)) + "\" while " + msg); -} - -static void getALFormat(InputStream *inp, int &fmt, int &rate) -{ - int ch, bits; - inp->getInfo(&rate, &ch, &bits); - - fmt = 0; - - if(bits == 8) - { - if(ch == 1) fmt = AL_FORMAT_MONO8; - if(ch == 2) fmt = AL_FORMAT_STEREO8; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); - if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); - } - } - if(bits == 16) - { - if(ch == 1) fmt = AL_FORMAT_MONO16; - if(ch == 2) fmt = AL_FORMAT_STEREO16; - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); - if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); - } - } - - if(fmt == 0) - fail("Unsupported input format"); -} - - -// ---- Manager ---- - -OpenAL_Manager::OpenAL_Manager() - : Context(NULL), Device(NULL) -{ - needsUpdate = true; - has3D = true; - canRepeatStream = false; - canLoadFile = false; - canLoadSource = true; - canLoadStream = false; - - // Set up sound system - Device = alcOpenDevice(NULL); - Context = alcCreateContext(Device, NULL); - - if(!Device || !Context) - fail("Failed to initialize context or device"); - - alcMakeContextCurrent(Context); -} - -OpenAL_Manager::~OpenAL_Manager() -{ - // Deinitialize sound system - alcMakeContextCurrent(NULL); - if(Context) alcDestroyContext(Context); - if(Device) alcCloseDevice(Device); -} - -Sound *OpenAL_Manager::load(const std::string &file, bool stream) -{ assert(0 && "OpenAL cannot decode files"); } - -Sound *OpenAL_Manager::load(Stream::Stream*,bool) -{ assert(0 && "OpenAL cannot decode streams"); } - -Sound *OpenAL_Manager::load(InputSource *source, bool stream) -{ return new OpenAL_Sound(source, this, stream); } - -void OpenAL_Manager::update() -{ - // Loop through all the streaming sounds and update them - LST::iterator it, next; - for(it = streaming.begin(); - it != streaming.end(); - it++) - { - (*it)->update(); - } -} - -void OpenAL_Manager::setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) -{ - ALfloat orient[6]; - orient[0] = fx; - orient[1] = fy; - orient[2] = fz; - orient[3] = ux; - orient[4] = uy; - orient[5] = uz; - alListener3f(AL_POSITION, x, y, z); - alListenerfv(AL_ORIENTATION, orient); - checkALError("setting listener position"); -} - -OpenAL_Manager::LST::iterator OpenAL_Manager::add_stream(OpenAL_Stream_Instance* inst) -{ - streaming.push_front(inst); - return streaming.begin(); -} - -void OpenAL_Manager::remove_stream(LST::iterator it) -{ - streaming.erase(it); -} - - -// ---- Sound ---- - -OpenAL_Sound::~OpenAL_Sound() -{ - // Kill the input source - if(source) source->drop(); - - // And any allocated buffers - if(bufferID) - alDeleteBuffers(1, &bufferID); -} - -Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) -{ - assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); - - if(stream) - return new OpenAL_Stream_Instance(source->getStream(), owner); - - // Load the buffer if it hasn't been done already - if(bufferID == 0) - { - // Get an input stream and load the file from it - InputStream *inp = source->getStream(); - - std::vector buffer; - - // Add 32 kb at each increment - const int ADD = 32*1024; - - // Fill the buffer. We increase the buffer until it's large - // enough to fit all the data. - while(true) - { - // Increase the buffer - int oldlen = buffer.size(); - buffer.resize(oldlen+ADD); - - // Read the data - size_t len = inp->getData(&buffer[oldlen], ADD); - - // If we read less than requested, we're done. - if(len < ADD) - { - // Downsize the buffer to the right size - buffer.resize(oldlen+len); - break; - } - } - - // Get the format - int fmt, rate; - getALFormat(inp, fmt, rate); - - // We don't need the file anymore - inp->drop(); - source->drop(); - source = NULL; - - // Move the data into OpenAL - alGenBuffers(1, &bufferID); - alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); - checkALError("loading sound buffer"); - } // End of buffer loading - - // At this point, the file data has been loaded into the buffer - // in 'bufferID', and we should be ready to go. - assert(bufferID != 0); - - return new OpenAL_Simple_Instance(bufferID); -} - - -// ---- OpenAL_Instance_Base ---- - -void OpenAL_Instance_Base::play() -{ - alSourcePlay(inst); - checkALError("starting playback"); -} - -void OpenAL_Instance_Base::stop() -{ - alSourceStop(inst); - checkALError("stopping"); -} - -void OpenAL_Instance_Base::pause() -{ - alSourcePause(inst); - checkALError("pausing"); -} - -bool OpenAL_Instance_Base::isPlaying() -{ - ALint state; - alGetSourcei(inst, AL_SOURCE_STATE, &state); - - return state == AL_PLAYING; -} - -void OpenAL_Instance_Base::setVolume(float volume) -{ - if(volume > 1.0) volume = 1.0; - if(volume < 0.0) volume = 0.0; - alSourcef(inst, AL_GAIN, volume); - checkALError("setting volume"); -} - -void OpenAL_Instance_Base::setPos(float x, float y, float z) -{ - alSource3f(inst, AL_POSITION, x, y, z); - checkALError("setting position"); -} - - -// ---- OpenAL_Simple_Instance ---- - -OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) -{ - // Create instance and associate buffer - alGenSources(1, &inst); - alSourcei(inst, AL_BUFFER, buf); -} - -OpenAL_Simple_Instance::~OpenAL_Simple_Instance() -{ - // Stop - alSourceStop(inst); - - // Return sound - alDeleteSources(1, &inst); -} - - -// ---- OpenAL_Stream_Instance ---- - -OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, - OpenAL_Manager *_owner) - : stream(_stream), owner(_owner) -{ - // Deduce the file format from the stream info - getALFormat(stream, fmt, rate); - - // Create the buffers and the sound instance - alGenBuffers(BUFS, bufs); - alGenSources(1, &inst); - - checkALError("initializing"); - - // Fill the buffers and que them - for(int i=0; iadd_stream(this); -} - -void OpenAL_Stream_Instance::queueBuffer(ALuint bId) -{ - char buf[SIZE]; - - // Get the data - int len = stream->getData(buf, SIZE); - if(len == 0) - return; - - // .. and stash it - alBufferData(bId, fmt, buf, len, rate); - alSourceQueueBuffers(inst, 1, &bId); -} - -OpenAL_Stream_Instance::~OpenAL_Stream_Instance() -{ - // Remove ourselves from streaming list - owner->remove_stream(lit); - - // Stop - alSourceStop(inst); - - // Kill the input stream - stream->drop(); - - // Return sound - alDeleteSources(1, &inst); - - // Delete buffers - alDeleteBuffers(BUFS, bufs); -} - -void OpenAL_Stream_Instance::update() -{ - ALint count; - alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); - - for(int i = 0;i < count;i++) - { - // Unque a finished buffer - ALuint bId; - alSourceUnqueueBuffers(inst, 1, &bId); - - // Queue a new buffer - queueBuffer(bId); - } -} diff --git a/sound/servers/output_openal.h b/sound/servers/output_openal.h deleted file mode 100644 index 53226c32f3..0000000000 --- a/sound/servers/output_openal.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef MANGLE_SOUND_OPENAL_H -#define MANGLE_SOUND_OPENAL_H - -#include "../sound.h" - -#include -#include -#include - -namespace Mangle { -namespace Sound { - -class OpenAL_Stream_Instance; - -/// OpenAL implementation of Manager -class OpenAL_Manager : public Manager -{ -public: - // List of all streaming sounds - these need to be updated regularly - typedef std::list LST; - - OpenAL_Manager(); - virtual ~OpenAL_Manager(); - - LST::iterator add_stream(OpenAL_Stream_Instance*); - void remove_stream(LST::iterator); - - virtual Sound *load(const std::string &file, bool stream=false); - virtual Sound *load(Stream::Stream *input, bool stream=false); - virtual Sound *load(InputSource* input, bool stream=false); - virtual void update(); - virtual void setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz); - - private: - ALCdevice *Device; - ALCcontext *Context; - - LST streaming; -}; - -/// OpenAL implementation of Sound -class OpenAL_Sound : public Sound -{ - InputSource *source; - OpenAL_Manager *owner; - bool stream; - - // Used for non-streaming files, contains the entire sound buffer if - // non-zero - ALuint bufferID; - - public: - OpenAL_Sound(InputSource *src, OpenAL_Manager *own, bool str) - : source(src), owner(own), stream(str), bufferID(0) {} - ~OpenAL_Sound(); - - virtual Instance *getInstance(bool is3d, bool repeat); - void drop() { delete this; } -}; - -/// Shared parent class that holds an OpenAL sound instance. Just used -/// for shared functionality, has no setup or cleanup code. -class OpenAL_Instance_Base : public Instance -{ - protected: - ALuint inst; - - public: - void drop() { delete this; } - virtual void play(); - virtual void stop(); - virtual void pause(); - virtual bool isPlaying(); - virtual void setVolume(float); - virtual void setPos(float x, float y, float z); -}; - -/// Non-streaming OpenAL-implementation of Instance. Uses a shared -/// sound buffer in OpenAL_Sound. -class OpenAL_Simple_Instance : public OpenAL_Instance_Base -{ - public: - OpenAL_Simple_Instance(ALuint buf); - ~OpenAL_Simple_Instance(); -}; - -/// Streaming OpenAL-implementation of Instance. -class OpenAL_Stream_Instance : public OpenAL_Instance_Base -{ - // Since OpenAL streams have to be updated manually each frame, we - // need to have a sufficiently large buffer so that we don't run out - // of data in the mean time. Each instance will take around 512 Kb - // of memory, independent of how large the file is. - static const int BUFS = 4; - static const int SIZE = 128*1024; - - // Buffers - ALuint bufs[BUFS]; - - // Sound format settings - int rate, fmt; - - // Source of data - InputStream *stream; - - OpenAL_Manager *owner; - - // List iterator, used for removing ourselves from the streaming - // list when we're deleted. - OpenAL_Manager::LST::iterator lit; - - // Load and queue a new buffer - void queueBuffer(ALuint buffer); - -public: - OpenAL_Stream_Instance(InputStream*, OpenAL_Manager*); - ~OpenAL_Stream_Instance(); - - void update(); -}; - -}} // namespaces -#endif From c8256e3bb31f161937b47cadbcffe9b8f3dbf016 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 15:54:05 +0100 Subject: [PATCH 021/269] Worked on outputs, OpenAL. VERY wip --- sound/outputs/input_filter.h | 80 ++++++++++ sound/outputs/openal_audiere.h | 29 ++++ sound/outputs/openal_out.cpp | 283 +++++++++++++++++++++++++++++++++ sound/outputs/openal_out.h | 109 +++++++++++++ 4 files changed, 501 insertions(+) create mode 100644 sound/outputs/input_filter.h create mode 100644 sound/outputs/openal_audiere.h create mode 100644 sound/outputs/openal_out.cpp create mode 100644 sound/outputs/openal_out.h diff --git a/sound/outputs/input_filter.h b/sound/outputs/input_filter.h new file mode 100644 index 0000000000..455a60e14f --- /dev/null +++ b/sound/outputs/input_filter.h @@ -0,0 +1,80 @@ +#ifndef MANGLE_INPUT_FILTER_H +#define MANGLE_INPUT_FILTER_H + +#include "../sound.h" + +#include + +namespace Mangle { +namespace Sound { + +/** + @brief This filter class adds file loading capabilities to a + Sound::Manager class, by associating an InputManager with it. + + The class takes an existing Manager able to load streams, and + associates an InputManager with it. The combined class is able to + load files directly. + + Example: + \code + + // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot + // decode sound files on its own. + InputFilter mg(new OpenAL_Manager, new FFM_InputManager); + + // We can now load filenames directly. + mg.load("file1.mp3"); + \endcode +*/ +class InputFilter : public Manager +{ + protected: + Manager *snd; + InputManager *inp; + + public: + /// Empty constructor + InputFilter() {} + + /// Assign an input manager and a sound manager to this object + InputFilter(Manager *_snd, InputManager *_inp) + { set(_snd, _inp); } + + /// Assign an input manager and a sound manager to this object + void set(Manager *_snd, InputManager *_inp) + { + inp = _inp; + snd = _snd; + + // Set capabilities + needsUpdate = snd->needsUpdate; + has3D = snd->has3D; + canRepeatStream = snd->canRepeatStream; + canLoadStream = inp->canLoadStream; + + // Both these should be true, or the use of this class is pretty + // pointless + canLoadSource = snd->canLoadSource; + canLoadFile = canLoadSource; + assert(canLoadSource && canLoadFile); + } + + virtual Sound *load(const std::string &file, bool stream=false) + { return load(inp->load(file), stream); } + + virtual Sound *load(Stream::Stream *input, bool stream=false) + { return load(inp->load(input), stream); } + + virtual Sound *load(InputSource *input, bool stream=false) + { return snd->load(input, stream); } + + virtual void update() { snd->update(); } + virtual void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { snd->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); } +}; + +}} +#endif diff --git a/sound/outputs/openal_audiere.h b/sound/outputs/openal_audiere.h new file mode 100644 index 0000000000..65947b22fc --- /dev/null +++ b/sound/outputs/openal_audiere.h @@ -0,0 +1,29 @@ +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H + +#include "input_filter.h" +#include "input_audiere.h" +#include "output_openal.h" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds audiere decoding to OpenAL. Audiere has +/// it's own output, but OpenAL sports 3D and other advanced features. +class OpenAL_Audiere_Manager : public InputFilter +{ + public: + OpenAL_Audiere_Manager() + { + set(new OpenAL_Manager, + new AudiereInput); + } + ~OpenAL_Audiere_Manager() + { + delete snd; + delete inp; + } +}; + +}} +#endif diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp new file mode 100644 index 0000000000..5d3633e753 --- /dev/null +++ b/sound/outputs/openal_out.cpp @@ -0,0 +1,283 @@ +#include "openal_out.h" +#include + +// ALuint bufferID; + +using namespace Mangle::Sound; + +// ---- Helper functions and classes ---- + +class OpenAL_Exception : public std::exception +{ + std::string msg; + + public: + + OpenAL_Exception(const std::string &m) : msg(m) {} + ~OpenAL_Exception() throw() {} + virtual const char* what() const throw() { return msg.c_str(); } +}; + +static void fail(const std::string &msg) +{ + throw OpenAL_Exception("OpenAL exception: " + msg); +} + +static void checkALError(const std::string &msg) +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + fail("\"" + std::string(alGetString(err)) + "\" while " + msg); +} + +static void getALFormat(InputStream *inp, int &fmt, int &rate) +{ + int ch, bits; + inp->getInfo(&rate, &ch, &bits); + + fmt = 0; + + if(bits == 8) + { + if(ch == 1) fmt = AL_FORMAT_MONO8; + if(ch == 2) fmt = AL_FORMAT_STEREO8; + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN8"); + } + } + if(bits == 16) + { + if(ch == 1) fmt = AL_FORMAT_MONO16; + if(ch == 2) fmt = AL_FORMAT_STEREO16; + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); + if(ch == 6) fmt = alGetEnumValue("AL_FORMAT_51CHN16"); + } + } + + if(fmt == 0) + fail("Unsupported input format"); +} + +// ---- OpenAL_Sound ---- + +OpenAL_Sound::OpenAL_Sound(SampleSource *input) +{ + +} + +void OpenAL_Sound::play() +{ + alSourcePlay(inst); + checkALError("starting playback"); +} + +void OpenAL_Sound::stop() +{ + alSourceStop(inst); + checkALError("stopping"); +} + +void OpenAL_Sound::pause() +{ + alSourcePause(inst); + checkALError("pausing"); +} + +bool OpenAL_Sound::isPlaying() +{ + ALint state; + alGetSourcei(inst, AL_SOURCE_STATE, &state); + + return state == AL_PLAYING; +} + +void OpenAL_Sound::setVolume(float volume) +{ + if(volume > 1.0) volume = 1.0; + if(volume < 0.0) volume = 0.0; + alSourcef(inst, AL_GAIN, volume); + checkALError("setting volume"); +} + +void OpenAL_Sound::setPos(float x, float y, float z) +{ + alSource3f(inst, AL_POSITION, x, y, z); + checkALError("setting position"); +} + + + + + + + + + + + + + + + + + + + + +Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) +{ + assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); + + if(stream) + return new OpenAL_Stream_Instance(source->getStream(), owner); + + // Load the buffer if it hasn't been done already + if(bufferID == 0) + { + // Get an input stream and load the file from it + InputStream *inp = source->getStream(); + + std::vector buffer; + + // Add 32 kb at each increment + const int ADD = 32*1024; + + // Fill the buffer. We increase the buffer until it's large + // enough to fit all the data. + while(true) + { + // Increase the buffer + int oldlen = buffer.size(); + buffer.resize(oldlen+ADD); + + // Read the data + size_t len = inp->getData(&buffer[oldlen], ADD); + + // If we read less than requested, we're done. + if(len < ADD) + { + // Downsize the buffer to the right size + buffer.resize(oldlen+len); + break; + } + } + + // Get the format + int fmt, rate; + getALFormat(inp, fmt, rate); + + // We don't need the file anymore + inp->drop(); + source->drop(); + source = NULL; + + // Move the data into OpenAL + alGenBuffers(1, &bufferID); + alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); + checkALError("loading sound buffer"); + } // End of buffer loading + + // At this point, the file data has been loaded into the buffer + // in 'bufferID', and we should be ready to go. + assert(bufferID != 0); + + return new OpenAL_Simple_Instance(bufferID); +} + + +// ---- OpenAL_Simple_Instance ---- + +OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) +{ + // Create instance and associate buffer + alGenSources(1, &inst); + alSourcei(inst, AL_BUFFER, buf); +} + +OpenAL_Simple_Instance::~OpenAL_Simple_Instance() +{ + // Stop + alSourceStop(inst); + + // Return sound + alDeleteSources(1, &inst); +} + + +// ---- OpenAL_Stream_Instance ---- + +OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, + OpenAL_Manager *_owner) + : stream(_stream), owner(_owner) +{ + // Deduce the file format from the stream info + getALFormat(stream, fmt, rate); + + // Create the buffers and the sound instance + alGenBuffers(BUFS, bufs); + alGenSources(1, &inst); + + checkALError("initializing"); + + // Fill the buffers and que them + for(int i=0; iadd_stream(this); +} + +void OpenAL_Stream_Instance::queueBuffer(ALuint bId) +{ + char buf[SIZE]; + + // Get the data + int len = stream->getData(buf, SIZE); + if(len == 0) + return; + + // .. and stash it + alBufferData(bId, fmt, buf, len, rate); + alSourceQueueBuffers(inst, 1, &bId); +} + +OpenAL_Stream_Instance::~OpenAL_Stream_Instance() +{ + // Remove ourselves from streaming list + owner->remove_stream(lit); + + // Stop + alSourceStop(inst); + + // Kill the input stream + stream->drop(); + + // Return sound + alDeleteSources(1, &inst); + + // Delete buffers + alDeleteBuffers(BUFS, bufs); +} + +void OpenAL_Stream_Instance::update() +{ + ALint count; + alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); + + for(int i = 0;i < count;i++) + { + // Unque a finished buffer + ALuint bId; + alSourceUnqueueBuffers(inst, 1, &bId); + + // Queue a new buffer + queueBuffer(bId); + } +} diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h new file mode 100644 index 0000000000..95d27845ca --- /dev/null +++ b/sound/outputs/openal_out.h @@ -0,0 +1,109 @@ +#ifndef MANGLE_SOUND_OPENAL_OUT_H +#define MANGLE_SOUND_OPENAL_OUT_H + +#include "../output.h" + +#include +#include +#include + +namespace Mangle { +namespace Sound { + +/// OpenAL sound output +class OpenAL_Sound : public Sound +{ + protected: + ALuint inst; + + public: + OpenAL_Sound(SampleSource *input); + ~OpenAL_Sound(); + + /// Play or resume the sound + void play(); + + /// Stop the sound + void stop(); + + /// Pause the sound, may be resumed later + void pause(); + + /// Check if the sound is still playing + bool isPlaying(); + + /// Set the volume. The parameter must be between 0.0 and 1.0. + void setVolume(float); + + /// Set the 3D position. + void setPos(float x, float y, float z); +}; + +class OpenALFactory : public SoundFactory +{ + ALCdevice *Device; + ALCcontext *Context; + bool didSetup; + + public: + /// Initialize object. Pass true (default) if you want the + /// constructor to set up the current ALCdevice and ALCcontext for + /// you. + OpenALFactory(bool doSetup = true) + : didSetup(doSetup) + { + needsUpdate = false; + has3D = true; + canRepeatStream = false; + canLoadFile = false; + canLoadStream = false; + canLoadSource = true; + + if(doSetup) + { + // Set up sound system + Device = alcOpenDevice(NULL); + Context = alcCreateContext(Device, NULL); + + if(!Device || !Context) + fail("Failed to initialize context or device"); + + alcMakeContextCurrent(Context); + } + } + + ~OpenALFactory() + { + // Deinitialize sound system + if(didSetup) + { + alcMakeContextCurrent(NULL); + if(Context) alcDestroyContext(Context); + if(Device) alcCloseDevice(Device); + } + } + + Sound *load(const std::string &file, bool stream=false) { assert(0); } + Sound *load(Stream::Stream *input, bool stream=false) { assert(0); } + Sound *load(SampleSource* input, bool stream=false) + { return new OpenAL_Sound(input); } + + void update() {} + setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { + ALfloat orient[6]; + orient[0] = fx; + orient[1] = fy; + orient[2] = fz; + orient[3] = ux; + orient[4] = uy; + orient[5] = uz; + alListener3f(AL_POSITION, x, y, z); + alListenerfv(AL_ORIENTATION, orient); + } +}; + +}} // namespaces +#endif From 0c18c4db039e6c92f43049ec259dea87c84584bc Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:16:33 +0100 Subject: [PATCH 022/269] Added memory_stream.h, rewrote tests --- stream/servers/memory_stream.h | 60 +++++++++++++++++++ stream/tests/Makefile | 6 +- stream/tests/audiere_client_test.cpp | 5 +- stream/tests/dummy_input.cpp | 48 --------------- .../tests/{dummy_test.cpp => memory_test.cpp} | 7 ++- stream/tests/ogre_client_test.cpp | 5 +- 6 files changed, 73 insertions(+), 58 deletions(-) create mode 100644 stream/servers/memory_stream.h delete mode 100644 stream/tests/dummy_input.cpp rename stream/tests/{dummy_test.cpp => memory_test.cpp} (89%) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h new file mode 100644 index 0000000000..063b0fcf14 --- /dev/null +++ b/stream/servers/memory_stream.h @@ -0,0 +1,60 @@ +#ifndef MANGLE_STREAM_MEMSERVER_H +#define MANGLE_STREAM_MEMSERVER_H + +#include +#include "../stream.h" + +namespace Mangle { +namespace Stream { + +/** A Stream wrapping a memory buffer + + This will create a fully seekable stream out any pointer/length + pair you give it. + */ +class MemoryStream : public Stream +{ + const void *data; + size_t length, pos; + + public: + MemoryStream(const void *ptr, size_t len) + : data(ptr), length(len), pos(0) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void *buf, size_t count) + { + assert(pos <= length); + + // Don't read more than we have + if(count > (length - pos)) + count = length - pos; + + // Copy data + if(count) + memcpy(buf, ((char*)data)+pos, count); + + // aaand remember to increase the count + pos += count; + + return count; + } + + void seek(size_t _pos) + { + pos = _pos; + if(pos > length) + pos = length; + } + + size_t tell() const { return pos; } + size_t size() const { return length; } + bool eof() const { return pos == length; } +}; + +}} // namespaces +#endif diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 84ec228cf1..8006572bdb 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -6,13 +6,13 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp dummy_input.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp +audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -dummy_test: dummy_test.cpp dummy_input.cpp ../stream.h +memory_test: memory_test.cpp ../stream.h ../servers/memory_stream.h $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index f64de0649b..01f548714c 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -1,8 +1,9 @@ -#include "dummy_input.cpp" +#include "../servers/memory_stream.h" #include "../clients/audiere_file.h" #include #include +using namespace Mangle::Stream; using namespace audiere; using namespace std; @@ -10,7 +11,7 @@ int main() { char str[12]; memset(str, 0, 12); - Stream *inp = new DummyInput(); + Stream *inp = new MemoryStream("hello world", 11); FilePtr p(new AudiereFile(inp, true)); cout << "pos=" << p->tell() << endl; p->read(str, 2); diff --git a/stream/tests/dummy_input.cpp b/stream/tests/dummy_input.cpp deleted file mode 100644 index c93f421602..0000000000 --- a/stream/tests/dummy_input.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// This file is shared between several test programs -#include "../stream.h" -#include -#include - -using namespace Mangle::Stream; - -// A simple dummy stream -const char _data[12] = "hello world"; - -class DummyInput : public Stream -{ -private: - int pos; - -public: - DummyInput() : pos(0) - { - isSeekable = true; - hasPosition = true; - hasSize = true; - } - - size_t read(void *buf, size_t count) - { - assert(pos >= 0 && pos <= 11); - if(count+pos > 11) - count = 11-pos; - assert(count <= 11); - - memcpy(buf, _data+pos, count); - pos += count; - - assert(pos >= 0 && pos <= 11); - return count; - } - - void seek(size_t npos) - { - if(npos > 11) npos = 11; - pos = npos; - } - - size_t tell() const { return pos; } - size_t size() const { return 11; } - - bool eof() const { return pos == 11; } -}; diff --git a/stream/tests/dummy_test.cpp b/stream/tests/memory_test.cpp similarity index 89% rename from stream/tests/dummy_test.cpp rename to stream/tests/memory_test.cpp index cd8f64e3c3..7e7f34075e 100644 --- a/stream/tests/dummy_test.cpp +++ b/stream/tests/memory_test.cpp @@ -1,13 +1,14 @@ -#include "dummy_input.cpp" - #include #include +#include "../servers/memory_stream.h" + +using namespace Mangle::Stream; using namespace std; int main() { - Stream *inp = new DummyInput(); + Stream *inp = new MemoryStream("hello world", 11); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index a2bae1c8e6..5301133622 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -1,13 +1,14 @@ -#include "dummy_input.cpp" +#include "../servers/memory_stream.h" #include "ogre_datastream.h" #include +using namespace Mangle::Stream; using namespace Ogre; using namespace std; int main() { - Stream *inp = new DummyInput(); + Stream *inp = new MemoryStream("hello world", 11); DataStreamPtr p(new MangleDataStream("hello", inp, true)); cout << "Name: " << p->getName() << endl; cout << "As string: " << p->getAsString() << endl; From 084fd9dfb2557409072481991e1e2f2b2924742a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:23:09 +0100 Subject: [PATCH 023/269] Added getPtr and clone to MemoryStream --- stream/servers/memory_stream.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 063b0fcf14..5bceb6bbd0 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -54,6 +54,25 @@ class MemoryStream : public Stream size_t tell() const { return pos; } size_t size() const { return length; } bool eof() const { return pos == length; } + + // New members in MemoryStream: + + /// Get the base pointer to the entire buffer + const void *getPtr() const { return data; } + + /// Clone this memory stream + /** Make a new stream of the same buffer. If setPos is true, we also + set the clone's position to be the same as ours. + + No memory is copied during this operation, the new stream is + just another 'view' into the same shared memory buffer. + */ + MemoryStream *clone(bool setPos=false) const + { + MemoryStream *res = new MemoryStream(data, length); + if(setPos) res->seek(pos); + return res; + } }; }} // namespaces From 9cb57f9ccd5a2f0be829224fb057f86b1e9db981 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:40:02 +0100 Subject: [PATCH 024/269] Added boost/shared_ptr to memory_stream for future testing --- stream/servers/memory_stream.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 5bceb6bbd0..f6e878dd15 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -3,10 +3,16 @@ #include #include "../stream.h" +#include namespace Mangle { namespace Stream { +// Do this before the class declaration, since the class itself +// depends on it +class MemoryStream; +typedef boost::shared_ptr MemoryStreamPtr; + /** A Stream wrapping a memory buffer This will create a fully seekable stream out any pointer/length @@ -67,9 +73,9 @@ class MemoryStream : public Stream No memory is copied during this operation, the new stream is just another 'view' into the same shared memory buffer. */ - MemoryStream *clone(bool setPos=false) const + MemoryStreamPtr clone(bool setPos=false) const { - MemoryStream *res = new MemoryStream(data, length); + MemoryStreamPtr res = new MemoryStream(data, length); if(setPos) res->seek(pos); return res; } From 71c3be13fcdc4aebf3cfa03803966da3aca07c68 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 29 Dec 2009 16:52:47 +0100 Subject: [PATCH 025/269] Started buffer_stream.h, WIP --- stream/filters/buffer_stream.h | 23 +++++++++++++++++++++++ stream/servers/memory_stream.h | 17 +++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 stream/filters/buffer_stream.h diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h new file mode 100644 index 0000000000..f8290ea5eb --- /dev/null +++ b/stream/filters/buffer_stream.h @@ -0,0 +1,23 @@ +#ifndef MANGLE_STREAM_BUFFER_H +#define MANGLE_STREAM_BUFFER_H + +#include "../servers/memory_stream.h" +#include "../clients/iwrapper.h" + +namespace Mangle { +namespace Stream { + +/** A Stream that reads another Stream into a buffer, and serves it as + a MemoryStream. + */ +class BufferStream : public MemoryStream +{ + public: + BufferStream(Stream *input) + { + // Allocate memory, read the stream into it. Then call set() + } +}; + +}} // namespaces +#endif diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index f6e878dd15..c47ffa5358 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -32,8 +32,17 @@ class MemoryStream : public Stream hasSize = true; } + MemoryStream() + : data(NULL), length(0), pos(0); + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + size_t read(void *buf, size_t count) { + assert(data != NULL); assert(pos <= length); // Don't read more than we have @@ -63,6 +72,14 @@ class MemoryStream : public Stream // New members in MemoryStream: + /// Set a new buffer and length. This will rewind the position to zero. + void set(const void* _data, size_t _length) + { + data = _data; + length = _length; + pos = 0; + } + /// Get the base pointer to the entire buffer const void *getPtr() const { return data; } From aafe01dcccc1d0dac958585e56a11e42c7a29721 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 11:50:21 +0100 Subject: [PATCH 026/269] Finished memory and buffer streams (for now) --- stream/filters/buffer_stream.h | 47 ++++++++++++++++++++++++++++++++-- stream/servers/memory_stream.h | 15 ++++++----- stream/tests/Makefile | 3 +++ stream/tests/buffer_test.cpp | 41 +++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 stream/tests/buffer_test.cpp diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h index f8290ea5eb..7790a75428 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.h @@ -2,20 +2,63 @@ #define MANGLE_STREAM_BUFFER_H #include "../servers/memory_stream.h" -#include "../clients/iwrapper.h" +#include namespace Mangle { namespace Stream { /** A Stream that reads another Stream into a buffer, and serves it as - a MemoryStream. + a MemoryStream. Might be expanded with other capabilities later. */ class BufferStream : public MemoryStream { + std::vector buffer; + public: BufferStream(Stream *input) { // Allocate memory, read the stream into it. Then call set() + if(input->hasSize) + { + // We assume that we can get the position as well + assert(input->hasPosition); + + // Calculate how much is left of the stream + size_t left = input->size() - input->tell(); + + // Allocate the buffer and fill it + buffer.resize(left); + input->read(&buffer[0], left); + } + else + { + // We DON'T know how big the stream is. We'll have to read + // it in increments. + const int ADD = 32*1024; + size_t len=0, newlen; + + while(!input->eof()) + { + // Read one block + newlen = len + ADD; + buffer.resize(newlen); + size_t read = input->read(&buffer[len], ADD); + + // Increase the total length + len += read; + + // If we read less than expected, we should be at the + // end of the stream + assert(read == ADD || input->eof()); + } + + // Downsize to match the real length + buffer.resize(len); + } + + // After the buffer has been filled, set up our MemoryStream + // ancestor to reference it. + set(&buffer[0], buffer.size()); } }; diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index c47ffa5358..03f7a42f2c 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -3,15 +3,14 @@ #include #include "../stream.h" -#include namespace Mangle { namespace Stream { // Do this before the class declaration, since the class itself -// depends on it -class MemoryStream; -typedef boost::shared_ptr MemoryStreamPtr; +// depends on it. TODO: Postponed for later +//class MemoryStream; +//typedef boost::shared_ptr MemoryStreamPtr; /** A Stream wrapping a memory buffer @@ -33,7 +32,7 @@ class MemoryStream : public Stream } MemoryStream() - : data(NULL), length(0), pos(0); + : data(NULL), length(0), pos(0) { isSeekable = true; hasPosition = true; @@ -89,10 +88,12 @@ class MemoryStream : public Stream No memory is copied during this operation, the new stream is just another 'view' into the same shared memory buffer. + + TODO: Rewrite to use smart pointers */ - MemoryStreamPtr clone(bool setPos=false) const + MemoryStream* clone(bool setPos=false) const { - MemoryStreamPtr res = new MemoryStream(data, length); + MemoryStream* res = new MemoryStream(data, length); if(setPos) res->seek(pos); return res; } diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 8006572bdb..c86a7397ff 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -15,5 +15,8 @@ audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/iwrapper.h . memory_test: memory_test.cpp ../stream.h ../servers/memory_stream.h $(GCC) $< -o $@ +buffer_test: buffer_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h + $(GCC) $< -o $@ + clean: rm *_test diff --git a/stream/tests/buffer_test.cpp b/stream/tests/buffer_test.cpp new file mode 100644 index 0000000000..24bb7e0706 --- /dev/null +++ b/stream/tests/buffer_test.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include "../filters/buffer_stream.h" + +using namespace Mangle::Stream; +using namespace std; + +int main() +{ + Stream *orig = new MemoryStream("hello world", 11); + Stream *inp = new BufferStream(orig); + + cout << "Size: " << inp->size() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking...\n"; + inp->seek(3); + cout << "Pos: " << inp->tell() << endl; + char data[12]; + memset(data, 0, 12); + cout << "Reading: " << inp->read(data, 4) << endl; + cout << "Four bytes: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking again...\n"; + inp->seek(33); + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << "\nSeek to 6\n"; + inp->seek(6); + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Over-reading: " << inp->read(data, 200) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + inp->seek(0); + cout << "Finally, reading the entire string: " << inp->read(data,11) << endl; + cout << "Result: " << data << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Pos: " << inp->tell() << endl; + + return 0; +} From d60f0fa9009992e2ae2a9cee1f11107591daa7ea Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 12:29:24 +0100 Subject: [PATCH 027/269] Finished first rewrite draft. Not done or tested, but good enough for now. --- sound/{outputs => filters}/input_filter.h | 32 ++-- sound/{outputs => filters}/openal_audiere.h | 14 +- sound/outputs/openal_out.cpp | 177 ++------------------ sound/outputs/openal_out.h | 8 +- sound/tests/audiere_test.cpp | 7 - sound/tests/ffmpeg_openal_test.cpp | 7 - sound/tests/openal_audiere_test.cpp | 4 +- 7 files changed, 40 insertions(+), 209 deletions(-) rename sound/{outputs => filters}/input_filter.h (67%) rename sound/{outputs => filters}/openal_audiere.h (59%) delete mode 100644 sound/tests/audiere_test.cpp delete mode 100644 sound/tests/ffmpeg_openal_test.cpp diff --git a/sound/outputs/input_filter.h b/sound/filters/input_filter.h similarity index 67% rename from sound/outputs/input_filter.h rename to sound/filters/input_filter.h index 455a60e14f..85a97321d4 100644 --- a/sound/outputs/input_filter.h +++ b/sound/filters/input_filter.h @@ -1,7 +1,7 @@ #ifndef MANGLE_INPUT_FILTER_H #define MANGLE_INPUT_FILTER_H -#include "../sound.h" +#include "../output.h" #include @@ -10,39 +10,29 @@ namespace Sound { /** @brief This filter class adds file loading capabilities to a - Sound::Manager class, by associating an InputManager with it. + Sound::SoundFactory class, by associating an SampleSourceLoader + with it. - The class takes an existing Manager able to load streams, and - associates an InputManager with it. The combined class is able to - load files directly. - - Example: - \code - - // Add FFmpeg input to an OpenAL soud output manager. OpenAL cannot - // decode sound files on its own. - InputFilter mg(new OpenAL_Manager, new FFM_InputManager); - - // We can now load filenames directly. - mg.load("file1.mp3"); - \endcode + The class takes an existing SoundFactory able to load streams, and + associates an SampleSourceLoader with it. The combined class is + able to load files directly. */ -class InputFilter : public Manager +class InputFilter : public SoundFactory { protected: - Manager *snd; - InputManager *inp; + SoundFactory *snd; + SampleSourceLoader *inp; public: /// Empty constructor InputFilter() {} /// Assign an input manager and a sound manager to this object - InputFilter(Manager *_snd, InputManager *_inp) + InputFilter(SoundFactory *_snd, SampleSourceLoader *_inp) { set(_snd, _inp); } /// Assign an input manager and a sound manager to this object - void set(Manager *_snd, InputManager *_inp) + void set(SoundFactory *_snd, SampleSourceLoader *_inp) { inp = _inp; snd = _snd; diff --git a/sound/outputs/openal_audiere.h b/sound/filters/openal_audiere.h similarity index 59% rename from sound/outputs/openal_audiere.h rename to sound/filters/openal_audiere.h index 65947b22fc..45024094ac 100644 --- a/sound/outputs/openal_audiere.h +++ b/sound/filters/openal_audiere.h @@ -2,23 +2,23 @@ #define MANGLE_FFMPEG_OPENAL_H #include "input_filter.h" -#include "input_audiere.h" -#include "output_openal.h" +#include "../sources/audiere_source.h" +#include "../outputs/openal_out.h" namespace Mangle { namespace Sound { /// A InputFilter that adds audiere decoding to OpenAL. Audiere has /// it's own output, but OpenAL sports 3D and other advanced features. -class OpenAL_Audiere_Manager : public InputFilter +class OpenAL_Audiere_Factory : public InputFilter { public: - OpenAL_Audiere_Manager() + OpenAL_Audiere_Factory() { - set(new OpenAL_Manager, - new AudiereInput); + set(new OpenAL_Factory, + new AudiereLoader); } - ~OpenAL_Audiere_Manager() + ~OpenAL_Audiere_Factory() { delete snd; delete inp; diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 5d3633e753..b5ec9c171e 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,8 +1,6 @@ #include "openal_out.h" #include -// ALuint bufferID; - using namespace Mangle::Sound; // ---- Helper functions and classes ---- @@ -65,11 +63,6 @@ static void getALFormat(InputStream *inp, int &fmt, int &rate) // ---- OpenAL_Sound ---- -OpenAL_Sound::OpenAL_Sound(SampleSource *input) -{ - -} - void OpenAL_Sound::play() { alSourcePlay(inst); @@ -110,174 +103,34 @@ void OpenAL_Sound::setPos(float x, float y, float z) checkALError("setting position"); } - - - - - - - - - - - - - - - - - - - -Instance *OpenAL_Sound::getInstance(bool is3d, bool repeat) +OpenAL_Sound::OpenAL_Sound(SampleSource *input) { - assert((!repeat || !stream) && "OpenAL implementation does not support looping streams"); + // Get the format + int fmt, rate; + getALFormat(inp, fmt, rate); - if(stream) - return new OpenAL_Stream_Instance(source->getStream(), owner); + // Read the entire stream into a buffer + BufferStream buf(input); - // Load the buffer if it hasn't been done already - if(bufferID == 0) - { - // Get an input stream and load the file from it - InputStream *inp = source->getStream(); - - std::vector buffer; - - // Add 32 kb at each increment - const int ADD = 32*1024; - - // Fill the buffer. We increase the buffer until it's large - // enough to fit all the data. - while(true) - { - // Increase the buffer - int oldlen = buffer.size(); - buffer.resize(oldlen+ADD); - - // Read the data - size_t len = inp->getData(&buffer[oldlen], ADD); - - // If we read less than requested, we're done. - if(len < ADD) - { - // Downsize the buffer to the right size - buffer.resize(oldlen+len); - break; - } - } - - // Get the format - int fmt, rate; - getALFormat(inp, fmt, rate); - - // We don't need the file anymore - inp->drop(); - source->drop(); - source = NULL; - - // Move the data into OpenAL - alGenBuffers(1, &bufferID); - alBufferData(bufferID, fmt, &buffer[0], buffer.size(), rate); - checkALError("loading sound buffer"); - } // End of buffer loading - - // At this point, the file data has been loaded into the buffer - // in 'bufferID', and we should be ready to go. + // Move the data into OpenAL + alGenBuffers(1, &bufferID); assert(bufferID != 0); + alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + checkALError("loading sound buffer"); - return new OpenAL_Simple_Instance(bufferID); -} - - -// ---- OpenAL_Simple_Instance ---- - -OpenAL_Simple_Instance::OpenAL_Simple_Instance(ALuint buf) -{ - // Create instance and associate buffer + // Create a source alGenSources(1, &inst); alSourcei(inst, AL_BUFFER, buf); } -OpenAL_Simple_Instance::~OpenAL_Simple_Instance() +OpenAL_Sound::~OpenAL_Sound() { // Stop alSourceStop(inst); // Return sound alDeleteSources(1, &inst); -} - - -// ---- OpenAL_Stream_Instance ---- - -OpenAL_Stream_Instance::OpenAL_Stream_Instance(InputStream *_stream, - OpenAL_Manager *_owner) - : stream(_stream), owner(_owner) -{ - // Deduce the file format from the stream info - getALFormat(stream, fmt, rate); - - // Create the buffers and the sound instance - alGenBuffers(BUFS, bufs); - alGenSources(1, &inst); - - checkALError("initializing"); - - // Fill the buffers and que them - for(int i=0; iadd_stream(this); -} - -void OpenAL_Stream_Instance::queueBuffer(ALuint bId) -{ - char buf[SIZE]; - - // Get the data - int len = stream->getData(buf, SIZE); - if(len == 0) - return; - - // .. and stash it - alBufferData(bId, fmt, buf, len, rate); - alSourceQueueBuffers(inst, 1, &bId); -} - -OpenAL_Stream_Instance::~OpenAL_Stream_Instance() -{ - // Remove ourselves from streaming list - owner->remove_stream(lit); - - // Stop - alSourceStop(inst); - - // Kill the input stream - stream->drop(); - - // Return sound - alDeleteSources(1, &inst); - - // Delete buffers - alDeleteBuffers(BUFS, bufs); -} - -void OpenAL_Stream_Instance::update() -{ - ALint count; - alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); - - for(int i = 0;i < count;i++) - { - // Unque a finished buffer - ALuint bId; - alSourceUnqueueBuffers(inst, 1, &bId); - - // Queue a new buffer - queueBuffer(bId); - } + + // Delete buffer + alDeleteBuffers(1, &bufferID); } diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 95d27845ca..1cce21607f 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -2,6 +2,7 @@ #define MANGLE_SOUND_OPENAL_OUT_H #include "../output.h" +#include "../../stream/filters/buffer_stream.h" #include #include @@ -15,6 +16,7 @@ class OpenAL_Sound : public Sound { protected: ALuint inst; + ALuint bufferID; public: OpenAL_Sound(SampleSource *input); @@ -39,7 +41,7 @@ class OpenAL_Sound : public Sound void setPos(float x, float y, float z); }; -class OpenALFactory : public SoundFactory +class OpenAL_Factory : public SoundFactory { ALCdevice *Device; ALCcontext *Context; @@ -49,7 +51,7 @@ class OpenALFactory : public SoundFactory /// Initialize object. Pass true (default) if you want the /// constructor to set up the current ALCdevice and ALCcontext for /// you. - OpenALFactory(bool doSetup = true) + OpenAL_Factory(bool doSetup = true) : didSetup(doSetup) { needsUpdate = false; @@ -72,7 +74,7 @@ class OpenALFactory : public SoundFactory } } - ~OpenALFactory() + ~OpenAL_Factory() { // Deinitialize sound system if(didSetup) diff --git a/sound/tests/audiere_test.cpp b/sound/tests/audiere_test.cpp deleted file mode 100644 index defb8c95fe..0000000000 --- a/sound/tests/audiere_test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "audiere_imp.h" - -using namespace Mangle::Sound; - -AudiereManager mg; - -#include "common.cpp" diff --git a/sound/tests/ffmpeg_openal_test.cpp b/sound/tests/ffmpeg_openal_test.cpp deleted file mode 100644 index 28391af3ec..0000000000 --- a/sound/tests/ffmpeg_openal_test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "openal_ffmpeg.h" - -using namespace Mangle::Sound; - -OpenAL_FFM_Manager mg; - -#include "common.cpp" diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index c9011f48e2..3036e02a2c 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,7 +1,7 @@ -#include "openal_audiere.h" +#include "../filters/openal_audiere.h" using namespace Mangle::Sound; -OpenAL_Audiere_Manager mg; +OpenAL_Audiere_Factory mg; #include "common.cpp" From d22bea5eab9e8064738aab4456952bda3bb88f95 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 12:36:35 +0100 Subject: [PATCH 028/269] Added more capabilities to Audiere sources --- sound/source.h | 3 ++- sound/sources/audiere_source.cpp | 25 ++++++++++++++++--------- sound/sources/audiere_source.h | 4 ++++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/sound/source.h b/sound/source.h index 7361fb1182..db5bc6b47f 100644 --- a/sound/source.h +++ b/sound/source.h @@ -33,7 +33,8 @@ class SampleSource : public Stream::Stream bool eof() const { return isEof; } - // Disabled functions + // Disabled functions by default. You can still override them in + // subclasses. void seek(size_t pos) const { assert(0); } size_t tell() const { assert(0); } size_t size() const { assert(0); } diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 6ebc4f8813..d99c6c4ee6 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -28,11 +28,14 @@ void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) { SampleFormat fmt; sample->getFormat(*channels, *rate, fmt); - if(fmt == SF_U8) - *bits = 8; - else if(fmt == SF_S16) - *bits = 16; - else assert(0); + if(bits) + { + if(fmt == SF_U8) + *bits = 8; + else if(fmt == SF_S16) + *bits = 16; + else assert(0); + } } /* @@ -103,7 +106,7 @@ AudiereSource::AudiereSource(const std::string &file) if(!sample) fail("Couldn't load file " + file); - getFormat(); + setup(); } AudiereSource::AudiereSource(Stream::Stream *input) @@ -114,15 +117,15 @@ AudiereSource::AudiereSource(Stream::Stream *input) if(!sample) fail("Couldn't load stream"); - getFormat(); + setup(); } AudiereSource::AudiereSource(audiere::SampleSourcePtr src) : sample(src) -{ assert(sample); getFormat(); } +{ assert(sample); setup(); } // Common function called from all constructors -AudiereSource::getFormat() +AudiereSource::setup() { assert(sample); @@ -136,4 +139,8 @@ AudiereSource::getFormat() // Make sure that our pullover hack will work. Increase this size if // this doesn't work in all cases. assert(frameSize <= PSIZE); + + isSeekable = sample->isSeekable(); + hasPosition = true; + hasSize = true; } diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h index 12ae81bce5..1348e112c0 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.h @@ -40,6 +40,10 @@ class AudiereSource : public SampleSource void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); size_t read(void *data, size_t length); + + void seek(size_t pos) const { sample->setPosition(pos); } + size_t tell() const { return sample->getPosition(); } + size_t size() const { return sample->getLength(); } }; #include "loadertemplate.h" From 7d36b599d0a991857b1d5952f9831b0828fad013 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Dec 2009 16:15:46 +0100 Subject: [PATCH 029/269] Added Sound.clone(), implemented with OpenAL. --- sound/output.h | 40 ++++++++++++++++++++++++++---------- sound/outputs/openal_out.cpp | 38 +++++++++++++++++++++++++++++++--- sound/outputs/openal_out.h | 24 ++++++++++------------ 3 files changed, 75 insertions(+), 27 deletions(-) diff --git a/sound/output.h b/sound/output.h index 4cdac7bed5..0829e4789c 100644 --- a/sound/output.h +++ b/sound/output.h @@ -17,6 +17,11 @@ namespace Sound { may be connected to a SampleSource or read directly from a file, and they may support 3d sounds, looping and other features depending on the capabilities of the backend system. + + To create multiple instances of one sound, it is recommended to + 'clone' an existing instance instead of reloading it from + file. Cloned sounds will often (depending on the back-end) use + less memory due to shared buffers. */ class Sound { @@ -31,14 +36,33 @@ class Sound virtual void pause() = 0; /// Check if the sound is still playing - virtual bool isPlaying() = 0; + virtual bool isPlaying() = 0 const; /// Set the volume. The parameter must be between 0.0 and 1.0. virtual void setVolume(float) = 0; - /// Set the position. May not have any effect on 2D sounds. + /// Set left/right pan. -1.0 is left, 0.0 is center and 1.0 is right. + virtual void setPan(float) = 0; + + /// Set the position. May not work with all backends. virtual void setPos(float x, float y, float z) = 0; + /// Set loop mode + virtual void setRepeat(bool) = 0; + + /// Set streaming mode. + /** This may be used by implementations to optimize for very large + files. If streaming mode is off (default), most implementations + will load the entire file into memory before starting playback. + */ + virtual void setStreaming(bool) = 0; + + /// Create a new instance of this sound. + /** Playback status is not cloned, only the sound data + itself. Back-ends can use this as a means of sharing data and + saving memory. */ + virtual Sound* clone() const = 0; + /// Virtual destructor virtual ~Sound() {} }; @@ -71,12 +95,6 @@ class SoundFactory */ bool has3D; - /** @brief true if 'repeat' and 'stream' can be used simultaneously. - If false, repeating a streamed sound will give undefined - behavior. - */ - bool canRepeatStream; - /// true if we can load sounds directly from file (containing encoded data) bool canLoadFile; @@ -99,7 +117,7 @@ class SoundFactory large files, but they are not required to. @return a new Sound object */ - virtual Sound *load(SampleSource *input, bool stream=false) = 0; + virtual Sound *load(SampleSource *input) = 0; /** @brief Load a sound file from stream. Only valid if canLoadStream @@ -109,7 +127,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(Stream::Stream *input, bool stream=false) = 0; + virtual Sound *load(Stream::Stream *input) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile @@ -119,7 +137,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(const std::string &file, bool stream=false) = 0; + virtual Sound *load(const std::string &file) = 0; /// Call this every frame if needsUpdate is true /** diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index b5ec9c171e..649a56aef3 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -103,6 +103,28 @@ void OpenAL_Sound::setPos(float x, float y, float z) checkALError("setting position"); } +void OpenAL_Sound::setRepeat(bool rep) +{ + alSourcei(Source, AL_LOOPING, rep?AL_TRUE:AL_FALSE); +} + +void OpenAL_Sound::setup() +{ +} + +// Constructor used for cloned sounds +OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) + : refCnt(ref), bufferID(buf) +{ + // Increase the reference count + assert(ref != NULL); + *refCnt++; + + // Create a source + alGenSources(1, &inst); + alSourcei(inst, AL_BUFFER, bufferID); +} + OpenAL_Sound::OpenAL_Sound(SampleSource *input) { // Get the format @@ -120,7 +142,11 @@ OpenAL_Sound::OpenAL_Sound(SampleSource *input) // Create a source alGenSources(1, &inst); - alSourcei(inst, AL_BUFFER, buf); + alSourcei(inst, AL_BUFFER, bufferID); + + // Create a cheap reference counter for the buffer + refCnt = new int; + *refCnt = 1; } OpenAL_Sound::~OpenAL_Sound() @@ -131,6 +157,12 @@ OpenAL_Sound::~OpenAL_Sound() // Return sound alDeleteSources(1, &inst); - // Delete buffer - alDeleteBuffers(1, &bufferID); + // Decrease the reference + if(--(*refCnt)) + { + // We're the last owner. Delete the buffer and the counter + // itself. + alDeleteBuffers(1, &bufferID); + delete refCnt; + } } diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 1cce21607f..a0b8ed4129 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -18,27 +18,26 @@ class OpenAL_Sound : public Sound ALuint inst; ALuint bufferID; + // Poor mans reference counting. Might improve this later. + int *refCnt; + public: OpenAL_Sound(SampleSource *input); + OpenAL_Sound(ALuint buf, int *ref); // Used for cloning ~OpenAL_Sound(); - /// Play or resume the sound void play(); - - /// Stop the sound void stop(); - - /// Pause the sound, may be resumed later void pause(); - - /// Check if the sound is still playing - bool isPlaying(); - - /// Set the volume. The parameter must be between 0.0 and 1.0. + bool isPlaying() const; void setVolume(float); - - /// Set the 3D position. void setPos(float x, float y, float z); + void setRepeat(bool); + void setStreaming(bool) {} // Not implemented yet + Sound* clone() const; + + /// Not implemented + void setPan(float) {} }; class OpenAL_Factory : public SoundFactory @@ -56,7 +55,6 @@ class OpenAL_Factory : public SoundFactory { needsUpdate = false; has3D = true; - canRepeatStream = false; canLoadFile = false; canLoadStream = false; canLoadSource = true; From 674ac556cc51e42636084c0c8667a44707792115 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 31 Dec 2009 14:48:34 +0100 Subject: [PATCH 030/269] Added StdStream, FileStream and SliceStream --- stream/filters/slice_stream.h | 54 ++++++++++++++++++++++++++++++++++ stream/servers/file_stream.h | 26 +++++++++++++++++ stream/servers/std_stream.h | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 stream/filters/slice_stream.h create mode 100644 stream/servers/file_stream.h create mode 100644 stream/servers/std_stream.h diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h new file mode 100644 index 0000000000..5bd47d978e --- /dev/null +++ b/stream/filters/slice_stream.h @@ -0,0 +1,54 @@ +#ifndef MANGLE_STREAM_SLICE_H +#define MANGLE_STREAM_SLICE_H + +#include "../stream.h" + +namespace Mangle { +namespace Stream { + +/** A Stream that represents a subset (called a slice) of another stream. + */ +class SliceStream : public Stream +{ + Stream *src; + size_t offset, length, pos; + + public: + SliceStream(Stream *_src, size_t _offset, size_t _length) + : src(_src), offset(_offset), length(_length), pos(0) + { + assert(src->hasSize); + assert(src->isSeekable); + + // Make sure we can actually fit inside the source stream + assert(src->size() <= offset+length); + } + + size_t read(void *buf, size_t count) + { + // Check that we're not reading past our slice + if(count > length-pos) + count = length-pos; + + // Seek into place and reading + src->seek(offset+pos); + count = src->read(buf, count); + + pos += count; + assert(pos <= length); + return count; + } + + void seek(size_t _pos) + { + pos = _pos; + if(pos > length) pos = length; + } + + bool eof() { return pos == length; } + size_t tell() { return pos; } + size_t size() { return length; } +}; + +}} // namespaces +#endif diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h new file mode 100644 index 0000000000..1d6f92032f --- /dev/null +++ b/stream/servers/file_stream.h @@ -0,0 +1,26 @@ +#ifndef MANGLE_STREAM_FILESERVER_H +#define MANGLE_STREAM_FILESERVER_H + +#include "std_stream.h" +#include + +namespace Mangle { +namespace Stream { + +/** Very simple file input stream, based on std::ifstream + */ +class FileStream : public StdStream +{ + std::ifstream file; + + public: + FileStream(const std::string &name) + : StdStream(&file) + { + file.open(name, ios::binary); + } + ~FileStream() { file.close(); } +}; + +}} // namespaces +#endif diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h new file mode 100644 index 0000000000..e93295f158 --- /dev/null +++ b/stream/servers/std_stream.h @@ -0,0 +1,55 @@ +#ifndef MANGLE_STREAM_FILESERVER_H +#define MANGLE_STREAM_FILESERVER_H + +#include "../stream.h" +#include + +namespace Mangle { +namespace Stream { + +/** Simplest wrapper for std::istream. + + TODO: No error checking yet. + */ +class StdStream : public Stream +{ + std::istream *inf; + + public: + StdStream(std::istream *_inf) + : inf(_inf) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + } + + size_t read(void* buf, size_t len) + { + inf->read((char*)buf, len); + return inf->gcount(); + } + + void seek(size_t pos) + { inf->seekg(pos); } + + size_t tell() const + // Hack around the fact that ifstream->tellg() isn't const + { return ((StdStream*)this)->inf->tellg(); } + + size_t size() const + { + // Use the standard iostream size hack, terrible as it is. + std::streampos pos = inf->tellg(); + inf->seekg(0, ios_base::end); + size_t res = inf->tellg(); + inf->seekg(pos); + return res; + } + + bool eof() const + { return inf->eof(); } +}; + +}} // namespaces +#endif From c45170f4206189eb6124213083880013c03f3907 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 31 Dec 2009 15:37:01 +0100 Subject: [PATCH 031/269] Starting rewrite to boost::shared_ptr. Not done, not tested. --- sound/buffer.h | 57 -------------------------------- sound/filters/input_filter.h | 14 ++++---- sound/filters/openal_audiere.h | 5 --- sound/output.h | 13 +++++--- sound/source.h | 8 +++-- stream/clients/audiere_file.h | 9 ++--- stream/clients/iwrapper.h | 30 ----------------- stream/clients/ogre_datastream.h | 15 +++++---- stream/filters/buffer_stream.h | 4 ++- stream/filters/slice_stream.h | 6 ++-- stream/servers/file_stream.h | 2 ++ stream/servers/memory_stream.h | 12 +++---- stream/servers/ogre_datastream.h | 2 ++ stream/servers/phys_stream.h | 2 ++ stream/servers/std_stream.h | 2 ++ stream/stream.h | 3 ++ tools/shared_ptr.h | 3 ++ vfs/clients/ogre_archive.cpp | 16 ++++----- vfs/clients/ogre_archive.h | 13 ++++---- vfs/clients/wrapper.h | 30 ----------------- vfs/servers/ogre_vfs.cpp | 12 +++---- vfs/servers/ogre_vfs.h | 18 +++++----- vfs/servers/physfs_vfs.h | 20 +++++------ vfs/vfs.h | 25 ++++++++------ 24 files changed, 116 insertions(+), 205 deletions(-) delete mode 100644 sound/buffer.h delete mode 100644 stream/clients/iwrapper.h create mode 100644 tools/shared_ptr.h delete mode 100644 vfs/clients/wrapper.h diff --git a/sound/buffer.h b/sound/buffer.h deleted file mode 100644 index 9cb27bd9cb..0000000000 --- a/sound/buffer.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef MANGLE_SOUND_BUFFER_H -#define MANGLE_SOUND_BUFFER_H - -#include "source.h" -#include "sources/memsource.h" -#include -#include - -namespace Mangle { -namespace Sound { - -/** A sample buffer is a factory that creates SampleSources from one - single sound source. It is helpful when you have many instances of - one sound and want to use one shared memory buffer. - - This is just a helper class - you don't have to include it in your - program if you don't need it. -*/ -class SampleBuffer -{ - std::vector buffer; - - public: - /// Reads the source into a memory buffer. Not heavily optimized. - SampleBuffer(SampleSource *source) - { - size_t final = 0; - - while(!source->eof()) - { - const int add = 16*1024; - - // Allocate more memory - size_t newSize = final + add; - buffer.resize(newSize); - - // Fill in data - size_t read = source->read(&buffer[final], add); - - // If we couldn't read enough data, we should be at the end - // of the stream - assert(read == add || source->eof()); - - final += read; - } - - // Downsize the buffer to the actual length - buffer.resize(final); - } - - /// Get a new source - SampleSource *get() - { return new MemorySource(&buffer[0], buffer.size()); } -}; - -}} -#endif diff --git a/sound/filters/input_filter.h b/sound/filters/input_filter.h index 85a97321d4..17dda55347 100644 --- a/sound/filters/input_filter.h +++ b/sound/filters/input_filter.h @@ -20,19 +20,19 @@ namespace Sound { class InputFilter : public SoundFactory { protected: - SoundFactory *snd; - SampleSourceLoader *inp; + SoundFactoryPtr snd; + SampleSourceLoaderPtr inp; public: /// Empty constructor InputFilter() {} /// Assign an input manager and a sound manager to this object - InputFilter(SoundFactory *_snd, SampleSourceLoader *_inp) + InputFilter(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp) { set(_snd, _inp); } /// Assign an input manager and a sound manager to this object - void set(SoundFactory *_snd, SampleSourceLoader *_inp) + void set(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp) { inp = _inp; snd = _snd; @@ -50,13 +50,13 @@ class InputFilter : public SoundFactory assert(canLoadSource && canLoadFile); } - virtual Sound *load(const std::string &file, bool stream=false) + virtual SoundPtr load(const std::string &file) { return load(inp->load(file), stream); } - virtual Sound *load(Stream::Stream *input, bool stream=false) + virtual SoundPtr load(Stream::StreamPtr input) { return load(inp->load(input), stream); } - virtual Sound *load(InputSource *input, bool stream=false) + virtual SoundPtr load(SampleSourcePtr input) { return snd->load(input, stream); } virtual void update() { snd->update(); } diff --git a/sound/filters/openal_audiere.h b/sound/filters/openal_audiere.h index 45024094ac..9a2bfcf8cb 100644 --- a/sound/filters/openal_audiere.h +++ b/sound/filters/openal_audiere.h @@ -18,11 +18,6 @@ class OpenAL_Audiere_Factory : public InputFilter set(new OpenAL_Factory, new AudiereLoader); } - ~OpenAL_Audiere_Factory() - { - delete snd; - delete inp; - } }; }} diff --git a/sound/output.h b/sound/output.h index 0829e4789c..dfd1902417 100644 --- a/sound/output.h +++ b/sound/output.h @@ -23,6 +23,9 @@ namespace Sound { file. Cloned sounds will often (depending on the back-end) use less memory due to shared buffers. */ +class Sound; +typedef boost::shared_ptr SoundPtr; + class Sound { public: @@ -61,7 +64,7 @@ class Sound /** Playback status is not cloned, only the sound data itself. Back-ends can use this as a means of sharing data and saving memory. */ - virtual Sound* clone() const = 0; + virtual SoundPtr clone() const = 0; /// Virtual destructor virtual ~Sound() {} @@ -117,7 +120,7 @@ class SoundFactory large files, but they are not required to. @return a new Sound object */ - virtual Sound *load(SampleSource *input) = 0; + virtual SoundPtr load(SampleSource *input) = 0; /** @brief Load a sound file from stream. Only valid if canLoadStream @@ -127,7 +130,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(Stream::Stream *input) = 0; + virtual SoundPtr load(Stream::Stream *input) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile @@ -137,7 +140,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual Sound *load(const std::string &file) = 0; + virtual SoundPtr load(const std::string &file) = 0; /// Call this every frame if needsUpdate is true /** @@ -161,6 +164,8 @@ class SoundFactory float ux, float uy, float uz) = 0; }; +typedef boost::shared_ptr SoundFactoryPtr; + }} // Namespaces #endif diff --git a/sound/source.h b/sound/source.h index db5bc6b47f..0301f4bae8 100644 --- a/sound/source.h +++ b/sound/source.h @@ -40,6 +40,8 @@ class SampleSource : public Stream::Stream size_t size() const { assert(0); } }; +typedef boost::shared_ptr SampleSourcePtr; + /// A factory interface for loading SampleSources from file or stream class SampleSourceLoader { @@ -51,14 +53,16 @@ class SampleSourceLoader bool canLoadFile; /// Load a sound input source from file (if canLoadFile is true) - virtual SampleSource *load(const std::string &file) = 0; + virtual SampleSourcePtr load(const std::string &file) = 0; /// Load a sound input source from stream (if canLoadStream is true) - virtual SampleSource *load(Stream::Stream *input) = 0; + virtual SampleSourcePtr load(Stream::StreamPtr input) = 0; /// Virtual destructor virtual ~SampleSourceLoader() {} }; +typedef boost::shared_ptr SampleSourceLoaderPtr; + }} // namespaces #endif diff --git a/stream/clients/audiere_file.h b/stream/clients/audiere_file.h index 87833e6b31..ec67fd1b73 100644 --- a/stream/clients/audiere_file.h +++ b/stream/clients/audiere_file.h @@ -3,7 +3,6 @@ #include #include -#include "iwrapper.h" namespace Mangle { namespace Stream { @@ -13,11 +12,13 @@ namespace Stream { This lets Audiere read sound files from any generic archive or file manager that supports Mangle streams. */ -class AudiereFile : public audiere::RefImplementation, _SWrapper +class AudiereFile : public audiere::RefImplementation { + StreamPtr inp; + public: - AudiereFile(Stream *inp, bool autoDel=false) - : _SWrapper(inp, autoDel) {} + AudiereFile(StreamPtr _inp) + : inp(_inp) {} /// Read 'count' bytes, return bytes successfully read int read(void *buf, int count) diff --git a/stream/clients/iwrapper.h b/stream/clients/iwrapper.h deleted file mode 100644 index cc12d5b7e2..0000000000 --- a/stream/clients/iwrapper.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef MANGLE_STREAM_IWRAPPER_H -#define MANGLE_STREAM_IWRAPPER_H - -#include "../stream.h" -#include - -namespace Mangle { -namespace Stream { - -/** A generic wrapper class for a Stream::Stream object. - - This is used by other implementations. - */ -class _SWrapper -{ - private: - bool autoDel; - - protected: - Stream *inp; - - public: - _SWrapper(Stream *_inp, bool _autoDel = false) - : inp(_inp), autoDel(_autoDel) { assert(inp != NULL); } - - virtual ~_SWrapper() { if(autoDel) delete inp; } -}; - -}} // namespaces -#endif diff --git a/stream/clients/ogre_datastream.h b/stream/clients/ogre_datastream.h index 6d09799307..23ab94fd4a 100644 --- a/stream/clients/ogre_datastream.h +++ b/stream/clients/ogre_datastream.h @@ -3,7 +3,7 @@ #include #include -#include "iwrapper.h" +#include "../stream.h" namespace Mangle { namespace Stream { @@ -14,8 +14,10 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class MangleDataStream : public Ogre::DataStream, _SWrapper +class MangleDataStream : public Ogre::DataStream { + StreamPtr inp; + void init() { // Get the size, if possible @@ -25,13 +27,12 @@ class MangleDataStream : public Ogre::DataStream, _SWrapper public: /// Constructor without name - MangleDataStream(Stream *inp, bool autoDel=false) - : _SWrapper(inp, autoDel) { init(); } + MangleDataStream(StreamPtr _inp) + : inp(_inp) { init(); } /// Constructor for a named data stream - MangleDataStream(const Ogre::String &name, Stream *inp, bool autoDel=false) - : _SWrapper(inp, autoDel), Ogre::DataStream(name) { init(); } - + MangleDataStream(const Ogre::String &name, StreamPtr _inp) + : inp(_inp), Ogre::DataStream(name) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h index 7790a75428..f04411f98c 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.h @@ -15,7 +15,7 @@ class BufferStream : public MemoryStream std::vector buffer; public: - BufferStream(Stream *input) + BufferStream(StreamPtr input) { // Allocate memory, read the stream into it. Then call set() if(input->hasSize) @@ -62,5 +62,7 @@ class BufferStream : public MemoryStream } }; +typedef boost::shared_ptr BufferStreamPtr; + }} // namespaces #endif diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index 5bd47d978e..49f0ec31da 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -10,11 +10,11 @@ namespace Stream { */ class SliceStream : public Stream { - Stream *src; + StreamPtr src; size_t offset, length, pos; public: - SliceStream(Stream *_src, size_t _offset, size_t _length) + SliceStream(StreamPtr _src, size_t _offset, size_t _length) : src(_src), offset(_offset), length(_length), pos(0) { assert(src->hasSize); @@ -50,5 +50,7 @@ class SliceStream : public Stream size_t size() { return length; } }; +typedef boost::shared_ptr SliceStreamPtr; + }} // namespaces #endif diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h index 1d6f92032f..bb4df4d381 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.h @@ -22,5 +22,7 @@ class FileStream : public StdStream ~FileStream() { file.close(); } }; +typedef boost::shared_ptr FileStreamPtr; + }} // namespaces #endif diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 03f7a42f2c..94b2f61d97 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -8,9 +8,9 @@ namespace Mangle { namespace Stream { // Do this before the class declaration, since the class itself -// depends on it. TODO: Postponed for later -//class MemoryStream; -//typedef boost::shared_ptr MemoryStreamPtr; +// depends on it. +class MemoryStream; +typedef boost::shared_ptr MemoryStreamPtr; /** A Stream wrapping a memory buffer @@ -88,12 +88,10 @@ class MemoryStream : public Stream No memory is copied during this operation, the new stream is just another 'view' into the same shared memory buffer. - - TODO: Rewrite to use smart pointers */ - MemoryStream* clone(bool setPos=false) const + MemoryStreamPtr clone(bool setPos=false) const { - MemoryStream* res = new MemoryStream(data, length); + MemoryStreamPtr res(new MemoryStream(data, length)); if(setPos) res->seek(pos); return res; } diff --git a/stream/servers/ogre_datastream.h b/stream/servers/ogre_datastream.h index 184fa1668f..a5be98c84a 100644 --- a/stream/servers/ogre_datastream.h +++ b/stream/servers/ogre_datastream.h @@ -31,5 +31,7 @@ class OgreStream : public Stream bool eof() const { return inp->eof(); } }; +typedef boost::shared_ptr OgreStreamPtr; + }} // namespaces #endif diff --git a/stream/servers/phys_stream.h b/stream/servers/phys_stream.h index 3660391fda..4312ac0416 100644 --- a/stream/servers/phys_stream.h +++ b/stream/servers/phys_stream.h @@ -30,5 +30,7 @@ class PhysFile : public Stream bool eof() const { return PHYSFS_eof(file); } }; +typedef boost::shared_ptr PhysFilePtr; + }} // namespaces #endif diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h index e93295f158..0e91ebb782 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.h @@ -51,5 +51,7 @@ class StdStream : public Stream { return inf->eof(); } }; +typedef boost::shared_ptr StdStreamPtr; + }} // namespaces #endif diff --git a/stream/stream.h b/stream/stream.h index 7116b8e2a8..75f84cbd2d 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -2,6 +2,7 @@ #define MANGLE_STREAM_INPUT_H #include +#include "../tools/shared_ptr.h" namespace Mangle { namespace Stream { @@ -46,5 +47,7 @@ class Stream virtual bool eof() const = 0; }; +typedef boost::shared_ptr StreamPtr; + }} // namespaces #endif diff --git a/tools/shared_ptr.h b/tools/shared_ptr.h new file mode 100644 index 0000000000..00af6084b7 --- /dev/null +++ b/tools/shared_ptr.h @@ -0,0 +1,3 @@ +// This file should include whatever it needs to define the boost/tr1 +// shared_ptr<> template. +#include diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index 936eda9b6b..0def218db6 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -38,10 +38,10 @@ static void fill(Ogre::StringVector &out, FileInfoList &in) Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) { assert(vfs->hasList); - FileInfoList lst = vfs->list("", recursive, dirs); + FileInfoListPtr lst = vfs->list("", recursive, dirs); Ogre::StringVector *res = new Ogre::StringVector; - fill(*res, lst); + fill(*res, *lst); return Ogre::StringVectorPtr(res); } @@ -49,10 +49,10 @@ Ogre::StringVectorPtr MangleArchive::list(bool recursive, bool dirs) Ogre::FileInfoListPtr MangleArchive::listFileInfo(bool recursive, bool dirs) { assert(vfs->hasList); - FileInfoList lst = vfs->list("", recursive, dirs); + FileInfoListPtr lst = vfs->list("", recursive, dirs); Ogre::FileInfoList *res = new Ogre::FileInfoList; - fill(*res, lst); + fill(*res, *lst); return Ogre::FileInfoListPtr(res); } @@ -63,10 +63,10 @@ Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern, bool dirs) { assert(vfs->hasFind); - FileInfoList lst = vfs->find(pattern, recursive, dirs); + FileInfoListPtr lst = vfs->find(pattern, recursive, dirs); Ogre::StringVector *res = new Ogre::StringVector; - fill(*res, lst); + fill(*res, *lst); return Ogre::StringVectorPtr(res); } @@ -76,10 +76,10 @@ Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, bool dirs) { assert(vfs->hasFind); - FileInfoList lst = vfs->find(pattern, recursive, dirs); + FileInfoListPtr lst = vfs->find(pattern, recursive, dirs); Ogre::FileInfoList *res = new Ogre::FileInfoList; - fill(*res, lst); + fill(*res, *lst); return Ogre::FileInfoListPtr(res); } diff --git a/vfs/clients/ogre_archive.h b/vfs/clients/ogre_archive.h index 70e6045c4e..84f23eb8a1 100644 --- a/vfs/clients/ogre_archive.h +++ b/vfs/clients/ogre_archive.h @@ -3,7 +3,7 @@ #include #include -#include "wrapper.h" +#include "../vfs.h" namespace Mangle { namespace VFS { @@ -15,13 +15,14 @@ namespace VFS { to make your own modifications if you're working with newer (or older) versions. */ -class MangleArchive : public Ogre::Archive, _Wrapper +class MangleArchive : public Ogre::Archive { + VFSPtr vfs; + public: - MangleArchive(VFS *vfs, const std::string &name, - const std::string &archType = "Mangle", - bool autoDel=false) - : _Wrapper(vfs, autoDel), Ogre::Archive(name, archType) {} + MangleArchive(VFSPtr _vfs, const std::string &name, + const std::string &archType = "Mangle") + : vfs(_vfs), Ogre::Archive(name, archType) {} bool isCaseSensitive() const { return vfs->isCaseSensitive; } diff --git a/vfs/clients/wrapper.h b/vfs/clients/wrapper.h deleted file mode 100644 index 357bc8b4f4..0000000000 --- a/vfs/clients/wrapper.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef MANGLE_VFS_WRAPPER_H -#define MANGLE_VFS_WRAPPER_H - -#include "../vfs.h" -#include - -namespace Mangle { -namespace VFS { - -/** A generic wrapper class for a VFS::VFS object. - - This is used by other implementations. - */ -class _Wrapper -{ - private: - bool autoDel; - - protected: - VFS *vfs; - - public: - _Wrapper(VFS *_vfs, bool _autoDel = false) - : vfs(_vfs), autoDel(_autoDel) { assert(vfs != NULL); } - - virtual ~_Wrapper() { if(autoDel) delete vfs; } -}; - -}} // namespaces -#endif diff --git a/vfs/servers/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp index af1f7c9632..0fc051a8aa 100644 --- a/vfs/servers/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -18,10 +18,10 @@ OgreVFS::OgreVFS(const std::string &_group) group = gm->getWorldResourceGroupName(); } -Mangle::Stream::Stream *OgreVFS::open(const std::string &name) +Mangle::Stream::StreamPtr OgreVFS::open(const std::string &name) { Ogre::DataStreamPtr data = gm->openResource(name, group); - return new Stream::OgreStream(data); + return Strea::StreamPtr(new Stream::OgreStream(data)); } static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) @@ -44,8 +44,8 @@ FileInfoList OgreVFS::list(const std::string& dir, bool dirs) const { Ogre::FileInfoListPtr olist = gm->listResourceFileInfo(group, dirs); - FileInfoList res; - fill(res, *olist, dirs); + FileInfoListPtr res(new FileInfoList); + fill(*res, *olist, dirs); return res; } @@ -54,7 +54,7 @@ FileInfoList OgreVFS::find(const std::string& pattern, bool dirs) const { Ogre::FileInfoListPtr olist = gm->findResourceFileInfo(group, pattern, dirs); - FileInfoList res; - fill(res, *olist, dirs); + FileInfoListPtr res(new FileInfoList); + fill(*res, *olist, dirs); return res; } diff --git a/vfs/servers/ogre_vfs.h b/vfs/servers/ogre_vfs.h index 8fe2cca1cd..f212432f80 100644 --- a/vfs/servers/ogre_vfs.h +++ b/vfs/servers/ogre_vfs.h @@ -36,7 +36,7 @@ class OgreVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::Stream *open(const std::string &name); + virtual Stream::StreamPtr open(const std::string &name); /// Check for the existence of a file virtual bool isFile(const std::string &name) const @@ -47,23 +47,23 @@ class OgreVFS : public VFS { return false; } /// This doesn't work. - virtual FileInfo stat(const std::string &name) const - { return FileInfo(); } + virtual FileInfoPtr stat(const std::string &name) const + { return FileInfoPtr(); } /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If /// dirs is true, list directories instead of files. OGRE note: The /// ogre resource systemd does not support recursive listing of /// files. We might make a separate filter for this later. - virtual FileInfoList list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const; + virtual FileInfoListPtr list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const; /// Find files after a given pattern. Wildcards (*) are /// supported. - virtual FileInfoList find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const; + virtual FileInfoListPtr find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const; }; }} // namespaces diff --git a/vfs/servers/physfs_vfs.h b/vfs/servers/physfs_vfs.h index c25a0035cd..f85cc54b89 100644 --- a/vfs/servers/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.h @@ -26,8 +26,8 @@ class PhysVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. - virtual Stream::Stream *open(const std::string &name) - { return new Stream::PhysFile(PHYSFS_openRead(name.c_str())); } + virtual Stream::StreamPtr open(const std::string &name) + { return new Stream::StreamPtr(Stream::PhysFile(PHYSFS_openRead(name.c_str()))); } /// Check for the existence of a file virtual bool isFile(const std::string &name) const @@ -38,15 +38,15 @@ class PhysVFS : public VFS { return PHYSFS_isDirectory(name.c_str()); } /// This doesn't work - virtual FileInfo stat(const std::string &name) const - { assert(0); return FileInfo(); } + virtual FileInfoPtr stat(const std::string &name) const + { assert(0); return FileInfoPtr(); } - virtual FileInfoList list(const std::string& dir = "", + virtual FileInfoListPtr list(const std::string& dir = "", bool recurse=true, bool dirs=false) const { char **files = PHYSFS_enumerateFiles(dir.c_str()); - FileInfoList lst; + FileInfoListPtr lst(new FileInfoList); // Add all teh files int i = 0; @@ -56,14 +56,14 @@ class PhysVFS : public VFS fi.name = files[i]; fi.isDir = false; - lst.push_back(fi); + lst->push_back(fi); } return lst; } - virtual FileInfoList find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const + virtual FileInfoListPtr find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const { assert(0); } }; diff --git a/vfs/vfs.h b/vfs/vfs.h index 4054c6069d..c52bcaebba 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.h @@ -29,6 +29,9 @@ struct FileInfo typedef std::vector FileInfoList; +typedef boost::shared_ptr FileInfoPtr; +typedef boost::shared_ptr FileInfoListPtr; + /** An interface to any file system or other provider of named data streams */ @@ -49,9 +52,9 @@ class VFS /// Virtual destructor virtual ~VFS() {} - /// Open a new data stream. Deleting the object should be enough to - /// close it. - virtual Stream::Stream *open(const std::string &name) = 0; + /// Open a new data stream. Deleting the object (letting all the + /// pointers to it go out of scope) should be enough to close it. + virtual Stream::StreamPtr open(const std::string &name) = 0; /// Check for the existence of a file virtual bool isFile(const std::string &name) const = 0; @@ -60,23 +63,25 @@ class VFS virtual bool isDir(const std::string &name) const = 0; /// Get info about a single file - virtual FileInfo stat(const std::string &name) const = 0; + virtual FileInfoPtr stat(const std::string &name) const = 0; /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If /// dirs is true, list directories instead of files. - virtual FileInfoList list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const = 0; + virtual FileInfoListPtr list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const = 0; /// Find files after a given pattern. Wildcards (*) are /// supported. Only valid if 'hasFind' is true. Don't implement your /// own pattern matching here if the backend doesn't support it /// natively; use a filter instead. - virtual FileInfoList find(const std::string& pattern, - bool recursive=true, - bool dirs=false) const = 0; + virtual FileInfoListPtr find(const std::string& pattern, + bool recursive=true, + bool dirs=false) const = 0; }; +typedef boost::shared_ptr VFSPtr; + }} // namespaces #endif From 38501777b0ae8663141397d2777507413ff777db Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 11:31:09 +0100 Subject: [PATCH 032/269] Added getPtr() family to Stream --- stream/filters/slice_stream.h | 20 ++++++++++++++++++++ stream/servers/memory_stream.h | 13 ++++++++++--- stream/stream.h | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index 49f0ec31da..a24a66e16b 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -22,6 +22,11 @@ class SliceStream : public Stream // Make sure we can actually fit inside the source stream assert(src->size() <= offset+length); + + isSeekable = true; + hasPosition = true; + hasSize = true; + hasPtr = src->hasPtr; } size_t read(void *buf, size_t count) @@ -48,6 +53,21 @@ class SliceStream : public Stream bool eof() { return pos == length; } size_t tell() { return pos; } size_t size() { return length; } + + void *getPtr() { return getPtr(0, length); } + void *getPtr(size_t size) { return getPtr(pos, size); } + void *getPtr(size_t pos, size_t size) + { + // Boundry checks on pos and size. Bounding the size is + // important even when getting pointers, as the source stream + // may use the size parameter for something (such as memory + // mapping or buffering.) + if(pos > length) pos = length; + if(pos+size > length) size = length-pos; + + // Ask the source to kindly give us a pointer + return src->getPtr(offset+pos, size); + } }; typedef boost::shared_ptr SliceStreamPtr; diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 94b2f61d97..23842e09bb 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -37,6 +37,7 @@ class MemoryStream : public Stream isSeekable = true; hasPosition = true; hasSize = true; + hasPtr = true; } size_t read(void *buf, size_t count) @@ -69,6 +70,15 @@ class MemoryStream : public Stream size_t size() const { return length; } bool eof() const { return pos == length; } + /// Get the base pointer to the entire buffer + const void *getPtr() const { return data; } + const void *getPtr(size_t size) const { return ((char*)data)+pos; } + const void *getPtr(size_t pos, size_t size) + { + if(pos > length) pos = length; + return ((char*)data)+pos; + } + // New members in MemoryStream: /// Set a new buffer and length. This will rewind the position to zero. @@ -79,9 +89,6 @@ class MemoryStream : public Stream pos = 0; } - /// Get the base pointer to the entire buffer - const void *getPtr() const { return data; } - /// Clone this memory stream /** Make a new stream of the same buffer. If setPos is true, we also set the clone's position to be the same as ours. diff --git a/stream/stream.h b/stream/stream.h index 75f84cbd2d..704abc88a0 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -3,6 +3,7 @@ #include #include "../tools/shared_ptr.h" +#include namespace Mangle { namespace Stream { @@ -22,6 +23,14 @@ class Stream /// If true, size() works bool hasSize; + /// If true, the getPtr() functions work + bool hasPtr; + + /// Initialize all bools to false by default + Stream() : + isSeekable(false), hasPosition(false), hasSize(false), + hasPtr(false) {} + /// Virtual destructor virtual ~Stream() {} @@ -45,6 +54,19 @@ class Stream /// Returns true if the stream is empty virtual bool eof() const = 0; + + /// Return a pointer to the entire stream. This function (and the + /// other getPtr() variants below) should only be implemented for + /// memory-based streams where using them would be an optimization. + virtual void *getPtr() const { assert(0); } + + /// Get a pointer to a memory region of 'size' bytes from the + /// current position. + virtual void *getPtr(size_t size) const { assert(0); } + + /// Get a pointer to a memory region of 'size' bytes starting from + /// position 'pos' + virtual void *getPtr(size_t pos, size_t size) const { assert(0); } }; typedef boost::shared_ptr StreamPtr; From eaf93691d50ecc97e70550e6dd0dfc4c36b18c6c Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 14:34:46 +0100 Subject: [PATCH 033/269] Nearly finished shared_ptr, and getPtr(). Tests not rewritten yet. --- sound/outputs/openal_out.cpp | 28 ++++++++++++++-------- sound/outputs/openal_out.h | 10 ++++---- sound/source.h | 10 +------- sound/sources/audiere_source.cpp | 2 +- sound/sources/audiere_source.h | 2 +- sound/sources/ffmpeg_source.h | 2 +- sound/sources/loadertemplate.h | 2 +- sound/sources/memsource.h | 40 +++++++++++++++----------------- 8 files changed, 47 insertions(+), 49 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 649a56aef3..9106901563 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -108,10 +108,6 @@ void OpenAL_Sound::setRepeat(bool rep) alSourcei(Source, AL_LOOPING, rep?AL_TRUE:AL_FALSE); } -void OpenAL_Sound::setup() -{ -} - // Constructor used for cloned sounds OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) : refCnt(ref), bufferID(buf) @@ -125,19 +121,31 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) alSourcei(inst, AL_BUFFER, bufferID); } -OpenAL_Sound::OpenAL_Sound(SampleSource *input) +OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) { // Get the format int fmt, rate; getALFormat(inp, fmt, rate); - // Read the entire stream into a buffer - BufferStream buf(input); - - // Move the data into OpenAL + // Set up the OpenAL buffer alGenBuffers(1, &bufferID); assert(bufferID != 0); - alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + + // Does the stream support pointer operations? + if(input->hasPtr) + { + // If so, we can read the data directly from the stream + alBufferData(bufferID, fmt, &input.getPtr(), input.size(), rate); + } + else + { + // Read the entire stream into a temporary buffer first + BufferStream buf(input); + + // Then copy that into OpenAL + alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + } + checkALError("loading sound buffer"); // Create a source diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index a0b8ed4129..115c3c13e3 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -22,7 +22,7 @@ class OpenAL_Sound : public Sound int *refCnt; public: - OpenAL_Sound(SampleSource *input); + OpenAL_Sound(SampleSourcePtr input); OpenAL_Sound(ALuint buf, int *ref); // Used for cloning ~OpenAL_Sound(); @@ -83,10 +83,10 @@ class OpenAL_Factory : public SoundFactory } } - Sound *load(const std::string &file, bool stream=false) { assert(0); } - Sound *load(Stream::Stream *input, bool stream=false) { assert(0); } - Sound *load(SampleSource* input, bool stream=false) - { return new OpenAL_Sound(input); } + SoundPtr load(const std::string &file, bool stream=false) { assert(0); } + SoundPtr load(Stream::StreamPtr input, bool stream=false) { assert(0); } + SoundPtr load(SampleSourcePtr input, bool stream=false) + { return SoundPtr(new OpenAL_Sound(input)); } void update() {} setListenerPos(float x, float y, float z, diff --git a/sound/source.h b/sound/source.h index 0301f4bae8..f987dfe4ff 100644 --- a/sound/source.h +++ b/sound/source.h @@ -17,15 +17,7 @@ class SampleSource : public Stream::Stream bool isEof; public: - SampleSource() - { - // These are usually not needed for sound data - isSeekable = false; - hasPosition = false; - hasSize = false; - - isEof = false; - } + SampleSource() : isEof(false) {} /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index d99c6c4ee6..1a0dfe8dc0 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -109,7 +109,7 @@ AudiereSource::AudiereSource(const std::string &file) setup(); } -AudiereSource::AudiereSource(Stream::Stream *input) +AudiereSource::AudiereSource(Stream::StreamPtr input) { // Use our Stream::AudiereFile implementation to convert a Mangle // 'Stream' to an Audiere 'File' diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h index 1348e112c0..671595a1af 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.h @@ -33,7 +33,7 @@ class AudiereSource : public SampleSource AudiereSource(const std::string &file); /// Decode the given sound stream - AudiereSource(Stream::Stream *src); + AudiereSource(Stream::StreamPtr src); /// Read directly from an existing audiere::SampleSource AudiereSource(audiere::SampleSourcePtr src); diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h index 5317c11c02..d185098fdb 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.h @@ -28,7 +28,7 @@ class FFMpegSource : public SampleSource FFMpegSource(const std::string &file); /// Decode the given sound stream (not supported by FFmpeg) - FFMpegSource(Stream::Stream *src) { assert(0); } + FFMpegSource(Stream::StreamPtr src) { assert(0); } ~FFMpegSource(); diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h index 0b668fffa6..f1ebea2d56 100644 --- a/sound/sources/loadertemplate.h +++ b/sound/sources/loadertemplate.h @@ -16,7 +16,7 @@ class SSL_Template : public SampleSourceLoader return new SourceT(file); } - SampleSource *load(Stream::Stream *input) + SampleSource *load(Stream::StreamPtr input) { assert(canLoadStream); return new SourceT(input); diff --git a/sound/sources/memsource.h b/sound/sources/memsource.h index 1e38d90524..72624b899c 100644 --- a/sound/sources/memsource.h +++ b/sound/sources/memsource.h @@ -6,19 +6,21 @@ namespace Mangle { namespace Sound { -/// A sample source reading directly from a memory buffer -class MemorySource : public SampleSource +/// A class for reading raw samples directly from a stream. +class Stream2Samples : public SampleSource { - char *buf; - size_t len; - size_t pos; - int32_t rate, channels, bits; + Stream::StreamPtr inp; public: - MemorySource(void *_buf, size_t _len, int32_t _rate, int32_t _channels, int32_t _bits) - : len(_len), pos(0), rate(_rate), channels(_channels), bits(_bits) - { buf = (char*)_buf; } + Stream2Samples(Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) + : inp(_inp), rate(_rate), channels(_channels), bits(_bits) + { + isSeekable = inp->isSeekable; + hasPosition = inp->hasPosition; + hasSize = inp->hasSize; + hasPtr = inp->hasPtr; + } /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. @@ -29,20 +31,16 @@ class MemorySource : public SampleSource if(_bits) *_bits = bits; } - bool eof() const { return pos == len; } - size_t read(void *out, size_t count) - { - assert(len >= pos); + { return inp->read(out, count); } - if(count > (len-pos)) - count = len-pos; - - if(count) memcpy(out, buf+pos, count); - pos += count; - - return count; - } + void seek(size_t pos) { inp->seek(pos); } + size_t tell() const { return inp->tell(); } + size_t size() const { return inp->size(); } + bool eof() const { return inp->eof(); } + void *getPtr() const { return inp->getPtr(); } + void *getPtr(size_t size) const { return inp->getPtr(size); } + void *getPtr(size_t pos, size_t size) const { return inp->getPtr(pos, size); } }; }} // namespaces From c5316804b566cd905a5ecf621eb8fdc6c4a42c20 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 16:39:11 +0100 Subject: [PATCH 034/269] Fixed and tested all Stream tests --- .../sources/{memsource.h => stream_source.h} | 10 ++--- stream/clients/audiere_file.h | 2 + stream/clients/ogre_datastream.h | 6 +-- stream/filters/slice_stream.h | 14 +++---- stream/servers/file_stream.h | 2 +- stream/servers/memory_stream.h | 5 ++- stream/servers/std_stream.h | 6 +-- stream/stream.h | 6 +-- stream/tests/Makefile | 16 ++++--- stream/tests/audiere_client_test.cpp | 4 +- ...buffer_test.cpp => buffer_filter_test.cpp} | 4 +- stream/tests/file_server_test.cpp | 18 ++++++++ ...memory_test.cpp => memory_server_test.cpp} | 4 +- stream/tests/ogre_client_test.cpp | 6 +-- stream/tests/slice_filter_test.cpp | 42 +++++++++++++++++++ tools/shared_ptr.h | 2 +- 16 files changed, 109 insertions(+), 38 deletions(-) rename sound/sources/{memsource.h => stream_source.h} (81%) rename stream/tests/{buffer_test.cpp => buffer_filter_test.cpp} (92%) create mode 100644 stream/tests/file_server_test.cpp rename stream/tests/{memory_test.cpp => memory_server_test.cpp} (89%) create mode 100644 stream/tests/slice_filter_test.cpp diff --git a/sound/sources/memsource.h b/sound/sources/stream_source.h similarity index 81% rename from sound/sources/memsource.h rename to sound/sources/stream_source.h index 72624b899c..278f701e8a 100644 --- a/sound/sources/memsource.h +++ b/sound/sources/stream_source.h @@ -1,5 +1,5 @@ -#ifndef MANGLE_SOUND_MEMSOURCE_H -#define MANGLE_SOUND_MEMSOURCE_H +#ifndef MANGLE_SOUND_STREAMSOURCE_H +#define MANGLE_SOUND_STREAMSOURCE_H #include "../source.h" @@ -38,9 +38,9 @@ class Stream2Samples : public SampleSource size_t tell() const { return inp->tell(); } size_t size() const { return inp->size(); } bool eof() const { return inp->eof(); } - void *getPtr() const { return inp->getPtr(); } - void *getPtr(size_t size) const { return inp->getPtr(size); } - void *getPtr(size_t pos, size_t size) const { return inp->getPtr(pos, size); } + const void *getPtr() { return inp->getPtr(); } + const void *getPtr(size_t size) { return inp->getPtr(size); } + const void *getPtr(size_t pos, size_t size) { return inp->getPtr(pos, size); } }; }} // namespaces diff --git a/stream/clients/audiere_file.h b/stream/clients/audiere_file.h index ec67fd1b73..670b36ffae 100644 --- a/stream/clients/audiere_file.h +++ b/stream/clients/audiere_file.h @@ -4,6 +4,8 @@ #include #include +#include "../stream.h" + namespace Mangle { namespace Stream { diff --git a/stream/clients/ogre_datastream.h b/stream/clients/ogre_datastream.h index 23ab94fd4a..5369ed11a9 100644 --- a/stream/clients/ogre_datastream.h +++ b/stream/clients/ogre_datastream.h @@ -14,7 +14,7 @@ namespace Stream { to make your own modifications if you're working with newer (or older) versions. */ -class MangleDataStream : public Ogre::DataStream +class Mangle2OgreStream : public Ogre::DataStream { StreamPtr inp; @@ -27,11 +27,11 @@ class MangleDataStream : public Ogre::DataStream public: /// Constructor without name - MangleDataStream(StreamPtr _inp) + Mangle2OgreStream(StreamPtr _inp) : inp(_inp) { init(); } /// Constructor for a named data stream - MangleDataStream(const Ogre::String &name, StreamPtr _inp) + Mangle2OgreStream(const Ogre::String &name, StreamPtr _inp) : inp(_inp), Ogre::DataStream(name) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index a24a66e16b..d792b57d67 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -21,7 +21,7 @@ class SliceStream : public Stream assert(src->isSeekable); // Make sure we can actually fit inside the source stream - assert(src->size() <= offset+length); + assert(src->size() >= offset+length); isSeekable = true; hasPosition = true; @@ -50,13 +50,13 @@ class SliceStream : public Stream if(pos > length) pos = length; } - bool eof() { return pos == length; } - size_t tell() { return pos; } - size_t size() { return length; } + bool eof() const { return pos == length; } + size_t tell() const { return pos; } + size_t size() const { return length; } - void *getPtr() { return getPtr(0, length); } - void *getPtr(size_t size) { return getPtr(pos, size); } - void *getPtr(size_t pos, size_t size) + const void *getPtr() { return getPtr(0, length); } + const void *getPtr(size_t size) { return getPtr(pos, size); } + const void *getPtr(size_t pos, size_t size) { // Boundry checks on pos and size. Bounding the size is // important even when getting pointers, as the source stream diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h index bb4df4d381..012cb3a780 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.h @@ -17,7 +17,7 @@ class FileStream : public StdStream FileStream(const std::string &name) : StdStream(&file) { - file.open(name, ios::binary); + file.open(name.c_str(), std::ios::binary); } ~FileStream() { file.close(); } }; diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 23842e09bb..d77779878b 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -3,6 +3,7 @@ #include #include "../stream.h" +#include namespace Mangle { namespace Stream { @@ -71,8 +72,8 @@ class MemoryStream : public Stream bool eof() const { return pos == length; } /// Get the base pointer to the entire buffer - const void *getPtr() const { return data; } - const void *getPtr(size_t size) const { return ((char*)data)+pos; } + const void *getPtr() { return data; } + const void *getPtr(size_t size) { return ((char*)data)+pos; } const void *getPtr(size_t pos, size_t size) { if(pos > length) pos = length; diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h index 0e91ebb782..d4b56a9aea 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.h @@ -1,5 +1,5 @@ -#ifndef MANGLE_STREAM_FILESERVER_H -#define MANGLE_STREAM_FILESERVER_H +#ifndef MANGLE_STREAM_STDIOSERVER_H +#define MANGLE_STREAM_STDIOSERVER_H #include "../stream.h" #include @@ -41,7 +41,7 @@ class StdStream : public Stream { // Use the standard iostream size hack, terrible as it is. std::streampos pos = inf->tellg(); - inf->seekg(0, ios_base::end); + inf->seekg(0, std::ios::end); size_t res = inf->tellg(); inf->seekg(pos); return res; diff --git a/stream/stream.h b/stream/stream.h index 704abc88a0..309b0b33a9 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -58,15 +58,15 @@ class Stream /// Return a pointer to the entire stream. This function (and the /// other getPtr() variants below) should only be implemented for /// memory-based streams where using them would be an optimization. - virtual void *getPtr() const { assert(0); } + virtual const void *getPtr() { assert(0); } /// Get a pointer to a memory region of 'size' bytes from the /// current position. - virtual void *getPtr(size_t size) const { assert(0); } + virtual const void *getPtr(size_t size) { assert(0); } /// Get a pointer to a memory region of 'size' bytes starting from /// position 'pos' - virtual void *getPtr(size_t pos, size_t size) const { assert(0); } + virtual const void *getPtr(size_t pos, size_t size) { assert(0); } }; typedef boost::shared_ptr StreamPtr; diff --git a/stream/tests/Makefile b/stream/tests/Makefile index c86a7397ff..d95908afb4 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,21 +1,27 @@ GCC=g++ -I../ -all: ogre_client_test dummy_test audiere_client_test +all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/ogre_datastream.h $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/iwrapper.h ../clients/audiere_file.h ../clients/audiere_file.cpp +audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/audiere_file.h ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -memory_test: memory_test.cpp ../stream.h ../servers/memory_stream.h +file_server_test: file_server_test.cpp ../stream.h ../servers/file_stream.h ../servers/std_stream.h $(GCC) $< -o $@ -buffer_test: buffer_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h +memory_server_test: memory_server_test.cpp ../stream.h ../servers/memory_stream.h + $(GCC) $< -o $@ + +buffer_filter_test: buffer_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h + $(GCC) $< -o $@ + +slice_filter_test: slice_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/slice_stream.h $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index 01f548714c..dd4dfe4adb 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -11,8 +11,8 @@ int main() { char str[12]; memset(str, 0, 12); - Stream *inp = new MemoryStream("hello world", 11); - FilePtr p(new AudiereFile(inp, true)); + StreamPtr inp(new MemoryStream("hello world", 11)); + FilePtr p(new AudiereFile(inp)); cout << "pos=" << p->tell() << endl; p->read(str, 2); cout << "2 bytes: " << str << endl; diff --git a/stream/tests/buffer_test.cpp b/stream/tests/buffer_filter_test.cpp similarity index 92% rename from stream/tests/buffer_test.cpp rename to stream/tests/buffer_filter_test.cpp index 24bb7e0706..b6f43e99b1 100644 --- a/stream/tests/buffer_test.cpp +++ b/stream/tests/buffer_filter_test.cpp @@ -8,8 +8,8 @@ using namespace std; int main() { - Stream *orig = new MemoryStream("hello world", 11); - Stream *inp = new BufferStream(orig); + StreamPtr orig (new MemoryStream("hello world", 11)); + StreamPtr inp (new BufferStream(orig)); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; diff --git a/stream/tests/file_server_test.cpp b/stream/tests/file_server_test.cpp new file mode 100644 index 0000000000..1c25051587 --- /dev/null +++ b/stream/tests/file_server_test.cpp @@ -0,0 +1,18 @@ +#include "../servers/file_stream.h" +#include + +using namespace Mangle::Stream; +using namespace std; + +int main() +{ + StreamPtr inp(new FileStream("file_server_test.cpp")); + + char buf[21]; + buf[20] = 0; + cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl; + inp->read(buf, 20); + cout << "First 20 bytes: " << buf << endl; + cout << "pos=" << inp->tell() << " eof=" << inp->eof() << endl; + return 0; +} diff --git a/stream/tests/memory_test.cpp b/stream/tests/memory_server_test.cpp similarity index 89% rename from stream/tests/memory_test.cpp rename to stream/tests/memory_server_test.cpp index 7e7f34075e..b9f21b9b1a 100644 --- a/stream/tests/memory_test.cpp +++ b/stream/tests/memory_server_test.cpp @@ -8,7 +8,7 @@ using namespace std; int main() { - Stream *inp = new MemoryStream("hello world", 11); + Stream* inp = new MemoryStream("hello world\0", 12); cout << "Size: " << inp->size() << endl; cout << "Pos: " << inp->tell() << "\nSeeking...\n"; @@ -35,6 +35,8 @@ int main() cout << "Result: " << data << endl; cout << "Eof: " << inp->eof() << endl; cout << "Pos: " << inp->tell() << endl; + + cout << "Entire stream from pointer: " << (char*)inp->getPtr() << endl; return 0; } diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index 5301133622..a89589d7c9 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "../servers/memory_stream.h" -#include "ogre_datastream.h" +#include "../clients/ogre_datastream.h" #include using namespace Mangle::Stream; @@ -8,8 +8,8 @@ using namespace std; int main() { - Stream *inp = new MemoryStream("hello world", 11); - DataStreamPtr p(new MangleDataStream("hello", inp, true)); + StreamPtr inp(new MemoryStream("hello world", 11)); + DataStreamPtr p(new Mangle2OgreStream("hello", inp)); cout << "Name: " << p->getName() << endl; cout << "As string: " << p->getAsString() << endl; cout << "pos=" << p->tell() << " eof=" << p->eof() << endl; diff --git a/stream/tests/slice_filter_test.cpp b/stream/tests/slice_filter_test.cpp new file mode 100644 index 0000000000..ff384c1bbb --- /dev/null +++ b/stream/tests/slice_filter_test.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include "../filters/slice_stream.h" +#include "../servers/memory_stream.h" + +using namespace Mangle::Stream; +using namespace std; + +void test(StreamPtr inp) +{ + cout << "Size: " << inp->size() << endl; + cout << "Pos: " << inp->tell() << "\nSeeking...\n"; + char data[6]; + memset(data, 0, 6); + cout << "Reading " << inp->read(data, 6) << " bytes\n"; + cout << "Result: " << data << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << endl; + inp->seek(2); + cout << "Seeking:\nPos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Reading " << inp->read(data, 6) << " bytes\n"; + cout << "Result: " << data << endl; + cout << "Pos: " << inp->tell() << endl; + cout << "Eof: " << inp->eof() << endl; + cout << "Entire stream as pointer: " << (char*)inp->getPtr() << endl; +} + +int main() +{ + StreamPtr orig (new MemoryStream("hello\0world\0", 12)); + StreamPtr slice1 (new SliceStream(orig,0,6)); + StreamPtr slice2 (new SliceStream(orig,6,6)); + + cout << "\nSlice 1:\n--------\n"; + test(slice1); + cout << "\nSlice 2:\n--------\n"; + test(slice2); + + return 0; +} diff --git a/tools/shared_ptr.h b/tools/shared_ptr.h index 00af6084b7..da0b399bd2 100644 --- a/tools/shared_ptr.h +++ b/tools/shared_ptr.h @@ -1,3 +1,3 @@ // This file should include whatever it needs to define the boost/tr1 // shared_ptr<> template. -#include +#include From 9e332c40671f8db0abab3f889f558d7450d03fa3 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 17:06:41 +0100 Subject: [PATCH 035/269] Fixed and tested all VFS tests --- vfs/clients/ogre_archive.cpp | 4 +-- vfs/clients/ogre_archive.h | 5 ++-- vfs/servers/ogre_vfs.cpp | 9 +++--- vfs/servers/physfs_vfs.h | 2 +- vfs/tests/Makefile | 2 +- vfs/tests/dummy_test.cpp | 8 +++-- vfs/tests/dummy_vfs.cpp | 50 +++++++++++++++++--------------- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_resource_test.cpp | 2 +- vfs/tests/server_common.cpp | 2 +- 10 files changed, 46 insertions(+), 40 deletions(-) diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index 0def218db6..e9612d7d90 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -7,8 +7,8 @@ using namespace Mangle::Stream; Ogre::DataStreamPtr MangleArchive::open(const Ogre::String& filename) const { - return Ogre::DataStreamPtr(new MangleDataStream - (filename, vfs->open(filename), true)); + return Ogre::DataStreamPtr(new Mangle2OgreStream + (filename, vfs->open(filename))); } static void fill(Ogre::FileInfoList &out, FileInfoList &in) diff --git a/vfs/clients/ogre_archive.h b/vfs/clients/ogre_archive.h index 84f23eb8a1..22964dc35d 100644 --- a/vfs/clients/ogre_archive.h +++ b/vfs/clients/ogre_archive.h @@ -26,7 +26,8 @@ class MangleArchive : public Ogre::Archive bool isCaseSensitive() const { return vfs->isCaseSensitive; } - // These do nothing. You have to load / unload the archive manually. + // These do nothing. You have to load / unload the archive in the + // constructor/destructor. void load() {} void unload() {} @@ -34,7 +35,7 @@ class MangleArchive : public Ogre::Archive { return vfs->isFile(filename); } time_t getModifiedTime(const Ogre::String& filename) - { return vfs->stat(filename).time; } + { return vfs->stat(filename)->time; } Ogre::DataStreamPtr open(const Ogre::String& filename) const; diff --git a/vfs/servers/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp index 0fc051a8aa..ab496a1924 100644 --- a/vfs/servers/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -2,6 +2,7 @@ #include "../../stream/servers/ogre_datastream.h" using namespace Mangle::VFS; +using namespace Mangle::Stream; OgreVFS::OgreVFS(const std::string &_group) : group(_group) @@ -18,10 +19,10 @@ OgreVFS::OgreVFS(const std::string &_group) group = gm->getWorldResourceGroupName(); } -Mangle::Stream::StreamPtr OgreVFS::open(const std::string &name) +StreamPtr OgreVFS::open(const std::string &name) { Ogre::DataStreamPtr data = gm->openResource(name, group); - return Strea::StreamPtr(new Stream::OgreStream(data)); + return StreamPtr(new OgreStream(data)); } static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) @@ -39,7 +40,7 @@ static void fill(FileInfoList &out, Ogre::FileInfoList &in, bool dirs) } } -FileInfoList OgreVFS::list(const std::string& dir, +FileInfoListPtr OgreVFS::list(const std::string& dir, bool recurse, bool dirs) const { @@ -49,7 +50,7 @@ FileInfoList OgreVFS::list(const std::string& dir, return res; } -FileInfoList OgreVFS::find(const std::string& pattern, +FileInfoListPtr OgreVFS::find(const std::string& pattern, bool recursive, bool dirs) const { diff --git a/vfs/servers/physfs_vfs.h b/vfs/servers/physfs_vfs.h index f85cc54b89..49acc398d3 100644 --- a/vfs/servers/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.h @@ -27,7 +27,7 @@ class PhysVFS : public VFS /// Open a new data stream. Deleting the object should be enough to /// close it. virtual Stream::StreamPtr open(const std::string &name) - { return new Stream::StreamPtr(Stream::PhysFile(PHYSFS_openRead(name.c_str()))); } + { return Stream::StreamPtr(new Stream::PhysFile(PHYSFS_openRead(name.c_str()))); } /// Check for the existence of a file virtual bool isFile(const std::string &name) const diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index 4595c82eac..c9963cfa11 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -6,7 +6,7 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_PHYSFS=-lphysfs -ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/wrapper.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp $(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) ogre_resource_test: ogre_resource_test.cpp diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp index b65f383358..8d7fc7dd63 100644 --- a/vfs/tests/dummy_test.cpp +++ b/vfs/tests/dummy_test.cpp @@ -5,7 +5,7 @@ using namespace std; -void print(FileInfo inf) +void print(FileInfo &inf) { cout << "name: " << inf.name << endl; cout << "basename: " << inf.basename << endl; @@ -13,12 +13,14 @@ void print(FileInfo inf) cout << "size: " << inf.size << endl; cout << "time: " << inf.time << endl; } +void print(FileInfoPtr inf) { print(*inf); } -void print(FileInfoList lst) +void print(FileInfoList &lst) { for(int i=0; isize() << endl; return 0; diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp index e7c77a7533..7a39da9322 100644 --- a/vfs/tests/dummy_vfs.cpp +++ b/vfs/tests/dummy_vfs.cpp @@ -3,9 +3,10 @@ #include #include -#include "../../stream/tests/dummy_input.cpp" +#include "../../stream/servers/memory_stream.h" using namespace Mangle::VFS; +using namespace Mangle::Stream; class DummyVFS : public VFS { @@ -13,14 +14,15 @@ public: DummyVFS() { hasFind = false; + hasList = true; isCaseSensitive = true; } // We only support opening 'file1' at the moment. - Mangle::Stream::Stream *open(const std::string &name) + StreamPtr open(const std::string &name) { assert(name == "file1"); - return new DummyInput(); + return StreamPtr(new MemoryStream("hello world", 11)); } bool isFile(const std::string &name) const @@ -35,31 +37,31 @@ public: } /// Get info about a single file - FileInfo stat(const std::string &name) const + FileInfoPtr stat(const std::string &name) const { - FileInfo fi; - fi.name = name; - fi.time = 0; + FileInfoPtr fi(new FileInfo); + fi->name = name; + fi->time = 0; if(isFile(name)) { if(name == "dir/file2") { - fi.basename = "file2"; - fi.size = 2; + fi->basename = "file2"; + fi->size = 2; } else { - fi.basename = "file1"; - fi.size = 1; + fi->basename = "file1"; + fi->size = 1; } - fi.isDir = false; + fi->isDir = false; } else if(isDir(name)) { - fi.basename = "dir"; - fi.isDir = true; - fi.size = 0; + fi->basename = "dir"; + fi->isDir = true; + fi->size = 0; } else assert(0); @@ -69,13 +71,13 @@ public: /// List all entries in a given directory. A blank dir should be /// interpreted as a the root/current directory of the archive. If /// dirs is true, list directories instead of files. - virtual FileInfoList list(const std::string& dir = "", - bool recurse=true, - bool dirs=false) const + virtual FileInfoListPtr list(const std::string& dir = "", + bool recurse=true, + bool dirs=false) const { assert(dir == ""); - FileInfoList fl; + FileInfoListPtr fl(new FileInfoList); FileInfo fi; @@ -86,14 +88,14 @@ public: fi.isDir = false; fi.size = 1; fi.time = 0; - fl.push_back(fi); + fl->push_back(fi); if(recurse) { fi.name = "dir/file2"; fi.basename = "file2"; fi.size = 2; - fl.push_back(fi); + fl->push_back(fi); } } else @@ -103,13 +105,13 @@ public: fi.isDir = true; fi.size = 0; fi.time = 0; - fl.push_back(fi); + fl->push_back(fi); } return fl; } - FileInfoList find(const std::string& pattern, + FileInfoListPtr find(const std::string& pattern, bool recursive=true, bool dirs=false) const - { assert(0); return FileInfoList(); } + { assert(0); } }; diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index d38add4da8..542ac9c7eb 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -17,7 +17,7 @@ void print(StringVectorPtr lst) int main() { - VFS *vfs = new DummyVFS(); + VFSPtr vfs(new DummyVFS()); MangleArchive arc(vfs, "dummy"); cout << "Case: " << arc.isCaseSensitive() << endl; diff --git a/vfs/tests/ogre_resource_test.cpp b/vfs/tests/ogre_resource_test.cpp index eadb1153f8..8965adaa1e 100644 --- a/vfs/tests/ogre_resource_test.cpp +++ b/vfs/tests/ogre_resource_test.cpp @@ -6,7 +6,7 @@ This isn't really a test of our implementation, but a test of using the Ogre resource system to find files. If the Ogre interface changes and you have to change this test, you will have to change - the ogre_vfs.cpp implementation equivalently. + the servers/ogre_vfs.cpp implementation equivalently. */ diff --git a/vfs/tests/server_common.cpp b/vfs/tests/server_common.cpp index ff16ac7f2c..1834bc25a7 100644 --- a/vfs/tests/server_common.cpp +++ b/vfs/tests/server_common.cpp @@ -14,7 +14,7 @@ void find(VFS &vfs, const std::string &file) return; } - Stream *data = vfs.open(file); + StreamPtr data = vfs.open(file); cout << "Size: " << data->size() << endl; From 56f9daed9685e2b6f36b15c300f61cfc006030fd Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 18:42:35 +0100 Subject: [PATCH 036/269] Combined OpenAL+Audiere sound test now compiles and runs, but segfaults --- sound/filters/input_filter.h | 9 ++- sound/filters/openal_audiere.h | 4 +- sound/output.h | 6 +- sound/outputs/openal_out.cpp | 61 +++++++++++++++++--- sound/outputs/openal_out.h | 49 +++------------- sound/source.h | 4 +- sound/sources/audiere_source.cpp | 8 ++- sound/sources/audiere_source.h | 10 ++-- sound/sources/loadertemplate.h | 10 ++-- sound/tests/Makefile | 12 +--- sound/tests/common.cpp | 89 ----------------------------- sound/tests/openal_audiere_test.cpp | 49 +++++++++++++++- stream/filters/buffer_stream.h | 3 + 13 files changed, 142 insertions(+), 172 deletions(-) delete mode 100644 sound/tests/common.cpp diff --git a/sound/filters/input_filter.h b/sound/filters/input_filter.h index 17dda55347..2b4486b75f 100644 --- a/sound/filters/input_filter.h +++ b/sound/filters/input_filter.h @@ -40,7 +40,6 @@ class InputFilter : public SoundFactory // Set capabilities needsUpdate = snd->needsUpdate; has3D = snd->has3D; - canRepeatStream = snd->canRepeatStream; canLoadStream = inp->canLoadStream; // Both these should be true, or the use of this class is pretty @@ -51,13 +50,13 @@ class InputFilter : public SoundFactory } virtual SoundPtr load(const std::string &file) - { return load(inp->load(file), stream); } + { return loadRaw(inp->load(file)); } virtual SoundPtr load(Stream::StreamPtr input) - { return load(inp->load(input), stream); } + { return loadRaw(inp->load(input)); } - virtual SoundPtr load(SampleSourcePtr input) - { return snd->load(input, stream); } + virtual SoundPtr loadRaw(SampleSourcePtr input) + { return snd->loadRaw(input); } virtual void update() { snd->update(); } virtual void setListenerPos(float x, float y, float z, diff --git a/sound/filters/openal_audiere.h b/sound/filters/openal_audiere.h index 9a2bfcf8cb..a1b2e6b649 100644 --- a/sound/filters/openal_audiere.h +++ b/sound/filters/openal_audiere.h @@ -15,8 +15,8 @@ class OpenAL_Audiere_Factory : public InputFilter public: OpenAL_Audiere_Factory() { - set(new OpenAL_Factory, - new AudiereLoader); + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new AudiereLoader)); } }; diff --git a/sound/output.h b/sound/output.h index dfd1902417..6bcd932377 100644 --- a/sound/output.h +++ b/sound/output.h @@ -39,7 +39,7 @@ class Sound virtual void pause() = 0; /// Check if the sound is still playing - virtual bool isPlaying() = 0 const; + virtual bool isPlaying() const = 0; /// Set the volume. The parameter must be between 0.0 and 1.0. virtual void setVolume(float) = 0; @@ -120,7 +120,7 @@ class SoundFactory large files, but they are not required to. @return a new Sound object */ - virtual SoundPtr load(SampleSource *input) = 0; + virtual SoundPtr loadRaw(SampleSourcePtr input) = 0; /** @brief Load a sound file from stream. Only valid if canLoadStream @@ -130,7 +130,7 @@ class SoundFactory @param stream true if the file should be streamed @see load(InputSource*,bool) */ - virtual SoundPtr load(Stream::Stream *input) = 0; + virtual SoundPtr load(Stream::StreamPtr input) = 0; /** @brief Load a sound directly from file. Only valid if canLoadFile diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 9106901563..99b976bc9d 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,6 +1,8 @@ #include "openal_out.h" #include +#include "../../stream/filters/buffer_stream.h" + using namespace Mangle::Sound; // ---- Helper functions and classes ---- @@ -28,7 +30,7 @@ static void checkALError(const std::string &msg) fail("\"" + std::string(alGetString(err)) + "\" while " + msg); } -static void getALFormat(InputStream *inp, int &fmt, int &rate) +static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) { int ch, bits; inp->getInfo(&rate, &ch, &bits); @@ -61,6 +63,41 @@ static void getALFormat(InputStream *inp, int &fmt, int &rate) fail("Unsupported input format"); } +// ---- OpenAL_Factory ---- + +OpenAL_Factory::OpenAL_Factory(bool doSetup) + : didSetup(doSetup) +{ + needsUpdate = false; + has3D = true; + canLoadFile = false; + canLoadStream = false; + canLoadSource = true; + + if(doSetup) + { + // Set up sound system + Device = alcOpenDevice(NULL); + Context = alcCreateContext(Device, NULL); + + if(!Device || !Context) + fail("Failed to initialize context or device"); + + alcMakeContextCurrent(Context); + } +} + +OpenAL_Factory::~OpenAL_Factory() +{ + // Deinitialize sound system + if(didSetup) + { + alcMakeContextCurrent(NULL); + if(Context) alcDestroyContext(Context); + if(Device) alcCloseDevice(Device); + } +} + // ---- OpenAL_Sound ---- void OpenAL_Sound::play() @@ -81,7 +118,7 @@ void OpenAL_Sound::pause() checkALError("pausing"); } -bool OpenAL_Sound::isPlaying() +bool OpenAL_Sound::isPlaying() const { ALint state; alGetSourcei(inst, AL_SOURCE_STATE, &state); @@ -105,7 +142,12 @@ void OpenAL_Sound::setPos(float x, float y, float z) void OpenAL_Sound::setRepeat(bool rep) { - alSourcei(Source, AL_LOOPING, rep?AL_TRUE:AL_FALSE); + alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE); +} + +SoundPtr OpenAL_Sound::clone() const +{ + return SoundPtr(new OpenAL_Sound(bufferID, refCnt)); } // Constructor used for cloned sounds @@ -121,11 +163,12 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) alSourcei(inst, AL_BUFFER, bufferID); } +// Constructor used for original (non-cloned) sounds OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) { // Get the format int fmt, rate; - getALFormat(inp, fmt, rate); + getALFormat(input, fmt, rate); // Set up the OpenAL buffer alGenBuffers(1, &bufferID); @@ -135,15 +178,15 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) if(input->hasPtr) { // If so, we can read the data directly from the stream - alBufferData(bufferID, fmt, &input.getPtr(), input.size(), rate); + alBufferData(bufferID, fmt, input->getPtr(), input->size(), rate); } else { // Read the entire stream into a temporary buffer first - BufferStream buf(input); + Mangle::Stream::BufferStream buf(input); // Then copy that into OpenAL - alBufferData(bufferID, fmt, &buf.getPtr(), buf.size(), rate); + alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate); } checkALError("loading sound buffer"); @@ -165,8 +208,8 @@ OpenAL_Sound::~OpenAL_Sound() // Return sound alDeleteSources(1, &inst); - // Decrease the reference - if(--(*refCnt)) + // Decrease the reference counter + if((-- *refCnt) == 0) { // We're the last owner. Delete the buffer and the counter // itself. diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 115c3c13e3..335880cda2 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -2,7 +2,6 @@ #define MANGLE_SOUND_OPENAL_OUT_H #include "../output.h" -#include "../../stream/filters/buffer_stream.h" #include #include @@ -34,7 +33,7 @@ class OpenAL_Sound : public Sound void setPos(float x, float y, float z); void setRepeat(bool); void setStreaming(bool) {} // Not implemented yet - Sound* clone() const; + SoundPtr clone() const; /// Not implemented void setPan(float) {} @@ -50,48 +49,18 @@ class OpenAL_Factory : public SoundFactory /// Initialize object. Pass true (default) if you want the /// constructor to set up the current ALCdevice and ALCcontext for /// you. - OpenAL_Factory(bool doSetup = true) - : didSetup(doSetup) - { - needsUpdate = false; - has3D = true; - canLoadFile = false; - canLoadStream = false; - canLoadSource = true; + OpenAL_Factory(bool doSetup = true); + ~OpenAL_Factory(); - if(doSetup) - { - // Set up sound system - Device = alcOpenDevice(NULL); - Context = alcCreateContext(Device, NULL); - - if(!Device || !Context) - fail("Failed to initialize context or device"); - - alcMakeContextCurrent(Context); - } - } - - ~OpenAL_Factory() - { - // Deinitialize sound system - if(didSetup) - { - alcMakeContextCurrent(NULL); - if(Context) alcDestroyContext(Context); - if(Device) alcCloseDevice(Device); - } - } - - SoundPtr load(const std::string &file, bool stream=false) { assert(0); } - SoundPtr load(Stream::StreamPtr input, bool stream=false) { assert(0); } - SoundPtr load(SampleSourcePtr input, bool stream=false) + SoundPtr load(const std::string &file) { assert(0); } + SoundPtr load(Stream::StreamPtr input) { assert(0); } + SoundPtr loadRaw(SampleSourcePtr input) { return SoundPtr(new OpenAL_Sound(input)); } void update() {} - setListenerPos(float x, float y, float z, - float fx, float fy, float fz, - float ux, float uy, float uz) + void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) { ALfloat orient[6]; orient[0] = fx; diff --git a/sound/source.h b/sound/source.h index f987dfe4ff..d5468b5181 100644 --- a/sound/source.h +++ b/sound/source.h @@ -21,13 +21,13 @@ class SampleSource : public Stream::Stream /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. - virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) const = 0; + virtual void getInfo(int32_t *rate, int32_t *channels, int32_t *bits) = 0; bool eof() const { return isEof; } // Disabled functions by default. You can still override them in // subclasses. - void seek(size_t pos) const { assert(0); } + void seek(size_t pos) { assert(0); } size_t tell() const { assert(0); } size_t size() const { assert(0); } }; diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 1a0dfe8dc0..8da4fe3c7d 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -2,6 +2,8 @@ #include "../../stream/clients/audiere_file.h" +using namespace Mangle::Stream; + // Exception handling class Audiere_Exception : public std::exception { @@ -109,11 +111,11 @@ AudiereSource::AudiereSource(const std::string &file) setup(); } -AudiereSource::AudiereSource(Stream::StreamPtr input) +AudiereSource::AudiereSource(StreamPtr input) { // Use our Stream::AudiereFile implementation to convert a Mangle // 'Stream' to an Audiere 'File' - sample = OpenSampleSource(new Stream::AudiereFile(input)); + sample = OpenSampleSource(new AudiereFile(input)); if(!sample) fail("Couldn't load stream"); @@ -125,7 +127,7 @@ AudiereSource::AudiereSource(audiere::SampleSourcePtr src) { assert(sample); setup(); } // Common function called from all constructors -AudiereSource::setup() +void AudiereSource::setup() { assert(sample); diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.h index 671595a1af..e2d09d006d 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.h @@ -26,14 +26,14 @@ class AudiereSource : public SampleSource // How much of the above buffer is in use int pullSize; - void getFormat(); + void setup(); public: /// Decode the given sound file AudiereSource(const std::string &file); /// Decode the given sound stream - AudiereSource(Stream::StreamPtr src); + AudiereSource(Mangle::Stream::StreamPtr src); /// Read directly from an existing audiere::SampleSource AudiereSource(audiere::SampleSourcePtr src); @@ -41,9 +41,9 @@ class AudiereSource : public SampleSource void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); size_t read(void *data, size_t length); - void seek(size_t pos) const { sample->setPosition(pos); } - size_t tell() const { return sample->getPosition(); } - size_t size() const { return sample->getLength(); } + void seek(size_t pos) { sample->setPosition(pos/frameSize); } + size_t tell() const { return sample->getPosition()*frameSize; } + size_t size() const { return sample->getLength()*frameSize; } }; #include "loadertemplate.h" diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.h index f1ebea2d56..a27a77d106 100644 --- a/sound/sources/loadertemplate.h +++ b/sound/sources/loadertemplate.h @@ -4,22 +4,24 @@ template class SSL_Template : public SampleSourceLoader { + public: + SSL_Template() { canLoadStream = stream; canLoadFile = file; } - SampleSource *load(const std::string &file) + SampleSourcePtr load(const std::string &filename) { assert(canLoadFile); - return new SourceT(file); + return SampleSourcePtr(new SourceT(filename)); } - SampleSource *load(Stream::StreamPtr input) + SampleSourcePtr load(Stream::StreamPtr input) { assert(canLoadStream); - return new SourceT(input); + return SampleSourcePtr(new SourceT(input)); } }; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 8a9c748124..ca7a933c63 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,19 +1,13 @@ GCC=g++ -I../ -all: audiere_test ffmpeg_openal_test openal_audiere_test +all: openal_audiere_test -L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +#L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ffmpeg_openal_test: ffmpeg_openal_test.cpp ../servers/input_ffmpeg.cpp ../servers/output_openal.cpp - $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) - -openal_audiere_test: openal_audiere_test.cpp ../servers/input_audiere.cpp ../servers/output_openal.cpp ../../stream/clients/audiere_file.cpp +openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -audiere_test: audiere_test.cpp ../servers/audiere_imp.cpp - $(GCC) $^ -o $@ $(L_AUDIERE) - clean: rm *_test diff --git a/sound/tests/common.cpp b/sound/tests/common.cpp deleted file mode 100644 index 818d1645f4..0000000000 --- a/sound/tests/common.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// This file is included directly into the test programs - -#include -#include -#include - -using namespace std; - -class TestStream : public Mangle::Stream::Stream -{ - ifstream io; - -public: - - TestStream(const char* name) - { - io.open(name, ios::binary); - isSeekable = true; - hasPosition = true; - hasSize = false; - } - - size_t read(void* buf, size_t len) - { - io.read((char*)buf, len); - return io.gcount(); - } - - void seek(size_t pos) - { - io.seekg(pos); - } - - size_t tell() const - { return ((TestStream*)this)->io.tellg(); } - - size_t size() const - { return 0; } - - bool eof() const - { return io.eof(); } -}; - -void play(const char* name, bool music=false, bool stream=false) -{ - // Only load streams if the backend supports it - if(stream && !mg.canLoadStream) - return; - - cout << "Playing " << name; - if(stream) cout << " (from stream)"; - cout << "\n"; - - Sound *snd = NULL; - Instance *s = NULL; - - try - { - if(stream) - snd = mg.load(new TestStream(name), music); - else - snd = mg.load(name, music); - - - s = snd->getInstance(false, false); - s->play(); - - while(s->isPlaying()) - { - usleep(10000); - if(mg.needsUpdate) mg.update(); - } - } - catch(exception &e) - { - cout << " ERROR: " << e.what() << "\n"; - } - - if(s) s->drop(); - if(snd) snd->drop(); -} - -int main() -{ - play("cow.wav"); - play("owl.ogg", true); - play("cow.wav", false, true); - return 0; -} diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index 3036e02a2c..a39ae3d98d 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,7 +1,54 @@ +#include +#include +#include + +#include "../../stream/servers/file_stream.h" +#include "../../stream/filters/buffer_stream.h" #include "../filters/openal_audiere.h" +using namespace std; +using namespace Mangle::Stream; using namespace Mangle::Sound; OpenAL_Audiere_Factory mg; -#include "common.cpp" +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("owl.ogg"); + play("cow.wav", true); + return 0; +} diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.h index f04411f98c..d1af09a8ca 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.h @@ -10,6 +10,7 @@ namespace Stream { /** A Stream that reads another Stream into a buffer, and serves it as a MemoryStream. Might be expanded with other capabilities later. */ + class BufferStream : public MemoryStream { std::vector buffer; @@ -17,6 +18,8 @@ class BufferStream : public MemoryStream public: BufferStream(StreamPtr input) { + assert(input); + // Allocate memory, read the stream into it. Then call set() if(input->hasSize) { From f8d3a35cf878fbfbb9ef1f56e1c4e6143737cdef Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 19:48:04 +0100 Subject: [PATCH 037/269] Made separate tests for audiere and openal. Fixed segfault. Everything is peachy. --- sound/outputs/openal_out.cpp | 8 +++- sound/outputs/openal_out.h | 7 ++- sound/sources/audiere_source.cpp | 2 + sound/sources/stream_source.h | 6 +-- sound/tests/Makefile | 8 +++- sound/tests/audiere_source_test.cpp | 69 ++++++++++++++++++++++++++++ sound/tests/cow.raw | Bin 0 -> 37502 bytes sound/tests/openal_audiere_test.cpp | 2 - sound/tests/openal_output_test.cpp | 46 +++++++++++++++++++ 9 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 sound/tests/audiere_source_test.cpp create mode 100644 sound/tests/cow.raw create mode 100644 sound/tests/openal_output_test.cpp diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 99b976bc9d..e6d0b3ae7b 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -27,7 +27,13 @@ static void checkALError(const std::string &msg) { ALenum err = alGetError(); if(err != AL_NO_ERROR) - fail("\"" + std::string(alGetString(err)) + "\" while " + msg); + { + const ALchar* errmsg = alGetString(err); + if(errmsg) + fail("\"" + std::string(alGetString(err)) + "\" while " + msg); + else + fail("non-specified error while " + msg + " (did you forget to initialize OpenAL?)"); + } } static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.h index 335880cda2..2ec5936653 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.h @@ -21,8 +21,13 @@ class OpenAL_Sound : public Sound int *refCnt; public: + /// Read samples from the given input buffer OpenAL_Sound(SampleSourcePtr input); - OpenAL_Sound(ALuint buf, int *ref); // Used for cloning + + /// Play an existing buffer, with a given ref counter. Used + /// internally for cloning. + OpenAL_Sound(ALuint buf, int *ref); + ~OpenAL_Sound(); void play(); diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 8da4fe3c7d..8a6b57ada8 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -135,6 +135,8 @@ void AudiereSource::setup() int channels, rate; sample->getFormat(channels, rate, fmt); + pullSize = 0; + // Calculate the size of one frame frameSize = GetSampleSize(fmt) * channels; diff --git a/sound/sources/stream_source.h b/sound/sources/stream_source.h index 278f701e8a..42791ccdf1 100644 --- a/sound/sources/stream_source.h +++ b/sound/sources/stream_source.h @@ -10,10 +10,10 @@ namespace Sound { class Stream2Samples : public SampleSource { int32_t rate, channels, bits; - Stream::StreamPtr inp; + Mangle::Stream::StreamPtr inp; public: - Stream2Samples(Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) + Stream2Samples(Mangle::Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) : inp(_inp), rate(_rate), channels(_channels), bits(_bits) { isSeekable = inp->isSeekable; @@ -24,7 +24,7 @@ class Stream2Samples : public SampleSource /// Get the sample rate, number of channels, and bits per /// sample. NULL parameters are ignored. - void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) const + void getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) { if(_rate) *_rate = rate; if(_channels) *_channels = channels; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index ca7a933c63..75fb1e9e63 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: openal_audiere_test +all: audiere_source_test openal_output_test openal_audiere_test #L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) @@ -9,5 +9,11 @@ L_AUDIERE=-laudiere openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) +openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ $(L_OPENAL) + +audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) + clean: rm *_test diff --git a/sound/tests/audiere_source_test.cpp b/sound/tests/audiere_source_test.cpp new file mode 100644 index 0000000000..6dba37b515 --- /dev/null +++ b/sound/tests/audiere_source_test.cpp @@ -0,0 +1,69 @@ +#include + +#include "../../stream/servers/file_stream.h" +#include "../sources/audiere_source.h" +#include "../../stream/filters/buffer_stream.h" + +#include +#include + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +// Contents and size of cow.raw +void *orig; +size_t orig_size; + +void run(SampleSourcePtr &src) +{ + size_t ss = src->size(); + assert(ss == orig_size); + + cout << "Source size: " << ss << endl; + int rate, channels, bits; + src->getInfo(&rate, &channels, &bits); + cout << "rate=" << rate << "\nchannels=" << channels + << "\nbits=" << bits << endl; + + cout << "Reading entire buffer into memory\n"; + void *buf = malloc(ss); + src->read(buf, ss); + + cout << "Comparing...\n"; + if(memcmp(buf, orig, ss) != 0) + { + cout << "Oops!\n"; + assert(0); + } + + cout << "Done\n"; +} + +int main() +{ + { + cout << "Reading cow.raw first\n"; + FileStream tmp("cow.raw"); + orig_size = tmp.size(); + cout << "Size: " << orig_size << endl; + orig = malloc(orig_size); + tmp.read(orig, orig_size); + cout << "Done\n"; + } + + { + cout << "\nLoading cow.wav by filename:\n"; + SampleSourcePtr cow_file( new AudiereSource("cow.wav") ); + run(cow_file); + } + + { + cout << "\nLoading cow.wav by stream:\n"; + StreamPtr inp( new FileStream("cow.wav") ); + SampleSourcePtr cow_stream( new AudiereSource(inp) ); + run(cow_stream); + } + + return 0; +} diff --git a/sound/tests/cow.raw b/sound/tests/cow.raw new file mode 100644 index 0000000000000000000000000000000000000000..c4d155bbfb1caba2e41b1a97e01f29276c945c07 GIT binary patch literal 37502 zcmX}#`IBGQc^>$4E*jlHH-N_8SlJ+U0wlprBt=SuV#|_kt%>6$w#!ycCQGFEHjtq0k++n1Q)p^6%FxHI?^UFNHkJho`l{)4( zKf~PoR`N5q_`Qq{BXzF-w!hnJBmJDt6^E{LwR6+_*IoQQr;H4>>-xM?i~su6&~>H# z-q)^{+WWKAp;X_m-pRK*-^v=xNF6C7{4a10sbPmO7wEc;k-Ni#=Y+zj-A$ z>ia$)&kcGP3ys@V=hV4_pVGKh=J^>bxai+e9BseNa`015j-goNnpaT~-$TKXE?h%x zd$G#izxl1b(steU;jVc-du*Hymr8aLS~rT!X}isT+jVAngj$VTKKUspUB@e*@@+I! zYEly(yL-ETqfJGGdVjdr-S6+>*PU_~m+p7>aL=nHKX>sI2itG=hx?7AjcXOxXX%NT zLzB5icd}8Mw2wAUj1A*uyv`%fbL=a0pM!q$$$xI%d3}CaSSQK?Yb*U7_v)Gi=CiJk zXFe8A;LOc(bGS{yFc#l#4>#)FSq(&;j`ZCw_q~6s-0@p>GT_pDTp9^qQE9wiSQgg_ zt1IPJxzXJj?vcdsKNRZPs>f+!u77v9gG5$WaTF!{;U0OR2Q*}kzexhUohai?hePeT z(du@kIn|zq1$BX4jky}?Wv2r=*ayk)BXOa_sBes*?cn=O#}FlYWJQr5*N)5&zHjgL z6jIb-Bo@QVck1o_26|9%>l*C*(r~_B9A@gZa^263{yGZXx7*IUx675`V!h-#Lzy6( zBkpwv9d7xXEVtFqALg+Mq^MaScB)MH>;%~+lZ|r|Wl32wETj*mt$VbL! z<<@W=zq8L-jh#zBMyzZB`j3#4@OsSe6OAL`@9jtu&5h(umdRnF8+XOgGOwFU7skti zZjrs=Za=?UG%O&I3vip|^TY0jGqlx}tup!+Js#`llE>S*mvv@bQ_j~5O`Hi?&yB(<*45m_6x{fG=FSZR43i5*=y^JCQQ_YWs$X1 zavu#sb+SCtRu}%oPY@K=#})A*29%c98^dMyy*gZJYm593c6^IGd8KLH=&;mUYQ0AG zE({mSEfgH5cT6ZSgCz3NMg>I#}Zg2Ly^IeM~i zSWy>S(fBaGQH<;km&?U+*$yLRBRDWKob&0ccD!haMY1N`phb3cY&b=q?$?{7b*RhA zwtgGQjJEf9eaUq$lykUA-$-h>>|R9mYQ2>83@hs@J7BT+ziU1};|I@iWV&oFoBQdy zVOY-=c&G2n+VVhIjQ;3ho!5MB4rv>=qFd#3JvN-N#}5U+Hjk~C93CjE*o6qkfOk`6S9z+evYr_%hr(B9xA(rooF?J5=Y9%_2_Vc1T3V# zbKT=;d2{$h-Ct+en?*EgEP779ChKCdxTn5Uel)yN52N>^^-ueMTfSAlfU>20P=46` zWO(1o*JOWqU_ZWdZ8%zILLIw#DA(vu)zkIdaB(x9cnYJJu2jT2-cpr^;q>bZR)_wJT(4h79ehyUVk~boNu<9e!EDi|Mj1w5sRp zM`iEub~#KRCixh0$is8XnL!BV5i@&d&tn; zdYvpx56_mzhRNY@IpDKf%jbvB)(!Z0m%)U~*h18-C&|Wu#-A^L)c?!+t$ue|MTVD= z;`8O@^4;Ml^&n{<8y*-QE!&4_(s+Zs_TKO4`FVe>8J;Vjs}G~#k>SPq!?K^8KRN6i zrr6HG9llq;KcEQP!&b$Mu|SJy+|7Eb953&dpOsh3VXu9D_&3AvCmJK|>xpJ~QH=HgX;pfNYRJqBTZ7I9^KXIim zuo$t$GofjHrMy}X!V0jCYpyEm*@EDM%f53D(uvQXaHr4KXPbQNEk7tf#G93MS9zjr zh;|GwmOroGx2nmqj?65w=j#wrR`F4HVEDvce%bA3&vy;~WcWAb4~AW&x(o;F>)lJ` z=fj~Asa=Z~+v!cN_W^mBs?YboUB56)cBhB;yTd(uQ_r-$Tt~?jO$+};`PaiYSiq^K ziAU<+41ZZs2j>=ffA_GvZWl$&)MMmxu&>3zc68@a5)hu9uCJA!mV+exIj>CN-aVXo zY54o@7sCPeX^b6S$*0Ep7Q6XX_tx-gd8O`0o9W@}rxZpSRVG!vyJwJ$tSG$lCS{7rUw9>H0gvAGf{C>kpS#y1&EWJ+yCI zdDJep4UgmV4Ey-w8vS~q{BP*Kn?*WTKg^ZfcV9W>^DE27VOxE2_`Uka|F9R9_Qm>t z5C5&aO$zU_KWpobep9!qA3EAtmei%Z?kN6E^&9J&VKpAz>W!=PCE+?cuvU^)Kq54L^ZBr-p}V*pvMi>sR|-fb4Ns`cCM(4s&wFhf-I^fhWV`;f?02R4!^*Oy?(CngUmkvM_+1jafrPy_{D3s~u;tXp^y4?0#B@dVNPY1V!ND;{gHx4hgPq*=S7J3;*J5)^&b3U0A|!!Wm- zWLw7DK2Nc`|4)fkhToD{%MxvW#t+ z9+soP5~vL>A;$+v5AVT>ttF2;$=H{|88&`|T>QKGXJwD|E`mlDbW8drqKRwuq7^M7 zQ6rG_I9Yy%&zUz|140Bdpyat3|zj3%{`>97glg*18XB8HI{?_F-Lp zsC=gGCb7TCCeAA#bpP2--iRFYH?g93pw&WS??FiXMldm-^JLj>Cui!>a+1Y4*3I@GjHQ`SO2s z-yROJtXu15d$0VO=%u>}-Hh{*HzBX3zVo?ZlYJf;_SX-CFL+dbmK5G?SeN(aAzme4 zQ`WoF{nx{vrJQbP?T2*uRD4svl?8peJ_l!xu+j(E?gRCMa&S1#Cr|h;4RwzLJoePE z!(F$R)poM578?B?>wxRAq;T+-erI{CK0SQCeyQPy)8#wEzpvjW#gk<#X<0#%O1)HP z`a5NAH)bcj@2xM7_}&H_JYNpgL)LrK&Uqzw5XXrjuChh!Bq^RGzniRR4Qn|Pt5#lO zKM#rx77mMjYgO4vy5Pm(=I}wc-#0Eo=2yw(P*>8fQa-95_c!b+9C;WIcMe-{{Sw;| z#Bi7Y6~mRa{r2*C9Qj)L?co_d<+z>vyY8>pE;?5p9v%`01T_b@jf!S&mD_ZCCtZAu z#NaRr9vg0S6T^C#4BjiJx^q5rvmWn%*}cIEP0`Y|^zlJEUw|hs*Pruicu04I6(nO9 zzxT9v3!i?}4&}D$W#1g|IDYv9>%Gs%P7ki+zuW6}_r8TDuhx&n5Q!}3(v|7{30L|? z`68=a$jV>T|HulzO`4Y5$BxjdUKTU==r`v3H|>8X^tA(`nxUibmJ=+^l;|P0`b7O? zIL{Z|qYrQLGdJnsW-D4;SsvkkZ1x^{HQuex_q((9@>Kb?Uz}8uwlzlM&qUekjOtKJP?!O7~@2;rAbN=W0Ux%M`r~01d z+!#vX^?i0M9-**RqK)A>wqcdP?g#|@5l>DMvld!*&?-gPr0Ct@jdHk~>>noEi`m4n zAOb%A4f;D-Huh6p!|y$w)Q5fdJR}t_w~1}IRnCyH_s&Hm)d1lx}eyz2{ zySsbc;o72u`e8jmDsI^OS#epg+Em%-C-KJ>G=H<$hwDze+ZaCI5CiN8wb-Gz>pAz} zlSJ~DhBF;2!_FQdyFGrc6Ys8wq?QN!)p&5ed;*_~qe=W&zr1eY6~0K*A8V5Ga{cf1 zyRp#dHxZvG0d=Z7*wkrrF9p_Axy?hK`A5-XYI3!~O1d&(g*F zpyPwXbjPAK%n_V4Q&#jF!%ulb)^XScN1f{SmVMThNRbtxCD%p9XX)&jPRxaaOZ#PI zg*7|~!A0V(b>bkh7aSkowtM(>9_gXTw{?B5{#Ty;0*kVWFO9vP!Q&aSA{*BDx29|z zo?vBgk5BlU`XWt#gq_&XFvu@i&~vQic{{(&o39`X>-lBakUK;*u{chqWs4lW_jlPOBTVAM>Nm~igJzAb{uMK?UyKP-FWaJ4~zto8!#aHLV zHRJvKGTM#vw_+x?IyT@iG_z0iwXm0Gf%I=c;t$l%qsU@Zf0=wWS-3GT-v|8|3>{n^Si}*UoL++{B^fSdM{Lp+#C=Q zz%t}RzKhj+s(hwA1gFgUgLhFk5zM1d&I0RvYk14&SppfEWyA7rmD~wDSG&bIqcvyt1w32Qn%914oyOP*jQ z*Og6H^+K?6EpWyG*5wzn1!A~vnLWsE(4>!sk9h0>E?HhT@vD!}BA$RfIXb+DZ#UVB zB`sT(i2Ai5*j@gBrC;U>lYYiz2+y$xZ}G2mo}}LD&vyJnzou@Bu7~bqk>#n=y+}KD z*IJLp*3*Lf;Zb>X_)PT<89&Y*&ae$z&~E#%g*^Rs`2u^#ma>)~$|<~A-$aucvbd)I zG-@n|ha)6$AmLB4XD^Va*q!6B?xF4~ORx|%=ha)og&H3&FM+1(WZt%rfGz#%CLKrE z@;!8aC2wdKY}noY0n}J7wz^{R+GKjJ?m0f%k@!%~p=Mvp5r~o92TCR`XOn z$={E`Di2x9)Ab)&)%HFdJvAKfJ}Mvjfrsg*%(p5-C~=D)e-}!=i`(BQ|3vh;l1+e9*h&x+%_AH%K9n!-3IXxjAf4C;mEAKJlT zSRbE;=jyX|d8GV}2ok-md{v$5g9kRqTf%3o@A-U^EN!cgljYBqUt^!9(eeCndN|Hz zU-V7cKltd9$X=cnH6~)ctl%y$^+)}m)-NwMclb)P)pOK-F3Ya%E$`v?2<{J1qzps7+F#|Jd&uB2%y1fC z+C50YC7G5FhqE+z3ho={q1c5^G#8Z4e)Emx_sbviL#tTDz2!UguM+3+nH%JZQ;C>N z{)M*6TtZqwA^W=v{L9*I9_e~#IEsFo$_~-rDqd@G%Pybin#kGH*@G!;|}L%Um(oC6Vbhvfpr2|0F-X0xqI+JkSQw!aNbxWfAF%owx`e z?m|9WSdOpMKj{Cl*rXww#C~LZ2@i9bze?Ql80p_khgqC*aX8$)Kb#!YK3QBm!L#Y% z^O1G$s{+Z6UPbB18-K|Ti9&`KWL|%>pB8yuasR`t`~g|68Clj5Xd_h*-d_CI#pA(e zv1u|0tkONwmdJKnd5WJ5GC#$pzf?tBvU#U)VLCr-#|v_3o3n@R`|_aI)8W6U{?-zCx%3yIEgcmY<*_<%|sTie_kH_ zI-Byg9LhN>FJvM0B^h%FFPX3tvX?SC&@8;n-t&DjA?t^S#VDWV%E}Lh|JePX^zfY3 zO|waxQ0qx~q<9*#Ld)(Y50}-6t`^a;MWUabb$2f+U=89UW@PKwX!;#&zO1ali6@5V z?V_+MZ`V`2^hQ)%*sRji^*741Ss$)mmj^y3s(r2OXN^@;aPBUti^t~0yKDUgTB;hv z*IkqOPgUoh{Y*oH|EN6H@wTMsFNVLB;p2nZlEtK6mfzjxLG`Jm+;i6vnMifI;TDNn zX^)Tc{2Q8N%MGCZ&2o#*9_Q(G%5Qx#?J3ZOiGv33|Yp@ ztn*89ZL*;7z%tZX%M#BSZj$&L^tq}<%yj4M4Ff6q!SKD|o#rc6 zSpP~msT+zrQ zDLJ77bqNhq6G6W_@;UMx>T4@y=kf_iNk;m8{&sGi=wl_9mX+-Ah+Os!_1$D*kCDV< zsoDq>j{Tpz1rgDGLVke}W>rhTkTs0}1#~ z<%euRBz=|q3d+>8<&)$Ctcb-=zRnKgQTQtF@m;LV7kyf^$ddk*C~BrG>{oQl;kX@E zxe6|oWq~9=65}7IS37wk@iLDj5{dNi!>8pxZ?b0R$_c*hDnE3``mfU2Mb@6$?YO$< zC_Tuh)KwuiRW*`%HojJ-V2Ty`0V^2qKMI8`t?OknQ^8VkAkV>9r|awd)f@4ZS{^3VIh`hU zcN^blc+Foy1Otpq9+U5drXGet=aXO6M831D*W}PI+u_2xgiPO}ZQJ_iS=Q5)4=jh# zTE>k|r4|8E$URM#M}|k}!c2L&{xvH(L$l|pa9*>r=j%TqKdWRm_SWyn?|#3-e^g#8 z6B7)z%4d_wTP#jaoHS$SpAQO1Tt`yp4j`@nPenEO^DRE@F82)iUl)*Vhzv!O&D+&~y;V=9eCHGU5zja2 z9?SZ9<%?b^H0=btJ|;F8Q&Aa3Q#tfmlqi#Uj+M++Z;JNdCVYv%Cp&h#CN`3Hsq3-gO?pm{{dBZku@9M6R9?;pY=TyTl4b1d`Fvr^`Ldw2#I_rU zWo+bLnj7kDp;4*Z-*Js-&>~(ki0W!X%|9=#o{6FpG-Mke+cF1@cPf!^&@8s3ALRS7 z`XEfQ6b&G8dyHI(o>(1K5Xj>Qi=8~hPN-;mP+xhUe%!KyJ7hI6$%JTqJ&LZ7%UCS7 zTtJqR7rDjje?rsm%I?n7Gw=bcrIy1RZ9|t&xmN@CX;O4XK8hfb?lF}>uga+U1#;j zv*IRcN3BPuid5*pLBVOU&2+05k|Vkn`8Ylt?M|Ta1sGCzsUfKrMd%U@2vM2 z9>-h0ufDJH@^*{HmZI%OcT|fIt+OlS>hFhdH@tk3Hcg`UR{HV?1h)Z_-^kX^)c071 zVR!--S2ab8!(HOwQP&cWS?BS3m~_oStK;|(Pn)dWGI^K8`64GCYm6mL)`E7zo=};5 zz`5b9|MkHlt`mQQGID!|`$gR|K}an71-UxeJC-4IUlMz$D)>#fJ<-r>B=3jnefwDV zyQ*6|hNo(951$FiT7wLI7dz*@?Il&hM1lJgO_2Q^GzI^L6DTR$;-;}Q?@Ahxk)Zi!UraqbQ zDV_l$#>XR~m``i)XePrV^ScJNTL!a?<90NlkUSQZPuS4Uk)t>(nv2z0%W{Y_t>+-_ z%nYy*4zKFT$^i1>I{2a=t5acz;`hMt4`)1^r7eam8NWr=mV_sqfj*zSavY@QC)lbu) zb;A%>rvswi)!q#auR>HWb>xU zm0gn~Jw!9E%jU@CWJUNr#UA;#DF14AL$)bWvPtd{ipKNPP{c7gvSeCNF7}soS7EaI zf$y2kbrpJfk{rqM<7#5^krun`Vof%%Lzn9T6(U)F8hn-3#5&&OkGG)g19c5t{xEwr z&dN=Sq>~A_hO@VQHpp!YtqLmoBypMVrP6r6QDHu>7`fKT1a-`nS$qmk*a3@|j>VB* zPuB5&%0Qi?jgViOQxEZfh6`VQu(x?RI}nK4qU+ro~n;rE%wm`Baxzi9}TuQT=0*_Nx5Qdo8yvhGk1Ppbcw@!fRb&HMu#waQ1M>-t$l?|bTr z?tBF;K8h_N=c`nc7sEkgxHbXrPQbShq^8sS&Ut7+)ezNw9Q@rsI{aoV1_?dQ7bGjA zRx8Vu=<5Q%aEVR7hO$eOtCTHJ-|QcvrI-5ss$4HYD)MhlhgS5P%M-EJY`~JTnm)~k zmlo7bGUm&m)hjIX`*mMvB#WQC_O(GjrheN?vM;Fz%l=h85U^J3oPp|A@@?utBzpsI zAMgG$C#EwXh!`_?8}ONQWM2P8*JmF1!4f|K2Azuaq}f?yO0j7?vE+_=+{gMs2mvYj%2# zFGx=NWBNA9639%}XNKq1%jU5;Zw}utf3G4Slg!h#jsq4i9%f0Snf@-`?Yh-J4jUwM zaNM0w;qB*Dhx5057MZ-4yU0GC5DCkIixrndYrsL7EwZ|`%6RI#zhUKj#WXL{*!SDM zC-~dnXdPzPpz~AKKOv8lnDzu2WmVzc;I_pO-&(j_7fDu9q5JZZPMGh*x2Nzd^h(7! zG2#f1J@o4udPpWc_Ps@B?9#==>amITm}(?@PjAF`vZD9ZdLcruFIM>+bmUDyAu2U> zgFCJikzA`E$dSIJQvPF=0C5%U|CK7|*&nW{$Ob*gv9Rc8dEn#|FP7^vTnprR9&n{q zw0fK9V<~+6K{~9&7SY6@=Cx&Od_k$=p0)D+n^+!ws_gX|-(8-*ohCKg3Y&}9$8`&R z%7Kl{(;TpwORVNRJQk%l?7s&FUttawREzO zcC(a*h4wRIt)o_dJg>5RYgoPwv~f{vPt%KZd;PfVp^Fz&X{2dtk8t6L`)y4Yqby~2 z?~V&C^pcuweH1psR*z+x`+;o$o8ZEoAsBFt2kU0LRSPmJk9T=8nZo~kz60< z2|tpL-Y<4Z?sGfjy*ZYrJWMw(b+3s^zdyV#19(z4HnPLlL}&1SYuT+I4+zV`<|_`+$5E%D8mUIY%*A+X??%B zK3*T|AA)eB7jKHV|4t%@k1pT~>El569CWlh+BIU?R@b+9C> zcshAMh(Y%%E9bMi>X+g_3Zj?ZIx*)eTGo(mTcJpPfNoQnq-oC#-yFVD<;P%f`(d-D z>21-S@Dm;(@vlq5*H1R>-jF}tUAC-ydH8Gj%zeDZx#4_wr(c9tzcoC`0L zWY!W4(`Y+rZ;qPP&=FxTeZIF8XdPHd1lBf@!QYE z^0FB$-2-|mzgVA;r8-jHL5IoyL3TTLzJcpk)zhF8$X^svb>zVEv+FueYk!`e9OyQu z9Yn@xG1af>9)OP15vY4cKImRQ>Qus#7B5|wjk`d%7O?HV7i{5+)Sj6u3vHZ^zE54_3|o4gccQJZq?V<3;Ogoggb%@`e6upH3Mh-@ZTi+g)-VL_fS$s?NY`PC$su z^_9pSts>9r!2A%2Ek{_CQ+RfyKOkDuCroxWh*njo{H@bLKl!cTdsxj6{-*jVGN+4= z!wa(VIy%la>c2>?uF}R?+aiP6B55%!+XK0(u_WqP1V61N(>j>Euk)H89+7K{4F9CQ zA&a6y8Of*LQ&zd0l}}Hgo<2Pad?4Nxn916ci3rtH0cXTci|Q`(z+d;-!9@HnU$@BgwvZIcFwb$X%BCFJLk`LmYE3{|-ABohXkt^#1 zR+Gqe1M0_)$i2zWq?0a{qMSe(gO=pr`w8^PNuou#w-(h>qfQ6?UB7iK*>Q4mOE&*B ziCa{kDbJV3_@L7CZw|@1M6Pl^>Ygg*Vb!El;iGK&(@rLAY}6m<`Rg!WI;ZKswOpI! z5L*~zo}MLrhS3Z50e<9l(}R+#rYu zRMJGt`OxGOmeZ7FFp@qOKC;vsPVCH~0fnyXL?LaeThM8GdA`^`BN|ebs;NjpNy$Tr z3`Mr~bH%4dTXrXP-g$Mt?sWQ?Y+n7II@+H8O!8Q4=9bPBw#%`hcnq+Q7h}b zP}KH#d0wvai7JysHq!m|8qfb~(1?{yLWzs(68DhJAtiD_Rschx8OfmCs!QQ{4N^#T zegukHCi^eCrJsB0&`DCBJS*gcHrH6rt4=MA>gCu5MaoVDukwoNoBjaO{HyxIx?dcb zYTY&{DE$IC`Lht37RmJSq^S~4kkKF$dHS}p^bwqLM^(I5`*1>%y}b)-F6`I&q%Jd@ zKjS>gI9ja4f%H4;5%!(G7o(gKc@jS~+g5+gHFYv|G9~D+g?z{EC%1QDxFWJS!>`1a zJksx~n@M3Rf^w;7rv}1WrP+x?S$Z^DHjuf>673O(UXbU*Mw_jnC5lOZ8dTw(@$1Mdka+>xaSjA{bR2a+?@ye!n<4TJ1JEr?)m)Y}ZPz z{&e{j+P4VbN+Z?Nf=Jdytsa!!Tt5ocF;m~PbO}{dnxP(W3TJNHsYaTK?I7|9o>VL-fU$+XCHF};p z1(fXj$(^2L0pxJ$QYw3?((5G@fgI!|hB7x%r5$foyV!`Ht&cKi0_d%bh`l@0=o`ss z_d2K^(94`|&*h=M=tTuhe|c(Tdg$2={NdNqle&uRMn6xL^vWj2SqKYlWuJ8spnvUd z;NB6@-Yam|Ij6;0r*@4|{f)Gm9n1N}`MtWN2sasRF$3f>((*&6Ae7nuAzkn+x?C1Z zbfnpU^zxU;vd`b8(N4?StDgCOm5k2<(Hq~vAAP_Ae>W!y;HUxpQngGU>Kf9&8V%#Q zPpMlZ^2`a;7QH~W&!VNe>M-Lx#7_rjOYC14tX+v=N!7hF!t0NsVIt2J!v*Q!oPfF% zgu>1yW1IdB83y!S=KQSlhGf|}kW}_@G}$7Z5j=ddaZoB-a7x7HeLngIne`I1wLsLF za~YRZfIiZ@lejS$XZA!boFb0Woi0zHJCYPh==G!SO=6NqET0|JeXQTv?_e_=JrUU? zrK#|_gNyn^q-G{67wb5cr{1aKFZ#~^>**l3ssAsb?@yMm z4S!r9axqBG10HFy{rAiMWRhCkzs{YeAfgTEkjg&n#v+O}QEx7eEETmo&r3J%HBWRv z9!joE)S;Tp?qw}1M!uVT`H|sZr?vvG>I)LvtIhCa=~yI5P9MBFyn_Dp6>UY(a#g#s zLsXaVju!JjY#?dNdergCZk=@SF}YAD8{X=i&P>&l{4an+7whU6OcuBgSEydFXs21S zwayjftPP&J(_RSw3o}7;iR63F_BZE{B(Mb zqW{I_HGZIf{wJjDE^D{6o9>;DZQg1G3^TVt-_9q{Y&?OJS#=wv@OAOf7gU|x%^nV* z|BI}S&UI49L+~Z*++`z;56%!fw77bah^w**lXCg&0KUB|200~9?7U>C$4caix^`H5 zsDSm?1y1@-RXtaB%Hp{0BYIsR8Sf^yaG0n6AE^egcEP{f#IZZYXzABoASRA8&#Awh z`)joaU0Gi$-;}RPrusBn`bOEOv&jhuHpgiveeDZSA{m`jh;*U{%aH!f=y?ln80i+E zcTR7qCk@v~-Bpp<{Fa4F9^+7uCabVAyGP-}@N3RErwe>BAGH<=PR8x3*Y}o}hQI8- z&0g!LkWENUb_qYbl%|gJUYW2k!m3<=2w6+kbQi4i&zz`xD#(j$>`{Yxy(VMbVjvVy z^`iDU{ho;%COV+4Z@TgT%iiR%%*yC*mkZq)*}jW>m+nAVto69JD+X&$k=CXCV2u~p zeYQoMw5G~QvAn5Zt#Xw+L0T$*Iz_u+ ziaN$5UfS!d$g8qr$6_O0Bj;L{M4o9_I#Bca!=t{QE^0j~kXr2UJ$7%OZh%k)qJY5H>JeClYYcp#{5t|*+8h+=b{WK4J0Khaaq_T%+Q z7UX5}7p%&<$-tz;><;86SK+g&FFe{fimlh-O^=!d$W)43vT$c&QTTvV*Orq!SyPmH zx1{qbk{lU803N?wCCNp)N;n@%N{ zD;nZgrsZu`k%_Bla1>9VheX5Z^Qqk0H{Qd1v{vl7G=3d5)$L`1-z=|I(E-G?R%U2R zeTe?9<7pR(6(X;xmK>rf=?8YUr2LEWEpdg@RNg(#_Wp(ZqB9_&IQlpxRmS^yG*T(z$&!v%fP{81{JWMmzljIs}BR3=$Y{;XL8 zcJ`Wc&)3_t_AC+(RPx@p$ zS9Is^R0MMdV@!nz4u@r{%m(|z-Z}?N@0?W;XH2m!`X`6mIzeZhZnj$^-R4W^_ZspuC65w> zb=$piruHshjML74{a*P4_GeQ(DC_xh{jna18SA1`<1bqy_C614?|Ea=jcET(bq3u2=9-gxy-eV5wCC*=TC z^lp=XRS}uFxpc1eepm%LK(B#4NPmS+H>;8F4*m7=|E6qrx)aaZ|KFvqMgr4067&%3 z6A501664Sg+(tXoXO`&W4lkz*-|DZCjl>}1tgZ@@O2E*xH+7=q636?{KUI*KW;>_L z&Mwi!yddbJNu^KCS+z&)SJ5S*ca%H&4#g*&v!{ zUXyD<`D_n{uq94acDGY2g=aDuWR%RYJ?bb~{j`c`PJt#Pdbyry++XUI*PRHK4%oYN82f)g#Xn4H&mxz^`gW2U%?vdBti0_nCPT?^2DNzgc-c6hkrJGyTA znJVkZ&L;9m*5=uwPuzVoWhCPEnUBp(eKO(-19ZwHcPQzWc8kQT^ zlTl2(zi1ev^_TG7IWG8RTnxUVUx7d7j6jLe!_{mIZt1E$rZOk`KZP`f8FMj&X{c?mI?Lc zquI`uH&z&F5~!Xn%eaWIphs}Sk?v4`sDpfEshn`?7NS@>bl0$o>6TfM`_rfsJoE)I z4ZImTjSkIx_4fsHB#w(KCA|nk+C+Vi#1Lk;1PS>D^DXYTGOY! zQ@733p5F>2>SR>Umc&ZcgypP_|25_hs&r&x%+ z;*y)J@o$x{lbm#dhJHt$nIRZ$bTP6nY{ashQSrO+={92vz4S zd@{7@M_gZ@iN;%#{waMU>GYZ}*Ic@z&Q>1cbH{l-Il5Lcl+iF>(qtnR>sUiwPSwNN zcgXriarFi|vrUBeWZ9gE4>#Y@&!Ki@Rn9;-4fhRJe>1dr9m0Lr?41wc+{6@eWIV+U zI7zNic0D;8G~xkF?rO9@D=#;fH*;0?U5-;mYyw>qU$mJeP*nO}AY9Rn{z{lPU26;R zbxNN?dRLb5CMG1ZVyIztd!77zBdFi&`nq-9EP|hQ4WG+gBpxDNnmP67glzvrm-=6- z`z`nBZU%CrxWQ9O~2D#U$-GKYasUTg6(K?BN|18`+`7 zvONu*(dHxKj6F3o3a;~5TgvCGUOexebBPYEVcZ@+@+m#$u9m3+t3(s&0Gv`uh8l8h zK5lPKbvqT-tFo6SR`QX{to|$df*xzVs=3LVM(|Y4YPC;BO zfzwv_IF5^5a?3$^=E;ZdEDgC$n?*2kwtDG9&1X*Uk96MJTFvJ(s1cIERmd^Of0nh44vln`Fjb(Sh8E)0GY>9vk5a!nolJ?{?do^Y zj5Yibia^t;6{qK6?`&V1ICza7qUEln+QFwJi=RBOdK zS-mRc-^viZVc*W6!FW#$+qws!lzDV#8C#v4+k-fzq6>H$l=gB|{|uE(5DPiK2(Pfqli@Am{f(i9HKY<<&^f2VGoR$QOclu#hVd3l zongTf31xa;I$})EGFKh7e_jR zO(GQ}hoAg_Cxn8XXivYZUJmja1hoWzK!;x?u8+M3qn4KTS-{4H9PdttGn_y0{wzcO|)-JR0 zrug6_f3~Ckmf0T9z$ob`i{1qzp5_aGC4NdT-bxa)wcnboHQkI9pXTxQ)B3mN=igW9 z&_~Y`hz;PVseVWItjZmmXP2g%k`5hlIqzomx{`vxoa}NMT&CzOU#piKRZPZIInIs} zMI24EE$+<(j?4m-_11~#jQwo=x}o#T)-jg^`e;*nSdR3(EycS_q+*}5esYg!kc>eh zY*{ZE?4030D|$LYXMc$f@!NM%>jrx238F`tbFv)XGLMy{MHkHsWchZ%?hXHv%w(AE zlE2s^cvp5KaYyXsZSN*0kof>#HOcEi@*eA`3pF*&^!vi*nSL||9sUM;x}B$)i>K-0 zh@UtPgXrktZzK0x_}SGhW|m__w{*6AP&1b<5ms{JYZASS%SPJj(F3LyEvX|W8+n!o zdB#M+hPTXGXS$qen(klFrA0nPSF&7WCX?LE)Wole%#7(i)(57GoV7Nigg49i z(I-(hr&n)@922(%-@OOlzk~XzSgoP8UtlG4w$qBcB+OnLy0!ch9#a7-_rY6Y!O zC62vwTB)Ay57Hg|95T!4Uyw~=^AjzblGE;)3Clr0q6OKHmEF_)O0o`O9RBwTdy^@= zGDnehcEZ_ae7p-OWrAN$O|Msp$=T4%?pQ`{W64jc2<>r_|AUqlkT2pFVrr*#-;pDY&l}x=RJ~a6K$`48qLGEBzRzv*b)$i8qTV%N4r$gbRUT+Vlf9c=XYB_(>$`Nm(~5qHw<~uJiFHW(dwDdqjl&x#{<33 zvnZkd;?s;?GGD1;cjD&F-Dk{G+2k^p$o2i$a(+1o^M&F0YJ!2k@6pe5WZ-E1L_B&C zop~rSl+)OQt_3*6p3<>FrtSC|YpSP9yml4(`9$UIqavba$ylXK2%~kGp%VSOi%YlI zsN+t%B+_kXRcN4e1)k4kg-2K(^9xWu*NAl+O-wES#NIpO@mw`M#^0AwvWtcBnVdj^ z)9MZgIn#qPAL2dIdoYnLnvBRo&UTs2P4N?_*VLzI&@Rzmax=ODXzxv3n@%>>*M^^& z@@5ik{o3%2AVTy!O6N~l*Hztwi9YbsbRN0*+D_#(1h|a99H-UrzjG-%^xNvq=Ync< zx6RrKlMcpvoqoZAvWv;X~rnA}Y z^Z2+ES1;*zR1ISJPosrdMKYlW!%>swzTWRv%QVkRymm$fH=W<6PT|ved7c~cnerrh z<;LYrlk0_`Y53vJbT685PX2ma7piz1dRH+GqNYh~*rz=xH^t1!`t2&aGaJEYA=3U7 zoBXR-KR#VAJS>}>stk`SsoT>w=Wlf8Ond#jlU&JeBom%2-CKJ7-gh^ zcvrk0?>C2sPZwjdBxlHCDtKl|(WsTF8lawxxL)gYV>{i!I_u7HE+DfP%%~ubrZbcM z)9}_Qe7w>SVeH#C>T~2YXBv}P$n55i*_vdW6WO7b&brL7(6><@G2@s0f)#XNseWaK zs?+ei_nujMZ_?k>^&9dm&yol+Bh&@CI45$4W{E7&j0EByT6UvWnfh|N81ZoUrS-qv z>GdGFi&&#{J?V~iE@gW)yMi@8t{TRMtMw+k@f6ShkKr!ec+pZ=>0Mc&eKK33E0K7z zftjsm(h-WMX1NRo*5^ixogCI(s{X^;%|I0&n>P10UkB^Rbj23W=wmiXmNm}P5@e{@ z1zt_8*|QbN=EN4rnPehuuBcyIjA_4exG1*7c^|XH^mohJTcwlwIqENACRiu2FD^PoQ8$`X zx6yw$`>W8&cWY*+1v@$wS)MV0;16QsR2*)&+spN*x*+5S=u_sD%YV97GS`{Uk+Y3X zN67}Hsu>z3Vws1R>acV(6=HKtL|~7hX3lizez&tkm#XDFcqZv3*PCTxz7SHxmzTxaJn|Lo8se+ctv0qom+U4r^S){}I|AM#f zvxe}8&dS;O>dQLa4039!H~rJ;uJvy0ccy)fs%~ZO*fA&uWnteZMRPnZ8>gxnJ;clF zR(FZbp6RZe|C64O%1*0-;rbf=%5;hL{H<6;6Q}Xet@Fq`UCW%UB`cCda1NLvfW}ArhkBhHNB7;PG zS|i%yRZNMOx%o1@@EG~hT`5nV4))Z&lX=f%Fd0p|xh_{V)ARkqyQUXqs$-@QsDi@) z)BQuN`7YOxoBP!2?1PR|)x6Xl><_Uix9!t(jo~Q>@QKiaEPZ6=*9ErDX*C!I^0n$a zR=iq9AX$aXU`Q3_CU5g$_g4Q7v@Lq*JQD-nT2*xK;QHEbLp#ZPif?&KmFEz?rxvUW zOO$1f7rAz78g8E_v-(llM*?*Tu^6cluIRUvN1*!`1`~?J!tuA6jgkps$wCnWaWw4d ztX$;&37MwFCNa*=nlde|&y<`yEK(xWwtjcx_t~pnPB5Jn^7C?cSL#JIey8GiU9)P% zW~=FS&c#gQW-6EZH27wxLAn?1fdv*xKj!q@gDp$9t?q=0ujzdB)B$wQDJr@nbDGmd zU#Wixhi#TIxB>g;Y|1P3<(gc8GcEFQnVrjKwodCvQ!0J)AenRZT|Vt34Sl?dy&$P{ zm|Sv&^d+YvQR)Zf{Z{+CYW)XsKRqTgFZ#?zNacuVKxK*dn4O}T-g+J3WJI3~AL5LZ z*kKKvn`~0@1$%O`$~C6MF(!hD>X+Kv7u9={Pivj`G(9u;oeW}~(wE(}_3wtQhRh(t zJ)IlwkPexk(HXJ(SNdPK2VEmpbCee#n=pZFHg4P|SHabKal|U=GGBpmWB$IMyx%ZK zcjpy4bxvnWrqV{s)ph!fBs^IX4)G}(bnNP4QbH4hBWKs!4#KTCEZ&ncOy?~8GwUCVMk zYjcv7j^w0|SRPf>(!w2jIhfU&tT7KGnvahDR5YKb4di6QXZ`3&LzBCCj!R-j`f;8I zOIA)N8M~1uLODV2#Md9Pw&pas&t(zYyY+kh>?F#ZWXQ>WI>7SB&m4oilZV;OI?uz^ z*HI-MF}YLnPNoOJ9?1eurqA0%ro_Uw!i7C1d($4O?;g6ePV1+>UhqicF@<7?Kz zKD(=vSkMGs^?F;&%Y)ghIM+_in=Z>Q(#&P1Y<)(m|o()|u;_z>x(cSjQhpE$s7O$RH20MjKo!5&eWbVW!e#D&Dr( z9eg?4U8b=o;vHJ>(AqPxron1ppQJLviHrC+dt{#9 zhU?S&Qnt0;w*m_W;lx6((}!jni8W?kgZUOtZ6^0dw}(gjuao zpnyC*CsVO_=kj~v^kgs8T+!`2>O>;=T%RQ8y6Fd>?Zl3s@DvH2jBKg?{}+C6hTVOt zJT05ShS7?{T89zhfLu!k4hE@UrDv|_JQf97x}Qi%Oz)iZx7*-?2YJ|MYH z*%UOJM+;7G_Upj-kFDKu@z;EQ^wp zX-?+gYi7i4w2z?25jN!KnLVv@%j-mDof$C-(NW`w@Cc< z1)pGTOPgz*OeNXPbd;PDo6Quo_&ip|oo4F^YH{cpDLhh>w-X80Jl`i>2PRcD4Co18 zB3?6MX+WFjA`%&JM_*hz!0a#2F7x1_A#>t3v&^1^;f{2XOx)2s((2;FA<~iOk$EOt zs^@j9_Uld)%WY4)ffA|CPzCDiqE;bWv-L#Z|3Yk*ktE;CdAX91vT?l77p|D zCpImz9^Omr_@=Jh1L_p~5uanylenmzjHEXQROZjpu~cn0IqN)+pKH&%bC;li8)TE0 z<)!#sep~IH#Xnu&G!y+CN)G6&w}O@Pq)xdtex<6~)tvHcr;>QK?=%e`5)qO1OvpN+ zqdli=Z;+8>Kh-68Tsm$~rUr;ZlZoPg{U_(7)4iQ8znq3J8-(7P;|A5~rAY40JMYT! zi|F|4HR$d1duIX+F00L18M~{aM9l=J6Kg%0IY}()7%6^34)jV88(upt(&(CsB^~3- zO>AWklGQs(KYjw~rh6q%a@gLyY$l#$nr+VccM~(&#BjBDp<3!gLK?-mm~FEc1QMxd_REm zJXW8~m=jyBdY=4VW@!@t=~PLC2)j8ooXlGEMDB18Om|RbFHc%a|L8J5>BwIY`_m$~ z(O@lE#v z^UzQA8+p9wRJD4DH+pvvcRBFSgyOOOiek-9{%etHg)W66ZmJXt{1kOj@um{A;2 zkzz-d;_6NL1s#<*JrWO5AII0!`47hvi?}a#dA??%yI{R}xZQRl8>Kju} za>wM}GqWzw_fY+7Yd9+FP4@dlfohRtC;fUi<%#y7mx+>S9c-6Qp=7g@P4?6m5|cd2 zk`{es$`~XG~+#kt*9MeKYTPT2M~o z<#}_Y%WBTlcOgKzvepA+DuQ0!SlmmA*XkbV`}6F^Kt5lQm%d2?oZYtmwVpzu-jnB7 zvS^-d!9E^r>+o*&Qoc4@*@1`l=DT>XZrGj}hCVy>PmeMW(^(y{TW~yype=W3YN`fK zGx0oT)pVD6o_qYG8ADd5I^7REVxrB^=c;HS#qc+O|>Bv1NEcjb(Z(ws}pCr`0h>93#1OXa*zPr{NAx3| zdZm+Vc^*~lUaA(!Yp#MA)XB)gmE_pr0VrE$FlRoJCp0mp&cFtFHe~i7Ql>xY+16VD zp?r|a7_YvoO)@jb%h}7;kMpTV&s2^vo`-7y1FRfhotl=(WHMZNj;kCqilQbzqc;p1&vPfzH<_G>r}NYmee%S| z%ZD;K;=u9=$;@1jDLnKH%%HGjKFK;uPNy?v^*&qrq*|H$IH^3Mx9bLUoZPMFud$r* z=y%*VnHAGnNW_!Z5QNfEit)Mw`*c%rr9UYm9b;kMdUo>Q0S z&|YK9kB~kyroBI!m?B=ObsMIN-k#gZPTZv1_hE?4)=Mq(293^Chddo{t!#XHTds(d zNhYiRu4$*v^rL?CCYT|eqOl{XQ0K&IPVkOd%?*``Lp)+^_@ljv8ltEmyL39=u&X>3 z2Aa0oimN&Gn{TesQ)HGAiCV!c!)H9-Box%Q>Z0}MUV9I4RF&zd-OpqtzcJ#!w zQ1W&HH+10I`58F>NO)|0D`G?3-xGyh=M2wq+|NmSP1SR(-yzp2OOjfK4&vaeNJshz zGFA8U%zsgs3k#|0pk(4kDA4Ac>sLSOZUxe!nl#Z6P;+cR6>Q{;>g_ zbIv4Bcuh}@IEQ43b4W;h-G&yuQf7}w_tb;oZ1TzMtY?on@2p?djFLQ8MSm2!IbF-f zoUbReK@dou*O^>)riurPO?IgmUFfc;eWhn6ST|hrT<#XnWTL@* zpGggk<)Z0_W5Y?l2*KYucbSg1yppM@3wYwp{X8S5a7(r-y)mg=CYp@@&a~FdMbJ4S z_IKhUSwPX=PWCpLIM}syV_g!7KnN{nSwtUlsuRwFf%2@~)oO|+xz^M!%t$jISk9Z~ znWyJWQF802od@Y-7tu9cIV*i@N&GR}lV^j4UuFhE<#|T+tYpd^bW1}smT)^OF)E%` z+s(;DXh1X@NfiHXP1aQg4#M{QWL@s$)x>+2(32)n_9_WZv6iS#c0=-?$L#G2!_|LDK1&ab8L+)4DWiulb}btGr%|GBY1ppHiq49NRLSp1UV< zzHh(DInNg}c?yLId~i7qi1o=o`C)U?J(g#(+-^1+((3bcbI)(8@6=bs8tHiwulk;6 zhg*#&d9yIiyr6?qc{<+M4cEzJ671ytWHm24%-r`#y=P+cL7vRrBJ^l}^2V8kc~g!ib+x7kvhLe!W`xaS zCp=F~MJF@BZqd%1k@Orb+OkS)zsMTWWs?j{&}!zN@tZ7#8b7-}DvzF711DK#^CbB# zGl)GAU6+D>$LP1tJ``+{&nHK&M^VVjti%GmUBJ^dX}7v)o0E7gN^!0{_%qsNkLhyG z91BlZ7kS82b*a&hHP3a4Z^#n>o0fX?m7V2;F#@we$P%5$=o!QAO{O(jbm3lcad{~Tnz z9eUP<(&}h);FLW3o4S&))FVWM{x{0<$|#FRvJSOjc!1aDJw(v>cCFQ{oK?3%r>j^S zCu?!?8tZNnS<@1-6bj;ZrWLMa@8ZcNtI%`-JCjMFOVdfii>6blc5~T}Tq~#ZMc62s z9;?hbWKE!6_*lf9UdT+bYtQ+Bzk;+Z+y&AncTavTeM2M7qHb%LV-C;cJQJ zISHO8bVE}-;T-*cndy=IaprH_k_AqedrouF!0ZXuHU~Dbb#~2%oB3i*=jjVd>pu$y znn&K(i>=qHd8ND7Gy}XcHN=y567ppFw92E?0ISHX?k5Z}; zHc5$HjpL#3xQ=`$zlG=~K_veX70ZNJcBgs{BkPsSO6UGo_#G{@zkCPFmAjRM*XqDFFj#*1* zkHH+HqS0i+F^@wvM1|kO!_f%3OBrvPvk>0CCaQ9 z*4dLk+s?<$I7vqxI}>zfFR+|D-+_Qc+oUDW`fGh0%>vAGS6gXz?Pic2`bReAChsR= z2rdi-tj_nsF?hdc#n~~~9W4_p$+e?>P^XR~>&q_UC;8EaSZ}q{a_RjEMz;<>PCt>J zbck8IE?boURL^|JxAM#;clSA(Z?~kf`Ktl%qv5#=tBn7180e<&Y(SG&D_{jaUA0~{ zD@n}3)|)&Yes>*vXlJp-a?-skSl#Mah&++R+(EI0{Bk~lHt9Q6g@bbE z(Wi2`W!}QQeA{(%eLJ(a$Ya(s z?k=#l72L93`^Fvmt>!qlpW|-n4UQM1D^A(V=gJs`Ua1ho2Soyc<*WmZtp!n8Cw4Hs zV40wuE99B5@r&2_pv;w;Jz1C;YN3X{L8s%**TXAl8qJ`(+|Q1)+em;+l(ku?okSiR zpR6P@pMAjzxwU&Fa$2M){h&2%d_YNAT?*R(l1 z$_zvJoYba1lhZkg$h~9Tvz`0s7L?oQmd~U&ShkqwffVpA>o6{>w{$;J(xASj4_372}9Iaf9y(Pab zK5hsn+`*QZk6F$&B_WUL^IN{>AA649@+^XAFbj}= z&v@leZ3L{I*}IvNd6%8e#KYL?d^UZdnX??rE9b$MoB8+;)NCscbw8zaD4*Z||NGAV z-ByyFeM*n4DXVG}@SRwIw#Hb4W)X7NW@DTuZT4pN?pjx@!cP3;zp?(acbwg|ZO2AL za^?UPk*a1fW^G3o3(|P&oybmwA;P`9n%_cC|As$)4;9^LmXZRNNQc+#uhBU4&0X`y F{|CY68e9MX literal 0 HcmV?d00001 diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index a39ae3d98d..9c49356a26 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,9 +1,7 @@ #include -#include #include #include "../../stream/servers/file_stream.h" -#include "../../stream/filters/buffer_stream.h" #include "../filters/openal_audiere.h" using namespace std; diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp new file mode 100644 index 0000000000..11b2fc42e4 --- /dev/null +++ b/sound/tests/openal_output_test.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include "../../stream/servers/file_stream.h" +#include "../sources/stream_source.h" +#include "../outputs/openal_out.h" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +int main() +{ + cout << "Loading cow.raw\n"; + + int rate = 11025; + int chan = 1; + int bits = 16; + + cout << " rate=" << rate << "\n channels=" << chan + << "\n bits=" << bits << endl; + + StreamPtr file( new FileStream("cow.raw") ); + SampleSourcePtr source( new Stream2Samples( file, rate, chan, bits)); + + cout << "Playing\n"; + + // This initializes OpenAL for us, and serves no other purpose. + OpenAL_Factory mg; + + OpenAL_Sound snd(source); + try + { + snd.play(); + + while(snd.isPlaying()) + { + usleep(10000); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } + return 0; +} From fbb77478c0005185e667a495495ceb57f3abf866 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 19:52:37 +0100 Subject: [PATCH 038/269] Updated vfs+sound test in root/tests/ --- tests/Makefile | 2 +- tests/ogrevfs_audiere_openal_test.cpp | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index ed680f3dba..d912c0784b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,7 @@ L_OGRE=$(shell pkg-config --libs OGRE) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/servers/input_audiere.cpp ../sound/servers/output_openal.cpp ../stream/clients/audiere_file.cpp +ogrevfs_audiere_openal_test: ogrevfs_audiere_openal_test.cpp ../vfs/servers/ogre_vfs.cpp ../sound/sources/audiere_source.cpp ../sound/outputs/openal_out.cpp ../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(I_OGRE) $(L_OGRE) $(L_OPENAL) $(L_AUDIERE) clean: diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp index 810fd635e4..3e267c98ae 100644 --- a/tests/ogrevfs_audiere_openal_test.cpp +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -7,7 +7,7 @@ */ -#include "sound/servers/openal_audiere.h" +#include "sound/filters/openal_audiere.h" #include "vfs/servers/ogre_vfs.h" #include #include @@ -32,21 +32,18 @@ int main() // Ogre file system VFS::OgreVFS vfs; - Sound::OpenAL_Audiere_Manager mg; - Sound::Sound *snd = mg.load(vfs.open("owl.ogg")); + // The main sound system + Sound::OpenAL_Audiere_Factory mg; + Sound::SoundPtr snd = mg.load(vfs.open("owl.ogg")); - Sound::Instance *s = snd->getInstance(false, false); cout << "Playing 'owl.ogg' from 'sound.zip'\n"; - s->play(); + snd->play(); - while(s->isPlaying()) + while(snd->isPlaying()) { usleep(10000); if(mg.needsUpdate) mg.update(); } - if(s) s->drop(); - if(snd) snd->drop(); - return 0; } From cdca68a36800e02d99fe23fc81a075c4c2177865 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 20:18:11 +0100 Subject: [PATCH 039/269] Fixed up FFMpeg input +test --- sound/sources/ffmpeg_source.cpp | 5 +-- sound/sources/ffmpeg_source.h | 7 ++-- sound/tests/Makefile | 7 +++- sound/tests/audiere_source_test.cpp | 1 - sound/tests/ffmpeg_source_test.cpp | 62 +++++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 sound/tests/ffmpeg_source_test.cpp diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index af785eaa35..372d1766df 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,4 +1,5 @@ -#include "input_ffmpeg.h" +#include "ffmpeg_source.h" +#include using namespace Mangle::Sound; @@ -52,8 +53,6 @@ FFMpegSource::FFMpegSource(const std::string &file) std::string msg; AVCodec *codec; - empty = false; - if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) fail("Error loading audio file " + file); diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.h index d185098fdb..2b33a32226 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.h @@ -1,8 +1,7 @@ #ifndef MANGLE_SOUND_FFMPEG_H #define MANGLE_SOUND_FFMPEG_H -#include "../input.h" -#include +#include "../source.h" #include #include @@ -28,7 +27,7 @@ class FFMpegSource : public SampleSource FFMpegSource(const std::string &file); /// Decode the given sound stream (not supported by FFmpeg) - FFMpegSource(Stream::StreamPtr src) { assert(0); } + FFMpegSource(Mangle::Stream::StreamPtr src) { assert(0); } ~FFMpegSource(); @@ -40,7 +39,7 @@ class FFMpegSource : public SampleSource #include "loadertemplate.h" /// A factory that loads FFMpegSources from file -class FFMpegLoader : public SSL_Template +class FFMpegLoader : public SSL_Template { public: diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 75fb1e9e63..79c2ecaf81 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,8 +1,8 @@ GCC=g++ -I../ -all: audiere_source_test openal_output_test openal_audiere_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test -#L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere @@ -15,5 +15,8 @@ openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) +ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp + $(GCC) $^ -o $@ $(L_FFMPEG) + clean: rm *_test diff --git a/sound/tests/audiere_source_test.cpp b/sound/tests/audiere_source_test.cpp index 6dba37b515..1631e89cba 100644 --- a/sound/tests/audiere_source_test.cpp +++ b/sound/tests/audiere_source_test.cpp @@ -2,7 +2,6 @@ #include "../../stream/servers/file_stream.h" #include "../sources/audiere_source.h" -#include "../../stream/filters/buffer_stream.h" #include #include diff --git a/sound/tests/ffmpeg_source_test.cpp b/sound/tests/ffmpeg_source_test.cpp new file mode 100644 index 0000000000..294565fb12 --- /dev/null +++ b/sound/tests/ffmpeg_source_test.cpp @@ -0,0 +1,62 @@ +#include + +#include "../../stream/servers/file_stream.h" +#include "../sources/ffmpeg_source.h" + +#include +#include + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +// Contents and size of cow.raw +void *orig; +size_t orig_size; + +void run(SampleSourcePtr &src) +{ + int rate, channels, bits; + src->getInfo(&rate, &channels, &bits); + cout << "rate=" << rate << "\nchannels=" << channels + << "\nbits=" << bits << endl; + + cout << "Reading entire buffer into memory\n"; + void *buf = malloc(orig_size); + size_t ss = src->read(buf, orig_size); + cout << "Actually read: " << ss << endl; + assert(ss == orig_size); + + cout << "Comparing...\n"; + if(memcmp(buf, orig, ss) != 0) + { + cout << "Oops!\n"; + assert(0); + } + + cout << "Done\n"; +} + +int main() +{ + { + cout << "Reading cow.raw first\n"; + FileStream tmp("cow.raw"); + orig_size = tmp.size(); + cout << "Size: " << orig_size << endl; + orig = malloc(orig_size); + tmp.read(orig, orig_size); + cout << "Done\n"; + } + + // Initializes the library, not used for anything else. + FFMpegLoader fm; + + { + cout << "\nLoading cow.wav by filename:\n"; + SampleSourcePtr cow_file( new FFMpegSource("cow.wav") ); + run(cow_file); + } + + return 0; +} From 63a3ad5e905a951ce40a698a933981d06ecee5f9 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 1 Jan 2010 20:41:19 +0100 Subject: [PATCH 040/269] minor Readme work --- README.txt | 84 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/README.txt b/README.txt index 2052d7d7b6..0fdc19b050 100644 --- a/README.txt +++ b/README.txt @@ -8,40 +8,47 @@ Documentation: http://asm-soft.com/mangle/docs -Mangle stands for Minimal Abstraction Game Layer, and it's meant to -become a small set of generic interfaces for various game middleware -libraries, such as sound, input, graphics, and so on. It consists of -several independent modules, one for each of these areas. These may be -used together to build an entire game engine, or they can be used +Mangle is the project name for a small set of generic interfaces for +various game middleware libraries, such as sound, input, graphics, and +so on. You can imagine that it stands for "Minimal Abstraction Game +Layer", if you like. It will consist of several more or less +independent modules, one for each of these areas. These may be used +together to build an entire game engine, or they can be used individually as separate libraries. However, Mangle does NOT actually implement a game engine, or any new fundamental functionality. More on that below. -Currently there's modules for sound and streams / archives (file -access). More will come in the future (including input, 2D/3D +Currently there's modules for sound and streams / archives (virtual +file systems.) More will come in the future (including input, 2D/3D graphics, GUI, physics, and more.) Main idea --------- -The idea behind to provide a uniform, consistent interface to other -game libraries. The library does not provide ANY functionality on its -own. Instead it connects to a backend implementation of your choice. +The idea behind Mangle is to provide a uniform, consistent interface +to other game libraries. The library does not provide ANY +functionality on its own. Instead it connects to a backend +implementation of your choice (or of your making.) The Sound module, for example, currently has backends for OpenAL (output only), FFmpeg (input only) and for Audiere. Hopefully we'll -soon add IrrKlang, FMod, DirectSound and Miles to that. It can combine -libraries to get more complete functionality (like using OpenAL for -output and FFmpeg to decode sound files), and it's also easy to write -your own backend if you're using a different (or home-brewed) sound -system. +add IrrKlang, FMod, DirectSound, Miles and more in the future It can +combine libraries to get more complete functionality (like using +OpenAL for output and FFmpeg to decode sound files), and it's also +easy to write your own backend if you're using a different (or +home-brewed) sound system. -Regardless of what backend you use, the front-end interface (found in -sound/sound.h) is identical, and as a library user you shouldn't -notice much difference at all if you swap one backend for another at a -later point. +Regardless of what backend you use, the front-end interfaces (found +eg. in sound/output.h) is identical, and as a library user you +shouldn't notice much difference at all if you swap one backend for +another at a later point. It should Just Work. + +The interfaces themselves are also quite simple. Setting up a sound +stream from FFmpeg or other decoder into OpenAL can be quite hairy - +but with Mangle the hairy parts have already been written for you. You +just plug the parts together. The goal in the long run is to support a wide variety of game-related libraries, and as many backend libraries (free and commercial) as @@ -66,24 +73,27 @@ you in many ways: The Mangle interfaces can help you keep your code clean, and its user interface is often simpler than the exteral library one. +- If you want to quickly connect different libraries together, it + really helps if they have speak a common language. The Mangle + interfaces are exactly that. Need to load Audiere sounds from a + weird archive format only implemented for PhysFS, all channeled + through the OGRE resource system? No problem! + - If you are creating a library that depends on a specific feature (such as sound), but you don't want to lock your users into any specific sound library. Mangle works as an abstraction that lets - your users select their own implementation. My own Monster scripting - language ( http://monsterscript.net ) will use this tactic, to - provide native-but-generic sound, input and GUI support, among other - features. + your users select their own implementation. - If you want to support multiple backends, or make it possible to easily switch backends later. You can select backends at compile - time or even at runtime. Maybe you decide to switch to to a - commercial library at a late stage in development, or you discover - that your favorite backend doesn't work on all the platforms you - want to reach. + time or even at runtime. For example you might want to switch to to + a commercial sound library at a later stage in development, or you + may want to use a different input library on console platforms than + on PC. The Mangle implementations are extremely light-weight - often just one -or two cpp/h pairs. You plug them directly into your program, there's -no separate build step required. +or two cpp/h pairs per module. You can plug them directly into your +program, there's no separate library building step required. Since the library aims to be very modularly put together, you can also, in many cases, just copy-and-paste the parts you need and ignore @@ -94,15 +104,15 @@ come crashing down, because there is no big 'system' to speak of. Past and future --------------- -Mangle started out as a spin-off from OpenMW, another project of mine -( http://openmw.sourceforge.net ). OpenMW is an attempt to recreate -the engine behind the commercial game Morrowind, using only open -source software. +Mangle started out as (and still is) a spin-off from OpenMW, another +project I am personally working on ( http://openmw.sourceforge.net ). +OpenMW is an attempt to recreate the engine behind the commercial game +Morrowind, using only open source software. -The projects are still tightly interlinked, and the will continue to -be until OpenMW is finished. That means that all near-future work on -Mangle for my part will be more or less guided by what OpenMW -needs. But I'll gladly accept external contributions that are not +The projects are still tightly interlinked, and they will continue to +be until OpenMW is finished. Most near-future work on Mangle will be +focused chiefly on OpenMW at the moment. However I will gladly +implement external contributions and suggestions that are not OpenMW-related. From 5e70bc7bd768b9fb6016c02656841d199cbb3759 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 2 Jan 2010 10:24:58 +0100 Subject: [PATCH 041/269] Added semi-automatic test scripts --- sound/tests/output/audiere_source_test.out | 21 ++++++++++++ sound/tests/output/ffmpeg_source_test.out | 12 +++++++ sound/tests/output/openal_audiere_test.out | 3 ++ sound/tests/output/openal_output_test.out | 5 +++ sound/tests/test.sh | 18 ++++++++++ stream/tests/output/audiere_client_test.out | 9 +++++ stream/tests/output/buffer_filter_test.out | 22 ++++++++++++ stream/tests/output/file_server_test.out | 3 ++ stream/tests/output/memory_server_test.out | 23 +++++++++++++ stream/tests/output/ogre_client_test.out | 5 +++ stream/tests/output/slice_filter_test.out | 36 ++++++++++++++++++++ stream/tests/test.sh | 18 ++++++++++ testall.sh | 13 +++++++ tests/output/ogrevfs_audiere_openal_test.out | 1 + tests/test.sh | 18 ++++++++++ vfs/tests/output/dummy_test.out | 31 +++++++++++++++++ vfs/tests/output/ogre_client_test.out | 12 +++++++ vfs/tests/output/ogre_resource_test.out | 20 +++++++++++ vfs/tests/output/ogre_server_test.out | 11 ++++++ vfs/tests/output/physfs_server_test.out | 11 ++++++ vfs/tests/test.sh | 18 ++++++++++ 21 files changed, 310 insertions(+) create mode 100644 sound/tests/output/audiere_source_test.out create mode 100644 sound/tests/output/ffmpeg_source_test.out create mode 100644 sound/tests/output/openal_audiere_test.out create mode 100644 sound/tests/output/openal_output_test.out create mode 100755 sound/tests/test.sh create mode 100644 stream/tests/output/audiere_client_test.out create mode 100644 stream/tests/output/buffer_filter_test.out create mode 100644 stream/tests/output/file_server_test.out create mode 100644 stream/tests/output/memory_server_test.out create mode 100644 stream/tests/output/ogre_client_test.out create mode 100644 stream/tests/output/slice_filter_test.out create mode 100755 stream/tests/test.sh create mode 100755 testall.sh create mode 100644 tests/output/ogrevfs_audiere_openal_test.out create mode 100755 tests/test.sh create mode 100644 vfs/tests/output/dummy_test.out create mode 100644 vfs/tests/output/ogre_client_test.out create mode 100644 vfs/tests/output/ogre_resource_test.out create mode 100644 vfs/tests/output/ogre_server_test.out create mode 100644 vfs/tests/output/physfs_server_test.out create mode 100755 vfs/tests/test.sh diff --git a/sound/tests/output/audiere_source_test.out b/sound/tests/output/audiere_source_test.out new file mode 100644 index 0000000000..47a5a9e418 --- /dev/null +++ b/sound/tests/output/audiere_source_test.out @@ -0,0 +1,21 @@ +Reading cow.raw first +Size: 37502 +Done + +Loading cow.wav by filename: +Source size: 37502 +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory +Comparing... +Done + +Loading cow.wav by stream: +Source size: 37502 +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory +Comparing... +Done diff --git a/sound/tests/output/ffmpeg_source_test.out b/sound/tests/output/ffmpeg_source_test.out new file mode 100644 index 0000000000..1c7d491139 --- /dev/null +++ b/sound/tests/output/ffmpeg_source_test.out @@ -0,0 +1,12 @@ +Reading cow.raw first +Size: 37502 +Done + +Loading cow.wav by filename: +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory +Actually read: 37502 +Comparing... +Done diff --git a/sound/tests/output/openal_audiere_test.out b/sound/tests/output/openal_audiere_test.out new file mode 100644 index 0000000000..4fe01eac2b --- /dev/null +++ b/sound/tests/output/openal_audiere_test.out @@ -0,0 +1,3 @@ +Playing cow.wav +Playing owl.ogg +Playing cow.wav (from stream) diff --git a/sound/tests/output/openal_output_test.out b/sound/tests/output/openal_output_test.out new file mode 100644 index 0000000000..04392a72e8 --- /dev/null +++ b/sound/tests/output/openal_output_test.out @@ -0,0 +1,5 @@ +Loading cow.raw + rate=11025 + channels=1 + bits=16 +Playing diff --git a/sound/tests/test.sh b/sound/tests/test.sh new file mode 100755 index 0000000000..b1ca6f1a66 --- /dev/null +++ b/sound/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/stream/tests/output/audiere_client_test.out b/stream/tests/output/audiere_client_test.out new file mode 100644 index 0000000000..2130db9fd0 --- /dev/null +++ b/stream/tests/output/audiere_client_test.out @@ -0,0 +1,9 @@ +pos=0 +2 bytes: he +pos=2 +pos=4 +3 bytes: o w +pos=6 +pos=7 +last 4 bytes: orld +entire stream: hello world diff --git a/stream/tests/output/buffer_filter_test.out b/stream/tests/output/buffer_filter_test.out new file mode 100644 index 0000000000..0ca252f25e --- /dev/null +++ b/stream/tests/output/buffer_filter_test.out @@ -0,0 +1,22 @@ +Size: 11 +Pos: 0 +Seeking... +Pos: 3 +Reading: 4 +Four bytes: lo w +Eof: 0 +Pos: 7 +Seeking again... +Pos: 11 +Eof: 1 +Seek to 6 +Eof: 0 +Pos: 6 +Over-reading: 5 +Result: world +Eof: 1 +Pos: 11 +Finally, reading the entire string: 11 +Result: hello world +Eof: 1 +Pos: 11 diff --git a/stream/tests/output/file_server_test.out b/stream/tests/output/file_server_test.out new file mode 100644 index 0000000000..240f066a31 --- /dev/null +++ b/stream/tests/output/file_server_test.out @@ -0,0 +1,3 @@ +pos=0 eof=0 +First 20 bytes: #include "../servers +pos=20 eof=0 diff --git a/stream/tests/output/memory_server_test.out b/stream/tests/output/memory_server_test.out new file mode 100644 index 0000000000..7cd9533e78 --- /dev/null +++ b/stream/tests/output/memory_server_test.out @@ -0,0 +1,23 @@ +Size: 12 +Pos: 0 +Seeking... +Pos: 3 +Reading: 4 +Four bytes: lo w +Eof: 0 +Pos: 7 +Seeking again... +Pos: 12 +Eof: 1 +Seek to 6 +Eof: 0 +Pos: 6 +Over-reading: 6 +Result: world +Eof: 1 +Pos: 12 +Finally, reading the entire string: 11 +Result: hello world +Eof: 0 +Pos: 11 +Entire stream from pointer: hello world diff --git a/stream/tests/output/ogre_client_test.out b/stream/tests/output/ogre_client_test.out new file mode 100644 index 0000000000..62cd14604c --- /dev/null +++ b/stream/tests/output/ogre_client_test.out @@ -0,0 +1,5 @@ +Name: hello +As string: hello world +pos=11 eof=1 +pos=0 eof=0 +pos=3 eof=0 diff --git a/stream/tests/output/slice_filter_test.out b/stream/tests/output/slice_filter_test.out new file mode 100644 index 0000000000..6d84704a77 --- /dev/null +++ b/stream/tests/output/slice_filter_test.out @@ -0,0 +1,36 @@ + +Slice 1: +-------- +Size: 6 +Pos: 0 +Seeking... +Reading 6 bytes +Result: hello +Pos: 6 +Eof: 1 +Seeking: +Pos: 2 +Eof: 0 +Reading 4 bytes +Result: llo +Pos: 6 +Eof: 1 +Entire stream as pointer: hello + +Slice 2: +-------- +Size: 6 +Pos: 0 +Seeking... +Reading 6 bytes +Result: world +Pos: 6 +Eof: 1 +Seeking: +Pos: 2 +Eof: 0 +Reading 4 bytes +Result: rld +Pos: 6 +Eof: 1 +Entire stream as pointer: world diff --git a/stream/tests/test.sh b/stream/tests/test.sh new file mode 100755 index 0000000000..b1ca6f1a66 --- /dev/null +++ b/stream/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/testall.sh b/testall.sh new file mode 100755 index 0000000000..b2cc3059d0 --- /dev/null +++ b/testall.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +function run() +{ + cd "$1/tests/" + ./test.sh + cd ../../ +} + +run stream +run vfs +run sound +run . diff --git a/tests/output/ogrevfs_audiere_openal_test.out b/tests/output/ogrevfs_audiere_openal_test.out new file mode 100644 index 0000000000..28ea8a71b7 --- /dev/null +++ b/tests/output/ogrevfs_audiere_openal_test.out @@ -0,0 +1 @@ +Playing 'owl.ogg' from 'sound.zip' diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 0000000000..b1ca6f1a66 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/vfs/tests/output/dummy_test.out b/vfs/tests/output/dummy_test.out new file mode 100644 index 0000000000..61f1fda80a --- /dev/null +++ b/vfs/tests/output/dummy_test.out @@ -0,0 +1,31 @@ +Listing all files: +name: file1 +basename: file1 +isDir: 0 +size: 1 +time: 0 +name: dir/file2 +basename: file2 +isDir: 0 +size: 2 +time: 0 + +Stat for single files: +name: file1 +basename: file1 +isDir: 0 +size: 1 +time: 0 + +name: dir/file2 +basename: file2 +isDir: 0 +size: 2 +time: 0 + +name: dir +basename: dir +isDir: 1 +size: 0 +time: 0 +filesize: 11 diff --git a/vfs/tests/output/ogre_client_test.out b/vfs/tests/output/ogre_client_test.out new file mode 100644 index 0000000000..bc10b1e5cc --- /dev/null +++ b/vfs/tests/output/ogre_client_test.out @@ -0,0 +1,12 @@ +Case: 1 +Name: dummy +Type: Mangle +All files: + file1 + dir/file2 +Non-recursive: + file1 +Dirs: + dir +filesize: 11 +contents: hello world diff --git a/vfs/tests/output/ogre_resource_test.out b/vfs/tests/output/ogre_resource_test.out new file mode 100644 index 0000000000..99b2c91ba3 --- /dev/null +++ b/vfs/tests/output/ogre_resource_test.out @@ -0,0 +1,20 @@ + +File: Makefile +Size: 814 +First line: GCC=g++ -I../ + +File: ogre_resource_test.cpp +Size: 1437 +First line: #include + +File: bleh +Does not exist + +All source files: + physfs_server_test.cpp + dummy_test.cpp + ogre_resource_test.cpp + server_common.cpp + dummy_vfs.cpp + ogre_client_test.cpp + ogre_server_test.cpp diff --git a/vfs/tests/output/ogre_server_test.out b/vfs/tests/output/ogre_server_test.out new file mode 100644 index 0000000000..b00752eae1 --- /dev/null +++ b/vfs/tests/output/ogre_server_test.out @@ -0,0 +1,11 @@ + +File: Makefile +Size: 814 +First 12 bytes: GCC=g++ -I.. + +File: testfile.txt +Size: 13 +First 12 bytes: hello world! + +File: blah_bleh +File doesn't exist diff --git a/vfs/tests/output/physfs_server_test.out b/vfs/tests/output/physfs_server_test.out new file mode 100644 index 0000000000..b00752eae1 --- /dev/null +++ b/vfs/tests/output/physfs_server_test.out @@ -0,0 +1,11 @@ + +File: Makefile +Size: 814 +First 12 bytes: GCC=g++ -I.. + +File: testfile.txt +Size: 13 +First 12 bytes: hello world! + +File: blah_bleh +File doesn't exist diff --git a/vfs/tests/test.sh b/vfs/tests/test.sh new file mode 100755 index 0000000000..b1ca6f1a66 --- /dev/null +++ b/vfs/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + $a | diff output/$a.out - + else + echo "Creating $a.out" + $a > "output/$a.out" + git add "output/$a.out" + fi +done From 86089816a7532724e4d8ab464a60229b1177b23a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 2 Jan 2010 13:06:37 +0100 Subject: [PATCH 042/269] Added tools/str_exception.h and better error handling for StdStream and FileStream --- sound/outputs/openal_out.cpp | 16 ++-------------- sound/sources/audiere_source.cpp | 17 ++--------------- sound/sources/ffmpeg_source.cpp | 27 +++------------------------ stream/servers/file_stream.h | 3 +++ stream/servers/std_stream.h | 18 +++++++++++++++--- tools/str_exception.h | 19 +++++++++++++++++++ 6 files changed, 44 insertions(+), 56 deletions(-) create mode 100644 tools/str_exception.h diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index e6d0b3ae7b..f0298feb24 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -2,26 +2,14 @@ #include #include "../../stream/filters/buffer_stream.h" +#include "../../tools/str_exception.h" using namespace Mangle::Sound; // ---- Helper functions and classes ---- -class OpenAL_Exception : public std::exception -{ - std::string msg; - - public: - - OpenAL_Exception(const std::string &m) : msg(m) {} - ~OpenAL_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - static void fail(const std::string &msg) -{ - throw OpenAL_Exception("OpenAL exception: " + msg); -} +{ throw str_exception("OpenAL exception: " + msg); } static void checkALError(const std::string &msg) { diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 8a6b57ada8..6a07764023 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -1,25 +1,12 @@ #include "audiere_source.h" #include "../../stream/clients/audiere_file.h" +#include "../../tools/str_exception.h" using namespace Mangle::Stream; -// Exception handling -class Audiere_Exception : public std::exception -{ - std::string msg; - - public: - - Audiere_Exception(const std::string &m) : msg(m) {} - ~Audiere_Exception() throw() {} - virtual const char* what() const throw() { return msg.c_str(); } -}; - static void fail(const std::string &msg) -{ - throw Audiere_Exception("Audiere exception: " + msg); -} +{ throw str_exception("Audiere exception: " + msg); } using namespace audiere; using namespace Mangle::Sound; diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index 372d1766df..5724689de7 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,5 +1,6 @@ #include "ffmpeg_source.h" -#include + +#include "../../tools/str_exception.h" using namespace Mangle::Sound; @@ -7,30 +8,8 @@ using namespace Mangle::Sound; // streams operated from the same thread. static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; -/// FFmpeg exception. -class FFM_Exception : public std::exception -{ - std::string msg; - - public: - - FFM_Exception(const std::string &m); - ~FFM_Exception() throw(); - virtual const char* what() const throw(); -}; - -FFM_Exception::FFM_Exception(const std::string &m) - : msg(m) {} - -const char* FFM_Exception::what() const throw() -{ return msg.c_str(); } - -FFM_Exception::~FFM_Exception() throw() {} - static void fail(const std::string &msg) -{ - throw FFM_Exception("FFMpeg exception: " + msg); -} +{ throw str_exception("FFMpeg exception: " + msg); } // --- Loader --- diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.h index 012cb3a780..4aeb7eeac3 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.h @@ -18,6 +18,9 @@ class FileStream : public StdStream : StdStream(&file) { file.open(name.c_str(), std::ios::binary); + + if(file.fail()) + throw str_exception("FileStream: failed to open file " + name); } ~FileStream() { file.close(); } }; diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.h index d4b56a9aea..657d9303b0 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.h @@ -3,18 +3,20 @@ #include "../stream.h" #include +#include "../../tools/str_exception.h" namespace Mangle { namespace Stream { /** Simplest wrapper for std::istream. - - TODO: No error checking yet. */ class StdStream : public Stream { std::istream *inf; + static void fail(const std::string &msg) + { throw str_exception("StdStream: " + msg); } + public: StdStream(std::istream *_inf) : inf(_inf) @@ -27,11 +29,17 @@ class StdStream : public Stream size_t read(void* buf, size_t len) { inf->read((char*)buf, len); + if(inf->fail()) + fail("error reading from stream"); return inf->gcount(); } void seek(size_t pos) - { inf->seekg(pos); } + { + inf->seekg(pos); + if(inf->fail()) + fail("seek error"); + } size_t tell() const // Hack around the fact that ifstream->tellg() isn't const @@ -44,6 +52,10 @@ class StdStream : public Stream inf->seekg(0, std::ios::end); size_t res = inf->tellg(); inf->seekg(pos); + + if(inf->fail()) + fail("could not get stream size"); + return res; } diff --git a/tools/str_exception.h b/tools/str_exception.h new file mode 100644 index 0000000000..39120a3f9a --- /dev/null +++ b/tools/str_exception.h @@ -0,0 +1,19 @@ +#ifndef __STR_EXCEPTION_H +#define __STR_EXCEPTION_H + +#include +#include + +/// A simple exception that takes and holds a string +class str_exception : public std::exception +{ + std::string msg; + + public: + + str_exception(const std::string &m) : msg(m) {} + ~str_exception() throw() {} + const char* what() const throw() { return msg.c_str(); } +}; + +#endif From 4065ac019982660076a5d3e8e2d964ec69c7bb6a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 4 Jan 2010 11:55:38 +0100 Subject: [PATCH 043/269] Changed definition of Stream::getPtr(size), now moves the position locator by 'size' bytes. --- stream/filters/slice_stream.h | 7 ++++++- stream/servers/memory_stream.h | 10 ++++++++-- stream/stream.h | 9 +++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index d792b57d67..990092be7b 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -55,7 +55,12 @@ class SliceStream : public Stream size_t size() const { return length; } const void *getPtr() { return getPtr(0, length); } - const void *getPtr(size_t size) { return getPtr(pos, size); } + const void *getPtr(size_t size) + { + void *ptr = getPtr(pos, size); + seek(pos+size); + return ptr; + } const void *getPtr(size_t pos, size_t size) { // Boundry checks on pos and size. Bounding the size is diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index d77779878b..29bc476fc0 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -71,9 +71,15 @@ class MemoryStream : public Stream size_t size() const { return length; } bool eof() const { return pos == length; } - /// Get the base pointer to the entire buffer const void *getPtr() { return data; } - const void *getPtr(size_t size) { return ((char*)data)+pos; } + const void *getPtr(size_t size) + { + // This variant of getPtr must move the position pointer + size_t opos = pos; + pos += size; + if(pos > length) pos = length; + return ((char*)data)+opos; + } const void *getPtr(size_t pos, size_t size) { if(pos > length) pos = length; diff --git a/stream/stream.h b/stream/stream.h index 309b0b33a9..53e662341d 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -60,13 +60,14 @@ class Stream /// memory-based streams where using them would be an optimization. virtual const void *getPtr() { assert(0); } - /// Get a pointer to a memory region of 'size' bytes from the - /// current position. - virtual const void *getPtr(size_t size) { assert(0); } - /// Get a pointer to a memory region of 'size' bytes starting from /// position 'pos' virtual const void *getPtr(size_t pos, size_t size) { assert(0); } + + /// Get a pointer to a memory region of 'size' bytes from the + /// current position. Unlike the two other getPtr variants, this + /// will advance the position past the returned area. + virtual const void *getPtr(size_t size) { assert(0); } }; typedef boost::shared_ptr StreamPtr; From 4d16beef1a586211504d4d510ea791bc65682ad3 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 4 Jan 2010 12:13:47 +0100 Subject: [PATCH 044/269] Added Stream::PureFilter --- stream/filters/pure_filter.h | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 stream/filters/pure_filter.h diff --git a/stream/filters/pure_filter.h b/stream/filters/pure_filter.h new file mode 100644 index 0000000000..4ce679d48b --- /dev/null +++ b/stream/filters/pure_filter.h @@ -0,0 +1,39 @@ +#ifndef MANGLE_STREAM_FILTER_H +#define MANGLE_STREAM_FILTER_H + +#include "../stream.h" + +namespace Mangle { +namespace Stream { + +/** A stream that filters another stream with no changes. Intended as + a base class for other filters. + */ +class PureFilter : public Stream +{ + protected: + StreamPtr src; + + public: + PureFilter(StreamPtr _src) + : src(_src) + { + isSeekable = src->isSeekable; + hasPosition = src->hasPosition; + hasSize = src->hasSize; + hasPtr = src->hasPtr; + } + + size_t read(void *buf, size_t count) { return src->read(buf, count); } + void seek(size_t pos) { src->seek(pos); } + size_t tell() const { return src->tell(); } + size_t size() const { return src->size(); } + bool eof() const { return src->eof(); } + const void *getPtr() { return src->getPtr(); } + const void *getPtr(size_t size) { return src->getPtr(size); } + const void *getPtr(size_t pos, size_t size) + { return src->getPtr(pos, size); } +}; + +}} // namespaces +#endif From fb1eec974c63fa435286456f5c82b9d9387ff900 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 4 Jan 2010 12:22:52 +0100 Subject: [PATCH 045/269] Fixed const error in slice_stream.h --- stream/filters/slice_stream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.h index 990092be7b..ade0df52bf 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.h @@ -57,7 +57,7 @@ class SliceStream : public Stream const void *getPtr() { return getPtr(0, length); } const void *getPtr(size_t size) { - void *ptr = getPtr(pos, size); + const void *ptr = getPtr(pos, size); seek(pos+size); return ptr; } From 2a6ed21351464751245c485f933ed788d6cb0698 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 5 Jan 2010 13:16:05 +0100 Subject: [PATCH 046/269] Fixed incorrect hasPtr in MemoryStream --- stream/servers/memory_stream.h | 1 + 1 file changed, 1 insertion(+) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index 29bc476fc0..e8c1dfbd5d 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -30,6 +30,7 @@ class MemoryStream : public Stream isSeekable = true; hasPosition = true; hasSize = true; + hasPtr = true; } MemoryStream() From 20960ad6846c91917335672be6ac72019a56a891 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 6 Feb 2010 11:41:19 +0100 Subject: [PATCH 047/269] comment spell fix --- stream/servers/memory_stream.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.h index e8c1dfbd5d..f20fe204a3 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.h @@ -9,13 +9,13 @@ namespace Mangle { namespace Stream { // Do this before the class declaration, since the class itself -// depends on it. +// uses it. class MemoryStream; typedef boost::shared_ptr MemoryStreamPtr; /** A Stream wrapping a memory buffer - This will create a fully seekable stream out any pointer/length + This will create a fully seekable stream out of any pointer/length pair you give it. */ class MemoryStream : public Stream From 401fa0079c9045e4550c05ca342bde8775e39264 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 4 Mar 2010 11:12:22 +0100 Subject: [PATCH 048/269] Added return statements to assert-functions in stream.h --- stream/stream.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stream/stream.h b/stream/stream.h index 53e662341d..acd001d24e 100644 --- a/stream/stream.h +++ b/stream/stream.h @@ -58,16 +58,16 @@ class Stream /// Return a pointer to the entire stream. This function (and the /// other getPtr() variants below) should only be implemented for /// memory-based streams where using them would be an optimization. - virtual const void *getPtr() { assert(0); } + virtual const void *getPtr() { assert(0); return NULL; } /// Get a pointer to a memory region of 'size' bytes starting from /// position 'pos' - virtual const void *getPtr(size_t pos, size_t size) { assert(0); } + virtual const void *getPtr(size_t pos, size_t size) { assert(0); return NULL; } /// Get a pointer to a memory region of 'size' bytes from the /// current position. Unlike the two other getPtr variants, this /// will advance the position past the returned area. - virtual const void *getPtr(size_t size) { assert(0); } + virtual const void *getPtr(size_t size) { assert(0); return NULL; } }; typedef boost::shared_ptr StreamPtr; From 52e7570b4fddd868cc0483e0fa2e49c50d5a1334 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 19 May 2010 13:33:18 +0200 Subject: [PATCH 049/269] The tests should run when ./ is not in $PATH, too. (Thanks to Henrik Kretzschmar) --- sound/tests/test.sh | 4 ++-- stream/tests/test.sh | 4 ++-- tests/test.sh | 4 ++-- vfs/tests/test.sh | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/tests/test.sh b/sound/tests/test.sh index b1ca6f1a66..2d07708adc 100755 --- a/sound/tests/test.sh +++ b/sound/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done diff --git a/stream/tests/test.sh b/stream/tests/test.sh index b1ca6f1a66..2d07708adc 100755 --- a/stream/tests/test.sh +++ b/stream/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done diff --git a/tests/test.sh b/tests/test.sh index b1ca6f1a66..2d07708adc 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done diff --git a/vfs/tests/test.sh b/vfs/tests/test.sh index b1ca6f1a66..2d07708adc 100755 --- a/vfs/tests/test.sh +++ b/vfs/tests/test.sh @@ -9,10 +9,10 @@ PROGS=*_test for a in $PROGS; do if [ -f "output/$a.out" ]; then echo "Running $a:" - $a | diff output/$a.out - + ./$a | diff output/$a.out - else echo "Creating $a.out" - $a > "output/$a.out" + ./$a > "output/$a.out" git add "output/$a.out" fi done From 6b0b7c95f8a40a53b4c26d551d4fb5118deb7e12 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 3 Jun 2010 20:13:27 +0200 Subject: [PATCH 050/269] Renamed all .h to .hpp for consistency --- sound/filters/{input_filter.h => input_filter.hpp} | 2 +- .../filters/{openal_audiere.h => openal_audiere.hpp} | 6 +++--- sound/{output.h => output.hpp} | 4 ++-- sound/outputs/openal_out.cpp | 6 +++--- sound/outputs/{openal_out.h => openal_out.hpp} | 2 +- sound/{source.h => source.hpp} | 2 +- sound/sources/audiere_source.cpp | 6 +++--- .../sources/{audiere_source.h => audiere_source.hpp} | 4 ++-- sound/sources/ffmpeg_source.cpp | 4 ++-- sound/sources/{ffmpeg_source.h => ffmpeg_source.hpp} | 4 ++-- .../sources/{loadertemplate.h => loadertemplate.hpp} | 0 sound/sources/{stream_source.h => stream_source.hpp} | 2 +- sound/tests/audiere_source_test.cpp | 4 ++-- sound/tests/ffmpeg_source_test.cpp | 4 ++-- sound/tests/openal_audiere_test.cpp | 4 ++-- sound/tests/openal_output_test.cpp | 6 +++--- stream/clients/audiere_file.cpp | 2 +- stream/clients/{audiere_file.h => audiere_file.hpp} | 2 +- .../{ogre_datastream.h => ogre_datastream.hpp} | 2 +- .../filters/{buffer_stream.h => buffer_stream.hpp} | 2 +- stream/filters/{pure_filter.h => pure_filter.hpp} | 2 +- stream/filters/{slice_stream.h => slice_stream.hpp} | 2 +- stream/servers/{file_stream.h => file_stream.hpp} | 2 +- .../servers/{memory_stream.h => memory_stream.hpp} | 2 +- .../{ogre_datastream.h => ogre_datastream.hpp} | 0 stream/servers/{phys_stream.h => phys_stream.hpp} | 0 stream/servers/{std_stream.h => std_stream.hpp} | 4 ++-- stream/{stream.h => stream.hpp} | 2 +- stream/tests/Makefile | 12 ++++++------ stream/tests/audiere_client_test.cpp | 4 ++-- stream/tests/buffer_filter_test.cpp | 2 +- stream/tests/file_server_test.cpp | 2 +- stream/tests/memory_server_test.cpp | 2 +- stream/tests/ogre_client_test.cpp | 4 ++-- stream/tests/slice_filter_test.cpp | 4 ++-- testall.sh | 1 + tests/ogrevfs_audiere_openal_test.cpp | 4 ++-- tools/{shared_ptr.h => shared_ptr.hpp} | 0 tools/{str_exception.h => str_exception.hpp} | 0 vfs/clients/ogre_archive.cpp | 4 ++-- vfs/clients/{ogre_archive.h => ogre_archive.hpp} | 2 +- vfs/servers/ogre_vfs.cpp | 4 ++-- vfs/servers/{ogre_vfs.h => ogre_vfs.hpp} | 2 +- vfs/servers/{physfs_vfs.h => physfs_vfs.hpp} | 4 ++-- vfs/tests/Makefile | 8 ++++---- vfs/tests/dummy_vfs.cpp | 4 ++-- vfs/tests/ogre_client_test.cpp | 2 +- vfs/tests/ogre_server_test.cpp | 2 +- vfs/tests/output/ogre_resource_test.out | 2 +- vfs/tests/output/ogre_server_test.out | 2 +- vfs/tests/output/physfs_server_test.out | 2 +- vfs/tests/physfs_server_test.cpp | 2 +- vfs/{vfs.h => vfs.hpp} | 2 +- 53 files changed, 80 insertions(+), 79 deletions(-) rename sound/filters/{input_filter.h => input_filter.hpp} (98%) rename sound/filters/{openal_audiere.h => openal_audiere.hpp} (81%) rename sound/{output.h => output.hpp} (98%) rename sound/outputs/{openal_out.h => openal_out.hpp} (98%) rename sound/{source.h => source.hpp} (97%) rename sound/sources/{audiere_source.h => audiere_source.hpp} (96%) rename sound/sources/{ffmpeg_source.h => ffmpeg_source.hpp} (94%) rename sound/sources/{loadertemplate.h => loadertemplate.hpp} (100%) rename sound/sources/{stream_source.h => stream_source.hpp} (98%) rename stream/clients/{audiere_file.h => audiere_file.hpp} (97%) rename stream/clients/{ogre_datastream.h => ogre_datastream.hpp} (98%) rename stream/filters/{buffer_stream.h => buffer_stream.hpp} (97%) rename stream/filters/{pure_filter.h => pure_filter.hpp} (97%) rename stream/filters/{slice_stream.h => slice_stream.hpp} (98%) rename stream/servers/{file_stream.h => file_stream.hpp} (95%) rename stream/servers/{memory_stream.h => memory_stream.hpp} (99%) rename stream/servers/{ogre_datastream.h => ogre_datastream.hpp} (100%) rename stream/servers/{phys_stream.h => phys_stream.hpp} (100%) rename stream/servers/{std_stream.h => std_stream.hpp} (95%) rename stream/{stream.h => stream.hpp} (98%) rename tools/{shared_ptr.h => shared_ptr.hpp} (100%) rename tools/{str_exception.h => str_exception.hpp} (100%) rename vfs/clients/{ogre_archive.h => ogre_archive.hpp} (98%) rename vfs/servers/{ogre_vfs.h => ogre_vfs.hpp} (99%) rename vfs/servers/{physfs_vfs.h => physfs_vfs.hpp} (96%) rename vfs/{vfs.h => vfs.hpp} (98%) diff --git a/sound/filters/input_filter.h b/sound/filters/input_filter.hpp similarity index 98% rename from sound/filters/input_filter.h rename to sound/filters/input_filter.hpp index 2b4486b75f..267858de13 100644 --- a/sound/filters/input_filter.h +++ b/sound/filters/input_filter.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_INPUT_FILTER_H #define MANGLE_INPUT_FILTER_H -#include "../output.h" +#include "../output.hpp" #include diff --git a/sound/filters/openal_audiere.h b/sound/filters/openal_audiere.hpp similarity index 81% rename from sound/filters/openal_audiere.h rename to sound/filters/openal_audiere.hpp index a1b2e6b649..5b62bd11b1 100644 --- a/sound/filters/openal_audiere.h +++ b/sound/filters/openal_audiere.hpp @@ -1,9 +1,9 @@ #ifndef MANGLE_FFMPEG_OPENAL_H #define MANGLE_FFMPEG_OPENAL_H -#include "input_filter.h" -#include "../sources/audiere_source.h" -#include "../outputs/openal_out.h" +#include "input_filter.hpp" +#include "../sources/audiere_source.hpp" +#include "../outputs/openal_out.hpp" namespace Mangle { namespace Sound { diff --git a/sound/output.h b/sound/output.hpp similarity index 98% rename from sound/output.h rename to sound/output.hpp index 6bcd932377..2ead277bcc 100644 --- a/sound/output.h +++ b/sound/output.hpp @@ -2,9 +2,9 @@ #define MANGLE_SOUND_OUTPUT_H #include -#include "source.h" +#include "source.hpp" -#include "../stream/stream.h" +#include "../stream/stream.hpp" namespace Mangle { namespace Sound { diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index f0298feb24..81e2223627 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,8 +1,8 @@ -#include "openal_out.h" +#include "openal_out.hpp" #include -#include "../../stream/filters/buffer_stream.h" -#include "../../tools/str_exception.h" +#include "../../stream/filters/buffer_stream.hpp" +#include "../../tools/str_exception.hpp" using namespace Mangle::Sound; diff --git a/sound/outputs/openal_out.h b/sound/outputs/openal_out.hpp similarity index 98% rename from sound/outputs/openal_out.h rename to sound/outputs/openal_out.hpp index 2ec5936653..18f32f2a9c 100644 --- a/sound/outputs/openal_out.h +++ b/sound/outputs/openal_out.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_OPENAL_OUT_H #define MANGLE_SOUND_OPENAL_OUT_H -#include "../output.h" +#include "../output.hpp" #include #include diff --git a/sound/source.h b/sound/source.hpp similarity index 97% rename from sound/source.h rename to sound/source.hpp index d5468b5181..1eb42a2837 100644 --- a/sound/source.h +++ b/sound/source.hpp @@ -5,7 +5,7 @@ #include #include -#include "../stream/stream.h" +#include "../stream/stream.hpp" namespace Mangle { namespace Sound { diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 6a07764023..6fde40c075 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -1,7 +1,7 @@ -#include "audiere_source.h" +#include "audiere_source.hpp" -#include "../../stream/clients/audiere_file.h" -#include "../../tools/str_exception.h" +#include "../../stream/clients/audiere_file.hpp" +#include "../../tools/str_exception.hpp" using namespace Mangle::Stream; diff --git a/sound/sources/audiere_source.h b/sound/sources/audiere_source.hpp similarity index 96% rename from sound/sources/audiere_source.h rename to sound/sources/audiere_source.hpp index e2d09d006d..476546af3a 100644 --- a/sound/sources/audiere_source.h +++ b/sound/sources/audiere_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_AUDIERE_SOURCE_H #define MANGLE_SOUND_AUDIERE_SOURCE_H -#include "../source.h" +#include "../source.hpp" #include @@ -46,7 +46,7 @@ class AudiereSource : public SampleSource size_t size() const { return sample->getLength()*frameSize; } }; -#include "loadertemplate.h" +#include "loadertemplate.hpp" /// A factory that loads AudiereSources from file and stream typedef SSL_Template AudiereLoader; diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index 5724689de7..bfb4282346 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,6 +1,6 @@ -#include "ffmpeg_source.h" +#include "ffmpeg_source.hpp" -#include "../../tools/str_exception.h" +#include "../../tools/str_exception.hpp" using namespace Mangle::Sound; diff --git a/sound/sources/ffmpeg_source.h b/sound/sources/ffmpeg_source.hpp similarity index 94% rename from sound/sources/ffmpeg_source.h rename to sound/sources/ffmpeg_source.hpp index 2b33a32226..f0b5342acc 100644 --- a/sound/sources/ffmpeg_source.h +++ b/sound/sources/ffmpeg_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_FFMPEG_H #define MANGLE_SOUND_FFMPEG_H -#include "../source.h" +#include "../source.hpp" #include #include @@ -36,7 +36,7 @@ class FFMpegSource : public SampleSource size_t read(void *data, size_t length); }; -#include "loadertemplate.h" +#include "loadertemplate.hpp" /// A factory that loads FFMpegSources from file class FFMpegLoader : public SSL_Template diff --git a/sound/sources/loadertemplate.h b/sound/sources/loadertemplate.hpp similarity index 100% rename from sound/sources/loadertemplate.h rename to sound/sources/loadertemplate.hpp diff --git a/sound/sources/stream_source.h b/sound/sources/stream_source.hpp similarity index 98% rename from sound/sources/stream_source.h rename to sound/sources/stream_source.hpp index 42791ccdf1..e4863545e9 100644 --- a/sound/sources/stream_source.h +++ b/sound/sources/stream_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_STREAMSOURCE_H #define MANGLE_SOUND_STREAMSOURCE_H -#include "../source.h" +#include "../source.hpp" namespace Mangle { namespace Sound { diff --git a/sound/tests/audiere_source_test.cpp b/sound/tests/audiere_source_test.cpp index 1631e89cba..637d743b21 100644 --- a/sound/tests/audiere_source_test.cpp +++ b/sound/tests/audiere_source_test.cpp @@ -1,7 +1,7 @@ #include -#include "../../stream/servers/file_stream.h" -#include "../sources/audiere_source.h" +#include "../../stream/servers/file_stream.hpp" +#include "../sources/audiere_source.hpp" #include #include diff --git a/sound/tests/ffmpeg_source_test.cpp b/sound/tests/ffmpeg_source_test.cpp index 294565fb12..f03b15b996 100644 --- a/sound/tests/ffmpeg_source_test.cpp +++ b/sound/tests/ffmpeg_source_test.cpp @@ -1,7 +1,7 @@ #include -#include "../../stream/servers/file_stream.h" -#include "../sources/ffmpeg_source.h" +#include "../../stream/servers/file_stream.hpp" +#include "../sources/ffmpeg_source.hpp" #include #include diff --git a/sound/tests/openal_audiere_test.cpp b/sound/tests/openal_audiere_test.cpp index 9c49356a26..ced7fe5d23 100644 --- a/sound/tests/openal_audiere_test.cpp +++ b/sound/tests/openal_audiere_test.cpp @@ -1,8 +1,8 @@ #include #include -#include "../../stream/servers/file_stream.h" -#include "../filters/openal_audiere.h" +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_audiere.hpp" using namespace std; using namespace Mangle::Stream; diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp index 11b2fc42e4..bc781c1a43 100644 --- a/sound/tests/openal_output_test.cpp +++ b/sound/tests/openal_output_test.cpp @@ -1,9 +1,9 @@ #include #include -#include "../../stream/servers/file_stream.h" -#include "../sources/stream_source.h" -#include "../outputs/openal_out.h" +#include "../../stream/servers/file_stream.hpp" +#include "../sources/stream_source.hpp" +#include "../outputs/openal_out.hpp" using namespace std; using namespace Mangle::Stream; diff --git a/stream/clients/audiere_file.cpp b/stream/clients/audiere_file.cpp index 53638781e3..1d8e5b7065 100644 --- a/stream/clients/audiere_file.cpp +++ b/stream/clients/audiere_file.cpp @@ -1,4 +1,4 @@ -#include "audiere_file.h" +#include "audiere_file.hpp" using namespace audiere; using namespace Mangle::Stream; diff --git a/stream/clients/audiere_file.h b/stream/clients/audiere_file.hpp similarity index 97% rename from stream/clients/audiere_file.h rename to stream/clients/audiere_file.hpp index 670b36ffae..691525ad92 100644 --- a/stream/clients/audiere_file.h +++ b/stream/clients/audiere_file.hpp @@ -4,7 +4,7 @@ #include #include -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/clients/ogre_datastream.h b/stream/clients/ogre_datastream.hpp similarity index 98% rename from stream/clients/ogre_datastream.h rename to stream/clients/ogre_datastream.hpp index 5369ed11a9..80c37fee76 100644 --- a/stream/clients/ogre_datastream.h +++ b/stream/clients/ogre_datastream.hpp @@ -3,7 +3,7 @@ #include #include -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/filters/buffer_stream.h b/stream/filters/buffer_stream.hpp similarity index 97% rename from stream/filters/buffer_stream.h rename to stream/filters/buffer_stream.hpp index d1af09a8ca..0d22798d00 100644 --- a/stream/filters/buffer_stream.h +++ b/stream/filters/buffer_stream.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_BUFFER_H #define MANGLE_STREAM_BUFFER_H -#include "../servers/memory_stream.h" +#include "../servers/memory_stream.hpp" #include namespace Mangle { diff --git a/stream/filters/pure_filter.h b/stream/filters/pure_filter.hpp similarity index 97% rename from stream/filters/pure_filter.h rename to stream/filters/pure_filter.hpp index 4ce679d48b..e4ae243750 100644 --- a/stream/filters/pure_filter.h +++ b/stream/filters/pure_filter.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_FILTER_H #define MANGLE_STREAM_FILTER_H -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/filters/slice_stream.h b/stream/filters/slice_stream.hpp similarity index 98% rename from stream/filters/slice_stream.h rename to stream/filters/slice_stream.hpp index ade0df52bf..49f241cfcf 100644 --- a/stream/filters/slice_stream.h +++ b/stream/filters/slice_stream.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_SLICE_H #define MANGLE_STREAM_SLICE_H -#include "../stream.h" +#include "../stream.hpp" namespace Mangle { namespace Stream { diff --git a/stream/servers/file_stream.h b/stream/servers/file_stream.hpp similarity index 95% rename from stream/servers/file_stream.h rename to stream/servers/file_stream.hpp index 4aeb7eeac3..c789d20223 100644 --- a/stream/servers/file_stream.h +++ b/stream/servers/file_stream.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_STREAM_FILESERVER_H #define MANGLE_STREAM_FILESERVER_H -#include "std_stream.h" +#include "std_stream.hpp" #include namespace Mangle { diff --git a/stream/servers/memory_stream.h b/stream/servers/memory_stream.hpp similarity index 99% rename from stream/servers/memory_stream.h rename to stream/servers/memory_stream.hpp index f20fe204a3..0849e4a3cd 100644 --- a/stream/servers/memory_stream.h +++ b/stream/servers/memory_stream.hpp @@ -2,7 +2,7 @@ #define MANGLE_STREAM_MEMSERVER_H #include -#include "../stream.h" +#include "../stream.hpp" #include namespace Mangle { diff --git a/stream/servers/ogre_datastream.h b/stream/servers/ogre_datastream.hpp similarity index 100% rename from stream/servers/ogre_datastream.h rename to stream/servers/ogre_datastream.hpp diff --git a/stream/servers/phys_stream.h b/stream/servers/phys_stream.hpp similarity index 100% rename from stream/servers/phys_stream.h rename to stream/servers/phys_stream.hpp diff --git a/stream/servers/std_stream.h b/stream/servers/std_stream.hpp similarity index 95% rename from stream/servers/std_stream.h rename to stream/servers/std_stream.hpp index 657d9303b0..bb8bb6cbc9 100644 --- a/stream/servers/std_stream.h +++ b/stream/servers/std_stream.hpp @@ -1,9 +1,9 @@ #ifndef MANGLE_STREAM_STDIOSERVER_H #define MANGLE_STREAM_STDIOSERVER_H -#include "../stream.h" +#include "../stream.hpp" #include -#include "../../tools/str_exception.h" +#include "../../tools/str_exception.hpp" namespace Mangle { namespace Stream { diff --git a/stream/stream.h b/stream/stream.hpp similarity index 98% rename from stream/stream.h rename to stream/stream.hpp index acd001d24e..a195fe3219 100644 --- a/stream/stream.h +++ b/stream/stream.hpp @@ -2,7 +2,7 @@ #define MANGLE_STREAM_INPUT_H #include -#include "../tools/shared_ptr.h" +#include "../tools/shared_ptr.hpp" #include namespace Mangle { diff --git a/stream/tests/Makefile b/stream/tests/Makefile index d95908afb4..8504de7414 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -6,22 +6,22 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_AUDIERE=-laudiere -ogre_client_test: ogre_client_test.cpp ../stream.h ../clients/ogre_datastream.h +ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream.hpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -audiere_client_test: audiere_client_test.cpp ../stream.h ../clients/audiere_file.h ../clients/audiere_file.cpp +audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -file_server_test: file_server_test.cpp ../stream.h ../servers/file_stream.h ../servers/std_stream.h +file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ -memory_server_test: memory_server_test.cpp ../stream.h ../servers/memory_stream.h +memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp $(GCC) $< -o $@ -buffer_filter_test: buffer_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/buffer_stream.h +buffer_filter_test: buffer_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/buffer_stream.hpp $(GCC) $< -o $@ -slice_filter_test: slice_filter_test.cpp ../stream.h ../servers/memory_stream.h ../filters/slice_stream.h +slice_filter_test: slice_filter_test.cpp ../stream.hpp ../servers/memory_stream.hpp ../filters/slice_stream.hpp $(GCC) $< -o $@ clean: diff --git a/stream/tests/audiere_client_test.cpp b/stream/tests/audiere_client_test.cpp index dd4dfe4adb..82be569cc3 100644 --- a/stream/tests/audiere_client_test.cpp +++ b/stream/tests/audiere_client_test.cpp @@ -1,5 +1,5 @@ -#include "../servers/memory_stream.h" -#include "../clients/audiere_file.h" +#include "../servers/memory_stream.hpp" +#include "../clients/audiere_file.hpp" #include #include diff --git a/stream/tests/buffer_filter_test.cpp b/stream/tests/buffer_filter_test.cpp index b6f43e99b1..971530d845 100644 --- a/stream/tests/buffer_filter_test.cpp +++ b/stream/tests/buffer_filter_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "../filters/buffer_stream.h" +#include "../filters/buffer_stream.hpp" using namespace Mangle::Stream; using namespace std; diff --git a/stream/tests/file_server_test.cpp b/stream/tests/file_server_test.cpp index 1c25051587..3e1e3cfa57 100644 --- a/stream/tests/file_server_test.cpp +++ b/stream/tests/file_server_test.cpp @@ -1,4 +1,4 @@ -#include "../servers/file_stream.h" +#include "../servers/file_stream.hpp" #include using namespace Mangle::Stream; diff --git a/stream/tests/memory_server_test.cpp b/stream/tests/memory_server_test.cpp index b9f21b9b1a..24dea79a2d 100644 --- a/stream/tests/memory_server_test.cpp +++ b/stream/tests/memory_server_test.cpp @@ -1,7 +1,7 @@ #include #include -#include "../servers/memory_stream.h" +#include "../servers/memory_stream.hpp" using namespace Mangle::Stream; using namespace std; diff --git a/stream/tests/ogre_client_test.cpp b/stream/tests/ogre_client_test.cpp index a89589d7c9..c8d0442c01 100644 --- a/stream/tests/ogre_client_test.cpp +++ b/stream/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ -#include "../servers/memory_stream.h" -#include "../clients/ogre_datastream.h" +#include "../servers/memory_stream.hpp" +#include "../clients/ogre_datastream.hpp" #include using namespace Mangle::Stream; diff --git a/stream/tests/slice_filter_test.cpp b/stream/tests/slice_filter_test.cpp index ff384c1bbb..da90e24ad0 100644 --- a/stream/tests/slice_filter_test.cpp +++ b/stream/tests/slice_filter_test.cpp @@ -1,8 +1,8 @@ #include #include -#include "../filters/slice_stream.h" -#include "../servers/memory_stream.h" +#include "../filters/slice_stream.hpp" +#include "../servers/memory_stream.hpp" using namespace Mangle::Stream; using namespace std; diff --git a/testall.sh b/testall.sh index b2cc3059d0..f38939a198 100755 --- a/testall.sh +++ b/testall.sh @@ -2,6 +2,7 @@ function run() { + echo "TESTING $1" cd "$1/tests/" ./test.sh cd ../../ diff --git a/tests/ogrevfs_audiere_openal_test.cpp b/tests/ogrevfs_audiere_openal_test.cpp index 3e267c98ae..4936538c55 100644 --- a/tests/ogrevfs_audiere_openal_test.cpp +++ b/tests/ogrevfs_audiere_openal_test.cpp @@ -7,8 +7,8 @@ */ -#include "sound/filters/openal_audiere.h" -#include "vfs/servers/ogre_vfs.h" +#include "sound/filters/openal_audiere.hpp" +#include "vfs/servers/ogre_vfs.hpp" #include #include diff --git a/tools/shared_ptr.h b/tools/shared_ptr.hpp similarity index 100% rename from tools/shared_ptr.h rename to tools/shared_ptr.hpp diff --git a/tools/str_exception.h b/tools/str_exception.hpp similarity index 100% rename from tools/str_exception.h rename to tools/str_exception.hpp diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index e9612d7d90..ed63370539 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -1,6 +1,6 @@ -#include "ogre_archive.h" +#include "ogre_archive.hpp" -#include "../../stream/clients/ogre_datastream.h" +#include "../../stream/clients/ogre_datastream.hpp" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/clients/ogre_archive.h b/vfs/clients/ogre_archive.hpp similarity index 98% rename from vfs/clients/ogre_archive.h rename to vfs/clients/ogre_archive.hpp index 22964dc35d..0ea9413669 100644 --- a/vfs/clients/ogre_archive.h +++ b/vfs/clients/ogre_archive.hpp @@ -3,7 +3,7 @@ #include #include -#include "../vfs.h" +#include "../vfs.hpp" namespace Mangle { namespace VFS { diff --git a/vfs/servers/ogre_vfs.cpp b/vfs/servers/ogre_vfs.cpp index ab496a1924..7f728c1b0f 100644 --- a/vfs/servers/ogre_vfs.cpp +++ b/vfs/servers/ogre_vfs.cpp @@ -1,5 +1,5 @@ -#include "ogre_vfs.h" -#include "../../stream/servers/ogre_datastream.h" +#include "ogre_vfs.hpp" +#include "../../stream/servers/ogre_datastream.hpp" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/servers/ogre_vfs.h b/vfs/servers/ogre_vfs.hpp similarity index 99% rename from vfs/servers/ogre_vfs.h rename to vfs/servers/ogre_vfs.hpp index f212432f80..7ab64169df 100644 --- a/vfs/servers/ogre_vfs.h +++ b/vfs/servers/ogre_vfs.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_VFS_OGRESERVER_H #define MANGLE_VFS_OGRESERVER_H -#include "../vfs.h" +#include "../vfs.hpp" #include namespace Mangle { diff --git a/vfs/servers/physfs_vfs.h b/vfs/servers/physfs_vfs.hpp similarity index 96% rename from vfs/servers/physfs_vfs.h rename to vfs/servers/physfs_vfs.hpp index 49acc398d3..8535088e06 100644 --- a/vfs/servers/physfs_vfs.h +++ b/vfs/servers/physfs_vfs.hpp @@ -1,8 +1,8 @@ #ifndef MANGLE_VFS_PHYSFS_SERVER_H #define MANGLE_VFS_PHYSFS_SERVER_H -#include "../vfs.h" -#include "../../stream/servers/phys_stream.h" +#include "../vfs.hpp" +#include "../../stream/servers/phys_stream.hpp" #include #include diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index c9963cfa11..e0577b64ee 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -6,19 +6,19 @@ I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) L_PHYSFS=-lphysfs -ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.h ../clients/ogre_archive.h ../clients/ogre_archive.cpp +ogre_client_test: ogre_client_test.cpp dummy_vfs.cpp ../vfs.hpp ../clients/ogre_archive.hpp ../clients/ogre_archive.cpp $(GCC) $< ../clients/ogre_archive.cpp -o $@ $(I_OGRE) $(L_OGRE) ogre_resource_test: ogre_resource_test.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) -ogre_server_test: ogre_server_test.cpp ../vfs.h ../servers/ogre_vfs.h ../servers/ogre_vfs.cpp +ogre_server_test: ogre_server_test.cpp ../vfs.hpp ../servers/ogre_vfs.hpp ../servers/ogre_vfs.cpp $(GCC) $< -o $@ $(I_OGRE) $(L_OGRE) ../servers/ogre_vfs.cpp -physfs_server_test: physfs_server_test.cpp ../vfs.h ../servers/physfs_vfs.h +physfs_server_test: physfs_server_test.cpp ../vfs.hpp ../servers/physfs_vfs.hpp $(GCC) $< -o $@ $(L_PHYSFS) -dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.h +dummy_test: dummy_test.cpp dummy_vfs.cpp ../vfs.hpp $(GCC) $< -o $@ clean: diff --git a/vfs/tests/dummy_vfs.cpp b/vfs/tests/dummy_vfs.cpp index 7a39da9322..0448e96a55 100644 --- a/vfs/tests/dummy_vfs.cpp +++ b/vfs/tests/dummy_vfs.cpp @@ -1,9 +1,9 @@ // This file is shared between several test programs -#include "vfs.h" +#include "vfs.hpp" #include #include -#include "../../stream/servers/memory_stream.h" +#include "../../stream/servers/memory_stream.hpp" using namespace Mangle::VFS; using namespace Mangle::Stream; diff --git a/vfs/tests/ogre_client_test.cpp b/vfs/tests/ogre_client_test.cpp index 542ac9c7eb..8e1269b56f 100644 --- a/vfs/tests/ogre_client_test.cpp +++ b/vfs/tests/ogre_client_test.cpp @@ -1,5 +1,5 @@ #include "dummy_vfs.cpp" -#include "../clients/ogre_archive.h" +#include "../clients/ogre_archive.hpp" #include using namespace Ogre; diff --git a/vfs/tests/ogre_server_test.cpp b/vfs/tests/ogre_server_test.cpp index cb46248942..b846eef96e 100644 --- a/vfs/tests/ogre_server_test.cpp +++ b/vfs/tests/ogre_server_test.cpp @@ -1,4 +1,4 @@ -#include "../servers/ogre_vfs.h" +#include "../servers/ogre_vfs.hpp" #include diff --git a/vfs/tests/output/ogre_resource_test.out b/vfs/tests/output/ogre_resource_test.out index 99b2c91ba3..9bfc4ff5b6 100644 --- a/vfs/tests/output/ogre_resource_test.out +++ b/vfs/tests/output/ogre_resource_test.out @@ -1,6 +1,6 @@ File: Makefile -Size: 814 +Size: 828 First line: GCC=g++ -I../ File: ogre_resource_test.cpp diff --git a/vfs/tests/output/ogre_server_test.out b/vfs/tests/output/ogre_server_test.out index b00752eae1..74f3508444 100644 --- a/vfs/tests/output/ogre_server_test.out +++ b/vfs/tests/output/ogre_server_test.out @@ -1,6 +1,6 @@ File: Makefile -Size: 814 +Size: 828 First 12 bytes: GCC=g++ -I.. File: testfile.txt diff --git a/vfs/tests/output/physfs_server_test.out b/vfs/tests/output/physfs_server_test.out index b00752eae1..74f3508444 100644 --- a/vfs/tests/output/physfs_server_test.out +++ b/vfs/tests/output/physfs_server_test.out @@ -1,6 +1,6 @@ File: Makefile -Size: 814 +Size: 828 First 12 bytes: GCC=g++ -I.. File: testfile.txt diff --git a/vfs/tests/physfs_server_test.cpp b/vfs/tests/physfs_server_test.cpp index ae42083fdb..9e9d088cdf 100644 --- a/vfs/tests/physfs_server_test.cpp +++ b/vfs/tests/physfs_server_test.cpp @@ -1,4 +1,4 @@ -#include "../servers/physfs_vfs.h" +#include "../servers/physfs_vfs.hpp" #include "server_common.cpp" diff --git a/vfs/vfs.h b/vfs/vfs.hpp similarity index 98% rename from vfs/vfs.h rename to vfs/vfs.hpp index c52bcaebba..ce51be94b1 100644 --- a/vfs/vfs.h +++ b/vfs/vfs.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_VFS_H #define MANGLE_VFS_H -#include "../stream/stream.h" +#include "../stream/stream.hpp" #include #include From 19649bfeaa5a204cc765c7b0a66789919d6681d9 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 25 Jun 2010 15:20:04 +0200 Subject: [PATCH 051/269] Minor -Wall warning fixes, expanded a comment --- stream/clients/ogre_datastream.hpp | 2 +- stream/filters/buffer_stream.hpp | 2 +- tools/str_exception.hpp | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/stream/clients/ogre_datastream.hpp b/stream/clients/ogre_datastream.hpp index 80c37fee76..7b4106001f 100644 --- a/stream/clients/ogre_datastream.hpp +++ b/stream/clients/ogre_datastream.hpp @@ -32,7 +32,7 @@ class Mangle2OgreStream : public Ogre::DataStream /// Constructor for a named data stream Mangle2OgreStream(const Ogre::String &name, StreamPtr _inp) - : inp(_inp), Ogre::DataStream(name) { init(); } + : Ogre::DataStream(name), inp(_inp) { init(); } // Only implement the DataStream functions we have to implement diff --git a/stream/filters/buffer_stream.hpp b/stream/filters/buffer_stream.hpp index 0d22798d00..63b70000e1 100644 --- a/stream/filters/buffer_stream.hpp +++ b/stream/filters/buffer_stream.hpp @@ -37,7 +37,7 @@ class BufferStream : public MemoryStream { // We DON'T know how big the stream is. We'll have to read // it in increments. - const int ADD = 32*1024; + const unsigned int ADD = 32*1024; size_t len=0, newlen; while(!input->eof()) diff --git a/tools/str_exception.hpp b/tools/str_exception.hpp index 39120a3f9a..2695010558 100644 --- a/tools/str_exception.hpp +++ b/tools/str_exception.hpp @@ -4,7 +4,13 @@ #include #include -/// A simple exception that takes and holds a string +/** @brief A simple exception that takes and holds a string + + Usage: + + throw str_exception("message"); + + */ class str_exception : public std::exception { std::string msg; From eed875ae043c3c48fd23e18342c17d6f2331cc17 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 25 Jun 2010 16:00:07 +0200 Subject: [PATCH 052/269] readme update --- README.txt | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/README.txt b/README.txt index 0fdc19b050..f4849bebda 100644 --- a/README.txt +++ b/README.txt @@ -34,7 +34,7 @@ implementation of your choice (or of your making.) The Sound module, for example, currently has backends for OpenAL (output only), FFmpeg (input only) and for Audiere. Hopefully we'll -add IrrKlang, FMod, DirectSound, Miles and more in the future It can +add IrrKlang, FMod, DirectSound, Miles and more in the future. It can combine libraries to get more complete functionality (like using OpenAL for output and FFmpeg to decode sound files), and it's also easy to write your own backend if you're using a different (or @@ -74,22 +74,23 @@ you in many ways: user interface is often simpler than the exteral library one. - If you want to quickly connect different libraries together, it - really helps if they have speak a common language. The Mangle - interfaces are exactly that. Need to load Audiere sounds from a - weird archive format only implemented for PhysFS, all channeled - through the OGRE resource system? No problem! + really helps if they speak a common language. The Mangle interfaces + are exactly that - a common language between libraries. Do you need + Audiere to load sounds from a weird archive format only implemented + for PhysFS, all channeled through the OGRE resource system? No + problem! - If you are creating a library that depends on a specific feature (such as sound), but you don't want to lock your users into any specific sound library. Mangle works as an abstraction that lets your users select their own implementation. -- If you want to support multiple backends, or make it possible to - easily switch backends later. You can select backends at compile - time or even at runtime. For example you might want to switch to to - a commercial sound library at a later stage in development, or you - may want to use a different input library on console platforms than - on PC. +- If you want to support multiple backends for your game/app, or want + to make it possible to easily switch backends later. You can select + backends at compile time or even at runtime. For example you might + want to switch to to a commercial sound library at a later stage in + development, or you may want to use a different input library on + console platforms than on PC. The Mangle implementations are extremely light-weight - often just one or two cpp/h pairs per module. You can plug them directly into your @@ -105,15 +106,15 @@ Past and future --------------- Mangle started out as (and still is) a spin-off from OpenMW, another -project I am personally working on ( http://openmw.sourceforge.net ). -OpenMW is an attempt to recreate the engine behind the commercial game +project I am personally working on ( http://openmw.com/ ). OpenMW is +an attempt to recreate the engine behind the commercial game Morrowind, using only open source software. The projects are still tightly interlinked, and they will continue to be until OpenMW is finished. Most near-future work on Mangle will be -focused chiefly on OpenMW at the moment. However I will gladly -implement external contributions and suggestions that are not -OpenMW-related. +focused chiefly on OpenMW at the moment. However I will gladly include +external contributions and suggestions that are not OpenMW-related if +someone sends them to me. Conclusion From 96e456ee09cce69c18ef080a3c14038e22b0722c Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 30 Jun 2010 11:54:04 +0200 Subject: [PATCH 053/269] Added Mangle::Input with SDL and OIS implementation. WIP. --- Doxyfile | 2 +- input/driver.hpp | 95 ++++++++++++++++ input/servers/ois_driver.cpp | 148 +++++++++++++++++++++++++ input/servers/ois_driver.hpp | 48 ++++++++ input/servers/sdl_driver.cpp | 54 +++++++++ input/servers/sdl_driver.hpp | 27 +++++ input/tests/.gitignore | 2 + input/tests/Makefile | 12 ++ input/tests/common.cpp | 34 ++++++ input/tests/ois_driver_test.cpp | 51 +++++++++ input/tests/output/ois_driver_test.out | 5 + input/tests/output/sdl_driver_test.out | 5 + input/tests/plugins.cfg | 12 ++ input/tests/sdl_driver_test.cpp | 16 +++ input/tests/test.sh | 18 +++ testall.sh | 1 + 16 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 input/driver.hpp create mode 100644 input/servers/ois_driver.cpp create mode 100644 input/servers/ois_driver.hpp create mode 100644 input/servers/sdl_driver.cpp create mode 100644 input/servers/sdl_driver.hpp create mode 100644 input/tests/.gitignore create mode 100644 input/tests/Makefile create mode 100644 input/tests/common.cpp create mode 100644 input/tests/ois_driver_test.cpp create mode 100644 input/tests/output/ois_driver_test.out create mode 100644 input/tests/output/sdl_driver_test.out create mode 100644 input/tests/plugins.cfg create mode 100644 input/tests/sdl_driver_test.cpp create mode 100755 input/tests/test.sh diff --git a/Doxyfile b/Doxyfile index f2a1c74552..f3e0180029 100644 --- a/Doxyfile +++ b/Doxyfile @@ -564,7 +564,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = sound stream vfs +INPUT = sound stream vfs 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 diff --git a/input/driver.hpp b/input/driver.hpp new file mode 100644 index 0000000000..f8bf9ce099 --- /dev/null +++ b/input/driver.hpp @@ -0,0 +1,95 @@ +#ifndef MANGLE_INPUT_DRIVER_H +#define MANGLE_INPUT_DRIVER_H + +#include "../tools/shared_ptr.hpp" + +namespace Mangle +{ + namespace Input + { + /** Generic callback for input events. The meaning of the + parameters depend on the system producing the events. + */ + struct Event + { + /// Event types + enum EventType + { + EV_Unknown = -1, // Unknown event type + EV_KeyDown = 1, // Key, mouse or other button was pressed + EV_KeyUp = 2, // Key, mouse or other button was released + EV_MouseMove = 3, // Mouse movement (all axis movement?) + EV_Other = 4 // Other event + }; + + /** + Called upon all events. The first parameter give the event + type, the second gives additional data (usually the local + keysym as defined by the driver), and the pointer points to + the full custom event structure provided by the driver (the + type may vary depending on the EventType, this is defined in + the Driver documentation.) + */ + virtual void event(EventType type, int index, const void *p) = 0; + virtual ~Event() {} + }; + + /** Input::Driver is the main interface to any input system that + handles keyboard and/or mouse input, along with any other + input source like joysticks. + + It is really a generalized event system, and could also be + used for non-input related events. The definition of the event + codes and structures are entirely dependent on the + implementation. + + A system-independent key code list will be found in keys.hpp, + and input drivers should privide optional translations to/from + this list for full compatibility. + */ + struct Driver + { + virtual ~Driver() {} + + /** Captures input and produces the relevant events from it. An + event callback must be set with setEvent(), or all events + will be ignored. + */ + virtual void capture() = 0; + + /** Check the state of a given key or button. The key/button + definitions depends on the driver. + */ + virtual bool isDown(int index) = 0; + + /** Show or hide system mouse cursor + */ + virtual void showMouse(bool show) = 0; + + /** Set the event handler for input events. The evt->event() + function is called for each event. The meaning of the index + and *p parameters will be specific to each driver and to + each input system. + */ + void setEvent(Event *evt) + { event = evt; } + + /** Instigate an event. Is used internally for all events, but + can also be called from the outside to "fake" events from + this driver. + */ + void makeEvent(Event::EventType type, int index, const void *p=NULL) + { + if(event) + event->event(type,index,p); + } + + private: + /// Holds the event callback set byt setEvent() + Event *event; + }; + + typedef boost::shared_ptr DriverPtr; + } +} +#endif diff --git a/input/servers/ois_driver.cpp b/input/servers/ois_driver.cpp new file mode 100644 index 0000000000..eabe9739d0 --- /dev/null +++ b/input/servers/ois_driver.cpp @@ -0,0 +1,148 @@ +#include "ois_driver.hpp" + +#include +#include +#include +#include + +#ifdef __APPLE_CC__ +#include +#endif + +using namespace Mangle::Input; +using namespace OIS; + +struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener +{ + OISDriver &drv; + + OISListener(OISDriver &driver) + : drv(driver) {} + + bool keyPressed( const OIS::KeyEvent &arg ) + { + drv.makeEvent(Event::EV_KeyDown, arg.key, &arg); + return true; + } + + bool keyReleased( const OIS::KeyEvent &arg ) + { + drv.makeEvent(Event::EV_KeyUp, arg.key, &arg); + return true; + } + + bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + // Mouse button events are handled as key events + // TODO: Translate mouse buttons into pseudo-keysyms + drv.makeEvent(Event::EV_KeyDown, -1, &arg); + return true; + } + + bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) + { + // TODO: ditto + drv.makeEvent(Event::EV_KeyUp, -1, &arg); + return true; + } + + bool mouseMoved( const OIS::MouseEvent &arg ) + { + drv.makeEvent(Event::EV_MouseMove, -1, &arg); + return true; + } +}; + +OISDriver::OISDriver(Ogre::RenderWindow *window, bool exclusive) +{ + assert(window); + + size_t windowHnd; + + window->getCustomAttribute("WINDOW", &windowHnd); + + std::ostringstream windowHndStr; + ParamList pl; + + windowHndStr << windowHnd; + pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); + + // Set non-exclusive mouse and keyboard input if the user requested + // it. + if(!exclusive) + { +#if defined OIS_WIN32_PLATFORM + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_FOREGROUND" ))); + pl.insert(std::make_pair(std::string("w32_mouse"), + std::string("DISCL_NONEXCLUSIVE"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_FOREGROUND"))); + pl.insert(std::make_pair(std::string("w32_keyboard"), + std::string("DISCL_NONEXCLUSIVE"))); +#elif defined OIS_LINUX_PLATFORM + pl.insert(std::make_pair(std::string("x11_mouse_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_mouse_hide"), + std::string("false"))); + pl.insert(std::make_pair(std::string("x11_keyboard_grab"), + std::string("false"))); + pl.insert(std::make_pair(std::string("XAutoRepeatOn"), + std::string("true"))); +#endif + } + +#ifdef __APPLE_CC__ + // Give the application window focus to receive input events + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); +#endif + + inputMgr = InputManager::createInputSystem( pl ); + + // Create all devices + keyboard = static_cast(inputMgr->createInputObject + ( OISKeyboard, true )); + mouse = static_cast(inputMgr->createInputObject + ( OISMouse, true )); + + // Set mouse region + const MouseState &ms = mouse->getMouseState(); + ms.width = window->getWidth(); + ms.height = window->getHeight(); + + // Set up the input listener + listener = new OISListener(*this); + keyboard-> setEventCallback(listener); + mouse-> setEventCallback(listener); +} + +OISDriver::~OISDriver() +{ + // Delete the listener object + if(listener) + delete listener; + + if(inputMgr == NULL) return; + + // Kill the input systems. This will reset input options such as key + // repeat rate. + inputMgr->destroyInputObject(keyboard); + inputMgr->destroyInputObject(mouse); + InputManager::destroyInputSystem(inputMgr); + inputMgr = NULL; +} + +void OISDriver::capture() +{ + // Capture keyboard and mouse events + keyboard->capture(); + mouse->capture(); +} + +bool OISDriver::isDown(int index) +{ + // TODO: Extend to mouse buttons as well + return keyboard->isKeyDown((OIS::KeyCode)index); +} diff --git a/input/servers/ois_driver.hpp b/input/servers/ois_driver.hpp new file mode 100644 index 0000000000..ba780c39e6 --- /dev/null +++ b/input/servers/ois_driver.hpp @@ -0,0 +1,48 @@ +#ifndef MANGLE_INPUT_OIS_DRIVER_H +#define MANGLE_INPUT_OIS_DRIVER_H + +#include "../driver.hpp" + +namespace OIS +{ + class InputManager; + class Mouse; + class Keyboard; +} + +namespace Ogre +{ + class RenderWindow; +} + +namespace Mangle +{ + namespace Input + { + struct OISListener; + + /** Input driver for OIS, the input manager typically used with + Ogre. + */ + struct OISDriver : Driver + { + /// If exclusive=true, then we capture mouse and keyboard from + /// the OS. + OISDriver(Ogre::RenderWindow *window, bool exclusive=true); + ~OISDriver(); + + void capture(); + bool isDown(int index); + /// Not currently supported. + void showMouse(bool) {} + + private: + OIS::InputManager *inputMgr; + OIS::Mouse *mouse; + OIS::Keyboard *keyboard; + + OISListener *listener; + }; + } +} +#endif diff --git a/input/servers/sdl_driver.cpp b/input/servers/sdl_driver.cpp new file mode 100644 index 0000000000..f1d92dd411 --- /dev/null +++ b/input/servers/sdl_driver.cpp @@ -0,0 +1,54 @@ +#include "sdl_driver.hpp" + +#include + +using namespace Mangle::Input; + +void SDLDriver::capture() +{ + // Poll for events + SDL_Event evt; + while(SDL_PollEvent(&evt)) + { + Event::EventType type = Event::EV_Unknown; + int index = -1; + + switch(evt.type) + { + // For key events, send the keysym as the index. + case SDL_KEYDOWN: + type = Event::EV_KeyDown; + index = evt.key.keysym.sym; + break; + case SDL_KEYUP: + type = Event::EV_KeyUp; + index = evt.key.keysym.sym; + break; + case SDL_MOUSEMOTION: + type = Event::EV_MouseMove; + break; + // Add more event types later + } + + // Pass the event along, using -1 as index for unidentified + // event types. + makeEvent(type, index, &evt); + } +} + +bool SDLDriver::isDown(int index) +{ + int num; + Uint8 *keys = SDL_GetKeyState(&num); + assert(index >= 0 && index < num); + + // The returned array from GetKeyState is indexed by the + // SDLK_KEYNAME enums and is just a list of bools. If the indexed + // value is true, the button is down. + return keys[index]; +} + +void SDLDriver::showMouse(bool show) +{ + SDL_ShowCursor(show?SDL_ENABLE:SDL_DISABLE); +} diff --git a/input/servers/sdl_driver.hpp b/input/servers/sdl_driver.hpp new file mode 100644 index 0000000000..b71346cba1 --- /dev/null +++ b/input/servers/sdl_driver.hpp @@ -0,0 +1,27 @@ +#ifndef MANGLE_INPUT_SDL_DRIVER_H +#define MANGLE_INPUT_SDL_DRIVER_H + +#include "../driver.hpp" + +namespace Mangle +{ + namespace Input + { + /** Input driver for SDL. As the input system of SDL is seldomly + used alone (most often along with the video system), it is + assumed that you do your own initialization and cleanup of SDL + before and after using this driver. + + The Event.event() calls will be given the proper EV_ type, the + key index (for key up/down events), and a pointer to the full + SDL_Event structure. + */ + struct SDLDriver : Driver + { + void capture(); + bool isDown(int index); + void showMouse(bool); + }; + } +} +#endif diff --git a/input/tests/.gitignore b/input/tests/.gitignore new file mode 100644 index 0000000000..460c76f00b --- /dev/null +++ b/input/tests/.gitignore @@ -0,0 +1,2 @@ +*_test +ogre.cfg diff --git a/input/tests/Makefile b/input/tests/Makefile new file mode 100644 index 0000000000..b7d7b5b4ed --- /dev/null +++ b/input/tests/Makefile @@ -0,0 +1,12 @@ +GCC=g++ + +all: sdl_driver_test ois_driver_test + +sdl_driver_test: sdl_driver_test.cpp + $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL + +ois_driver_test: ois_driver_test.cpp + $(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem + +clean: + rm *_test diff --git a/input/tests/common.cpp b/input/tests/common.cpp new file mode 100644 index 0000000000..33f2188e98 --- /dev/null +++ b/input/tests/common.cpp @@ -0,0 +1,34 @@ +#include +#include "../driver.hpp" +#include +using namespace std; +using namespace Mangle::Input; + +Driver *input; + +struct MyCB : Event +{ + void event(Event::EventType type, int i, const void *p) + { + cout << "got event: type=" << type << " index=" << i << endl; + } +} mycb; +void mainLoop(int argc, int quitKey) +{ + cout << "Hold the Q key to quit:\n"; + input->setEvent(&mycb); + while(!input->isDown(quitKey)) + { + input->capture(); + usleep(20000); + + if(argc == 1) + { + cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; + break; + } + } + + delete input; + cout << "\nBye bye!\n"; +} diff --git a/input/tests/ois_driver_test.cpp b/input/tests/ois_driver_test.cpp new file mode 100644 index 0000000000..386f24055e --- /dev/null +++ b/input/tests/ois_driver_test.cpp @@ -0,0 +1,51 @@ +#include "common.cpp" + +#include "../servers/ois_driver.hpp" +#include +#include +#include + +bool isFile(const char *name) +{ + boost::filesystem::path cfg_file_path(name); + return boost::filesystem::exists(cfg_file_path); +} + +using namespace Ogre; +using namespace OIS; + +Root *root; +RenderWindow *window; + +void setupOgre() +{ + // Disable logging + new LogManager; + Log *log = LogManager::getSingleton().createLog(""); + log->setDebugOutputEnabled(false); + + bool useConfig = isFile("ogre.cfg"); + + // Set up Root + root = new Root("plugins.cfg", "ogre.cfg", ""); + + // Configure + if(!useConfig) + root->showConfigDialog(); + else + root->restoreConfig(); + + // Initialize OGRE window + window = root->initialise(true, "test", ""); +} + +int main(int argc, char** argv) +{ + setupOgre(); + input = new OISDriver(window); + + mainLoop(argc, KC_Q); + + delete root; + return 0; +} diff --git a/input/tests/output/ois_driver_test.out b/input/tests/output/ois_driver_test.out new file mode 100644 index 0000000000..eb9c29bf47 --- /dev/null +++ b/input/tests/output/ois_driver_test.out @@ -0,0 +1,5 @@ +Hold the Q key to quit: +got event: type=3 index=-1 +You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly + +Bye bye! diff --git a/input/tests/output/sdl_driver_test.out b/input/tests/output/sdl_driver_test.out new file mode 100644 index 0000000000..54e941fb66 --- /dev/null +++ b/input/tests/output/sdl_driver_test.out @@ -0,0 +1,5 @@ +Hold the Q key to quit: +got event: type=-1 index=-1 +You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly + +Bye bye! diff --git a/input/tests/plugins.cfg b/input/tests/plugins.cfg new file mode 100644 index 0000000000..57ec54e1a0 --- /dev/null +++ b/input/tests/plugins.cfg @@ -0,0 +1,12 @@ +# Defines plugins to load + +# Define plugin folder +PluginFolder=/usr/local/lib/OGRE/ + +# Define plugins +Plugin=RenderSystem_GL +Plugin=Plugin_ParticleFX +Plugin=Plugin_OctreeSceneManager +# Plugin=Plugin_CgProgramManager + + diff --git a/input/tests/sdl_driver_test.cpp b/input/tests/sdl_driver_test.cpp new file mode 100644 index 0000000000..5db6dbba8f --- /dev/null +++ b/input/tests/sdl_driver_test.cpp @@ -0,0 +1,16 @@ +#include "common.cpp" + +#include "../servers/sdl_driver.hpp" +#include + +int main(int argc, char** argv) +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); + input = new SDLDriver(); + + mainLoop(argc, SDLK_q); + + SDL_Quit(); + return 0; +} diff --git a/input/tests/test.sh b/input/tests/test.sh new file mode 100755 index 0000000000..2d07708adc --- /dev/null +++ b/input/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/testall.sh b/testall.sh index f38939a198..31b8678c2f 100755 --- a/testall.sh +++ b/testall.sh @@ -11,4 +11,5 @@ function run() run stream run vfs run sound +run input run . From 2b5d574ac78102b39c9f4b3c949ea2d468f4c11d Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 1 Jul 2010 10:31:51 +0200 Subject: [PATCH 054/269] Split Event out to separate file. --- input/driver.hpp | 30 ++---------------------------- input/event.hpp | 36 ++++++++++++++++++++++++++++++++++++ input/servers/sdl_driver.cpp | 2 +- input/tests/common.cpp | 2 +- 4 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 input/event.hpp diff --git a/input/driver.hpp b/input/driver.hpp index f8bf9ce099..d95fa206e1 100644 --- a/input/driver.hpp +++ b/input/driver.hpp @@ -2,38 +2,12 @@ #define MANGLE_INPUT_DRIVER_H #include "../tools/shared_ptr.hpp" +#include "event.hpp" namespace Mangle { namespace Input { - /** Generic callback for input events. The meaning of the - parameters depend on the system producing the events. - */ - struct Event - { - /// Event types - enum EventType - { - EV_Unknown = -1, // Unknown event type - EV_KeyDown = 1, // Key, mouse or other button was pressed - EV_KeyUp = 2, // Key, mouse or other button was released - EV_MouseMove = 3, // Mouse movement (all axis movement?) - EV_Other = 4 // Other event - }; - - /** - Called upon all events. The first parameter give the event - type, the second gives additional data (usually the local - keysym as defined by the driver), and the pointer points to - the full custom event structure provided by the driver (the - type may vary depending on the EventType, this is defined in - the Driver documentation.) - */ - virtual void event(EventType type, int index, const void *p) = 0; - virtual ~Event() {} - }; - /** Input::Driver is the main interface to any input system that handles keyboard and/or mouse input, along with any other input source like joysticks. @@ -78,7 +52,7 @@ namespace Mangle can also be called from the outside to "fake" events from this driver. */ - void makeEvent(Event::EventType type, int index, const void *p=NULL) + void makeEvent(Event::Type type, int index, const void *p=NULL) { if(event) event->event(type,index,p); diff --git a/input/event.hpp b/input/event.hpp new file mode 100644 index 0000000000..5948653bd9 --- /dev/null +++ b/input/event.hpp @@ -0,0 +1,36 @@ +#ifndef MANGLE_INPUT_EVENT_H +#define MANGLE_INPUT_EVENT_H + +namespace Mangle +{ + namespace Input + { + /** Generic callback for input events. The meaning of the + parameters depend on the system producing the events. + */ + struct Event + { + /// Event types + enum Type + { + EV_Unknown = -1, // Unknown event type + EV_KeyDown = 1, // Key, mouse or other button was pressed + EV_KeyUp = 2, // Key, mouse or other button was released + EV_MouseMove = 3, // Mouse movement (all axis movement?) + EV_Other = 4 // Other event + }; + + /** + Called upon all events. The first parameter give the event + type, the second gives additional data (usually the local + keysym as defined by the driver), and the pointer points to + the full custom event structure provided by the driver (the + type may vary depending on the EventType, this is defined in + the Driver documentation.) + */ + virtual void event(Type type, int index, const void *p) = 0; + virtual ~Event() {} + }; + } +} +#endif diff --git a/input/servers/sdl_driver.cpp b/input/servers/sdl_driver.cpp index f1d92dd411..93884a6e6c 100644 --- a/input/servers/sdl_driver.cpp +++ b/input/servers/sdl_driver.cpp @@ -10,7 +10,7 @@ void SDLDriver::capture() SDL_Event evt; while(SDL_PollEvent(&evt)) { - Event::EventType type = Event::EV_Unknown; + Event::Type type = Event::EV_Unknown; int index = -1; switch(evt.type) diff --git a/input/tests/common.cpp b/input/tests/common.cpp index 33f2188e98..47a0c53de0 100644 --- a/input/tests/common.cpp +++ b/input/tests/common.cpp @@ -8,7 +8,7 @@ Driver *input; struct MyCB : Event { - void event(Event::EventType type, int i, const void *p) + void event(Event::Type type, int i, const void *p) { cout << "got event: type=" << type << " index=" << i << endl; } From 2139bb78afd59627fe22120f54b2e079caaff387 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 3 Jul 2010 17:11:28 +0200 Subject: [PATCH 055/269] fixed missing NULL initialization in input/driver.hpp --- input/driver.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/input/driver.hpp b/input/driver.hpp index d95fa206e1..1c1e950478 100644 --- a/input/driver.hpp +++ b/input/driver.hpp @@ -23,6 +23,7 @@ namespace Mangle */ struct Driver { + Driver() : event(NULL) {} virtual ~Driver() {} /** Captures input and produces the relevant events from it. An From 7583fc3f1bfa6d0fde56c925da959a5e9e50031a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 3 Jul 2010 18:46:21 +0200 Subject: [PATCH 056/269] Added 2D rendring component with SDL backend. --- rend2d/driver.hpp | 61 ++++++++++ rend2d/servers/sdl_driver.cpp | 199 +++++++++++++++++++++++++++++++ rend2d/servers/sdl_driver.hpp | 116 ++++++++++++++++++ rend2d/sprite.hpp | 34 ++++++ rend2d/tests/.gitignore | 1 + rend2d/tests/Makefile | 9 ++ rend2d/tests/output/sdl_test.out | 11 ++ rend2d/tests/sdl_test.cpp | 65 ++++++++++ rend2d/tests/test.sh | 18 +++ rend2d/tests/tile1-blue.png | Bin 0 -> 273 bytes rend2d/tests/tile1-yellow.png | Bin 0 -> 257 bytes testall.sh | 1 + vfs/clients/ogre_archive.hpp | 3 + 13 files changed, 518 insertions(+) create mode 100644 rend2d/driver.hpp create mode 100644 rend2d/servers/sdl_driver.cpp create mode 100644 rend2d/servers/sdl_driver.hpp create mode 100644 rend2d/sprite.hpp create mode 100644 rend2d/tests/.gitignore create mode 100644 rend2d/tests/Makefile create mode 100644 rend2d/tests/output/sdl_test.out create mode 100644 rend2d/tests/sdl_test.cpp create mode 100755 rend2d/tests/test.sh create mode 100644 rend2d/tests/tile1-blue.png create mode 100644 rend2d/tests/tile1-yellow.png diff --git a/rend2d/driver.hpp b/rend2d/driver.hpp new file mode 100644 index 0000000000..7366e04625 --- /dev/null +++ b/rend2d/driver.hpp @@ -0,0 +1,61 @@ +#ifndef MANGLE_REND2D_DRIVER_H +#define MANGLE_REND2D_DRIVER_H + +#include +#include "sprite.hpp" + +namespace Mangle +{ + namespace Rend2D + { + /** + The driver is the connection to the backend system that powers + 2D sprite rendering. For example the backend could be SDL or + any other 2D-capable graphics library. + */ + struct Driver + { + /// Get the screen sprite + virtual Sprite *getScreen() = 0; + + /// Sets the video mode. + virtual void setVideoMode(int width, int height, int bpp=32, bool fullscreen=false) = 0; + + /** Update the screen. Until this function is called, none of + the changes written to the screen sprite will be visible. + */ + virtual void update() = 0; + + /// Set the window title, as well as the title of the window + /// when "iconified" + virtual void setWindowTitle(const std::string &title, + const std::string &icon) = 0; + + /// Set the window title + void setWindowTitle(const std::string &title) { setWindowTitle(title,title); } + + /// Load sprite from an image file + virtual Sprite* loadImage(const std::string &file) = 0; + + /// Load a sprite from an image file stored in memory. + virtual Sprite* loadImage(const void* data, size_t size) = 0; + + /** @brief Set gamma value for all colors. + + Note: Setting this in windowed mode will affect the ENTIRE + SCREEN! + */ + virtual void setGamma(float gamma) = 0; + + /// Set gamma individually for red, green, blue + virtual void setGamma(float red, float green, float blue) = 0; + + /// Get screen width + virtual int width() = 0; + + /// Get screen height + virtual int height() = 0; + }; + } +} +#endif diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp new file mode 100644 index 0000000000..cc102e6615 --- /dev/null +++ b/rend2d/servers/sdl_driver.cpp @@ -0,0 +1,199 @@ +#include "sdl_driver.hpp" + +#include +#include +#include "../../tools/str_exception.hpp" +#include + +using namespace Mangle::Rend2D; + +void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite + int x, int y, // Destination position + int sx, int sy, // Source position + int w, int h // Amount to draw. -1 means remainder. + ) +{ + // Get source surface + SDL_Sprite *other = dynamic_cast(s); + assert(other != NULL); + SDL_Surface *img = other->getSurface(); + + // Check coordinate validity + assert(sx <= img->w && sy <= img->h); + assert(x <= surface->w && y <= surface->h); + assert(sx >= 0 && sy >= 0); + + // Compute width and height if necessary + if(w == -1) w = img->w - sx; + if(h == -1) h = img->h - sy; + + // Check them if they're valid + assert(w >= 0 && w <= img->w); + assert(h >= 0 && h <= img->h); + + SDL_Rect dest; + dest.x = x; + dest.y = y; + dest.w = w; + dest.h = h; + + SDL_Rect src; + src.x = sx; + src.y = sy; + src.w = w; + src.h = h; + + // Do the Blitman + SDL_BlitSurface(img, &src, surface, &dest); +} + +SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete) + : surface(s), autoDel(autoDelete) +{ + assert(surface != NULL); +} + +SDL_Sprite::~SDL_Sprite() +{ + if(autoDel) + SDL_FreeSurface(surface); +} + +void SDL_Sprite::fill(int value) +{ + SDL_FillRect(surface, NULL, value); +} + +int SDL_Sprite::width() { return surface->w; } +int SDL_Sprite::height() { return surface->h; } + +SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false) +{ + if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) + throw str_exception("Error initializing SDL video"); +} +SDLDriver::~SDLDriver() +{ + if(display) delete display; + SDL_Quit(); +} + +void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) +{ + unsigned int flags; + + if(display) delete display; + + if (fullscreen) + // Assume fullscreen mode allows a double-bufferd hardware + // mode. We need more test code for this to be safe though. + flags = SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; + else + flags = SDL_SWSURFACE; + + // Create the surface and check it + realDisp = SDL_SetVideoMode(width, height, bpp, flags); + if(realDisp == NULL) + throw str_exception("Failed setting SDL video mode"); + + // Code for software double buffering. I haven't found this to be + // any speed advantage at all in windowed mode (it's slower, as one + // would expect.) Not properly tested in fullscreen mode with + // hardware buffers, but it will probably only be an improvement if + // we do excessive writing (ie. write each pixel on average more + // than once) or try to read from the display buffer. + if(softDouble) + { + // Make a new surface with the same attributes as the real + // display surface. + SDL_Surface *back = SDL_DisplayFormat(realDisp); + assert(back != NULL); + + // Create a sprite representing the double buffer + display = new SDL_Sprite(back); + } + else + { + // Create a sprite directly representing the display surface. + // The 'false' parameter means do not autodelete the screen + // surface upon exit (since SDL manages it) + display = new SDL_Sprite(realDisp, false); + } +} + +/// Update the screen +void SDLDriver::update() +{ + // Blit the soft double buffer onto the real display buffer + if(softDouble) + SDL_BlitSurface(display->getSurface(), NULL, realDisp, NULL ); + + if(realDisp) + SDL_Flip(realDisp); +} + +/// Set the window title, as well as the title of the window when +/// "iconified" +void SDLDriver::setWindowTitle(const std::string &title, + const std::string &icon) +{ + SDL_WM_SetCaption( title.c_str(), icon.c_str() ); +} + +// Convert the given surface to display format. +static SDL_Surface* convertImage(SDL_Surface* surf) +{ + if(surf != NULL) + { + // Convert the image to the display buffer format, for faster + // blitting + SDL_Surface *surf2 = SDL_DisplayFormat(surf); + SDL_FreeSurface(surf); + surf = surf2; + } + return surf; +} + +/// Load sprite from an image file, using SDL_image. +Sprite* SDLDriver::loadImage(const std::string &file) +{ + SDL_Surface *surf = IMG_Load(file.c_str()); + surf = convertImage(surf); + if(surf == NULL) + throw str_exception("SDL failed to load image file '" + file + "'"); + return spriteFromSDL(surf); +} + +/// Load sprite from an SDL_RWops structure. autoFree determines +/// whether the RWops struct should be closed/freed after use. +Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree) +{ + SDL_Surface *surf = IMG_Load_RW(src, autoFree); + surf = convertImage(surf); + if(surf == NULL) + throw str_exception("SDL failed to load image"); + return spriteFromSDL(surf); +} + +/// Load a sprite from an image file stored in memory. Uses +/// SDL_image. +Sprite* SDLDriver::loadImage(const void* data, size_t size) +{ + SDL_RWops *rw = SDL_RWFromConstMem(data, size); + return loadImage(rw, true); +} + +void SDLDriver::setGamma(float red, float green, float blue) +{ + SDL_SetGamma(red,green,blue); +} + +/// Convert an existing SDL surface into a sprite +Sprite* SDLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) +{ + assert(surf); + return new SDL_Sprite(surf, autoFree); +} + +void SDLDriver::sleep(int ms) { SDL_Delay(ms); } +unsigned int SDLDriver::ticks() { return SDL_GetTicks(); } diff --git a/rend2d/servers/sdl_driver.hpp b/rend2d/servers/sdl_driver.hpp new file mode 100644 index 0000000000..9aeed2f926 --- /dev/null +++ b/rend2d/servers/sdl_driver.hpp @@ -0,0 +1,116 @@ +#ifndef MANGLE_DRAW2D_SDL_H +#define MANGLE_DRAW2D_SDL_H + +#include "../driver.hpp" + +// Predeclarations keep the streets safe at night +struct SDL_Surface; +struct SDL_RWops; + +namespace Mangle +{ + namespace Rend2D + { + /// SDL-implementation of Sprite + struct SDL_Sprite : Sprite + { + /** Draw a sprite in the given position. Can only draw other SDL + sprites. + */ + void draw(Sprite *s, // Must be SDL_Sprite + int x, int y, // Destination position + int sx=0, int sy=0, // Source position + int w=-1, int h=-1 // Amount to draw. -1 means remainder. + ); + + SDL_Sprite(SDL_Surface *s, bool autoDelete=true); + ~SDL_Sprite(); + + // Information retrieval + int width(); + int height(); + SDL_Surface *getSurface() { return surface; } + + // Fill with a given pixel value + void fill(int value); + + private: + // The SDL surface + SDL_Surface* surface; + + // If true, delete this surface when the canvas is destructed + bool autoDel; + }; + + class SDLDriver : public Driver + { + // The main display surface + SDL_Sprite *display; + + // The actual display surface. May or may not be the same + // surface pointed to by 'display' above, depending on the + // softDouble flag. + SDL_Surface *realDisp; + + // If true, we do software double buffering. + bool softDouble; + + public: + SDLDriver(); + ~SDLDriver(); + + /// Sets the video mode. Will create the window if it is not + /// already set up. Note that for SDL, bpp=0 means use current + /// bpp. + void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); + + /// Update the screen + void update(); + + /// Set the window title, as well as the title of the window + /// when "iconified" + void setWindowTitle(const std::string &title, + const std::string &icon); + + // Include overloads from our Glorious parent + using Driver::setWindowTitle; + + /// Load sprite from an image file, using SDL_image. + Sprite* loadImage(const std::string &file); + + /// Load sprite from an SDL_RWops structure. autoFree determines + /// whether the RWops struct should be closed/freed after use. + Sprite* loadImage(SDL_RWops *src, bool autoFree=false); + + /// Load a sprite from an image file stored in memory. Uses + /// SDL_image. + Sprite* loadImage(const void* data, size_t size); + + /// Set gamma value + void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } + + /// Set gamma individually for red, green, blue + void setGamma(float red, float green, float blue); + + /// Convert an existing SDL surface into a sprite + Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); + + // Get width and height + int width() { return display ? display->width() : 0; } + int height() { return display ? display->height() : 0; } + + /// Get the screen sprite + Sprite *getScreen() { return display; } + + /// Not really a graphic-related function, but very + /// handly. Sleeps the given number of milliseconds using + /// SDL_Delay(). + void sleep(int ms); + + /// Get the number of ticks since SDL initialization, using + /// SDL_GetTicks(). + unsigned int ticks(); + }; + } +} +#endif diff --git a/rend2d/sprite.hpp b/rend2d/sprite.hpp new file mode 100644 index 0000000000..150d97e4e7 --- /dev/null +++ b/rend2d/sprite.hpp @@ -0,0 +1,34 @@ +#ifndef MANGLE_REND2D_SPRITE_H +#define MANGLE_REND2D_SPRITE_H + +namespace Mangle +{ + namespace Rend2D + { + /** + A Sprite is either a bitmap to be drawn or an output of area + for blitting other bitmaps, or both. They are created by the + Driver. + */ + struct Sprite + { + /// Draw a sprite in the given position + virtual void draw(Sprite *s, // The sprite to draw + int x, int y, // Destination position + int sx=0, int sy=0, // Source position + int w=-1, int h=-1 // Amount to draw. -1 means remainder. + ) = 0; + + virtual ~Sprite() {} + + // Information retrieval + virtual int width() = 0; + virtual int height() = 0; + + /// Fill the sprite with the given pixel value. The pixel format + /// depends on the format of the sprite. + virtual void fill(int value) = 0; + }; + } +} +#endif diff --git a/rend2d/tests/.gitignore b/rend2d/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/rend2d/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/rend2d/tests/Makefile b/rend2d/tests/Makefile new file mode 100644 index 0000000000..c2e16c7565 --- /dev/null +++ b/rend2d/tests/Makefile @@ -0,0 +1,9 @@ +GCC=g++ + +all: sdl_test + +sdl_test: sdl_test.cpp + $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image + +clean: + rm *_test diff --git a/rend2d/tests/output/sdl_test.out b/rend2d/tests/output/sdl_test.out new file mode 100644 index 0000000000..4528e1a98a --- /dev/null +++ b/rend2d/tests/output/sdl_test.out @@ -0,0 +1,11 @@ +Loading SDL driver. +Creating window. +Current mode: 640x480 +Setting fancy title, cause we like fancy titles. +Loading tile1-blue.png from file. +Loading tile1-yellow.png from memory. +Going bananas. +Taking a breather. +WOW DID YOU SEE THAT!? +Mucking about with the gamma settings +Done. diff --git a/rend2d/tests/sdl_test.cpp b/rend2d/tests/sdl_test.cpp new file mode 100644 index 0000000000..0355112e61 --- /dev/null +++ b/rend2d/tests/sdl_test.cpp @@ -0,0 +1,65 @@ +#include +#include + +using namespace std; + +#include "../servers/sdl_driver.hpp" + +using namespace Mangle::Rend2D; + +int main() +{ + cout << "Loading SDL driver.\n"; + SDLDriver sdl; + + cout << "Creating window.\n"; + sdl.setVideoMode(640,480); + cout << "Current mode: " << sdl.width() << "x" << sdl.height() << endl; + + cout << "Setting fancy title, cause we like fancy titles.\n"; + sdl.setWindowTitle("Chief executive window"); + + // Display surface + Sprite *screen = sdl.getScreen(); + + const char* imgName = "tile1-blue.png"; + cout << "Loading " << imgName << " from file.\n"; + Sprite *image = sdl.loadImage(imgName); + + const char* imgName2 = "tile1-yellow.png"; + cout << "Loading " << imgName2 << " from memory.\n"; + Sprite *image2; + { + // This is hard-coded for file sizes below 500 bytes, so obviously + // you shouldn't mess with the image files. + ifstream file(imgName2, ios::binary); + char buf[500]; + file.read(buf, 500); + int size = file.gcount(); + image2 = sdl.loadImage(buf, size); + } + + cout << "Going bananas.\n"; + for(int i=1; i<20; i++) + screen->draw(image, 30*i, 20*i); + + cout << "Taking a breather.\n"; + sdl.update(); + for(int i=1; i<20; i++) + screen->draw(image2, 30*(20-i), 20*i); + sdl.sleep(800); + sdl.update(); + cout << "WOW DID YOU SEE THAT!?\n"; + sdl.sleep(800); + + cout << "Mucking about with the gamma settings\n"; + sdl.setGamma(2.0, 0.1, 0.8); + sdl.sleep(100); + sdl.setGamma(0.6, 2.1, 2.1); + sdl.sleep(100); + sdl.setGamma(1.6); + sdl.sleep(100); + + cout << "Done.\n"; + return 0; +} diff --git a/rend2d/tests/test.sh b/rend2d/tests/test.sh new file mode 100755 index 0000000000..2d07708adc --- /dev/null +++ b/rend2d/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/rend2d/tests/tile1-blue.png b/rend2d/tests/tile1-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..066e6f8eb932e81d0ab391bc16a3b9e0fb063d7d GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgg4HgO>q^~cs6azG);64!_l=ltB< z)VvY~=c3falGGH1^30M91$R&1fbd2>aiF3cPZ!4!jq}MON!e)%tQ!)zB&BycNVusb zWdv<3H_ml_#P;5c#V!`t_IO|Ks0DIaVrPLG@TdWn2J z|KvBBkM=tLPH;V9awjzAOkRtr!tdY5?;n;qvu%fo@uXRM82JyY98GH4whCx3gQu&X J%Q~loCIFm~TL1t6 literal 0 HcmV?d00001 diff --git a/rend2d/tests/tile1-yellow.png b/rend2d/tests/tile1-yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..2aaf9015d313d5cd00915ffbb1df2f0262ea2116 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfEjDo>Htxq)+3JBpk|nMYCC>S|xv6<249-QVi6yBi3gww4 z84B*6z5(HleBwYwd7dtgAsXkCb@CIF6<9YUa9LVOJ4;MTQp*TBJ?;FFMT!6KZ8mro zxg%{yo!B3~hU~W2DM<=~6P7+aaBe@#)q{84nt0F0&sDtcsm}jj_2%Ux)y>}yc6DU# wSS@pBCTF(8x9=wp*PqRuS(uroVp_?}Ab(0lNY${m9cVLyr>mdKI;Vst0EqflTmS$7 literal 0 HcmV?d00001 diff --git a/testall.sh b/testall.sh index 31b8678c2f..b93fee2159 100755 --- a/testall.sh +++ b/testall.sh @@ -12,4 +12,5 @@ run stream run vfs run sound run input +run rend2d run . diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index 0ea9413669..a4986d7b9f 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -37,7 +37,10 @@ class MangleArchive : public Ogre::Archive time_t getModifiedTime(const Ogre::String& filename) { return vfs->stat(filename)->time; } + // With both these we support both OGRE 1.6 and 1.7 Ogre::DataStreamPtr open(const Ogre::String& filename) const; + Ogre::DataStreamPtr open(const Ogre::String& filename, bool readOnly) const + { return open(filename); } Ogre::StringVectorPtr list(bool recursive = true, bool dirs = false); Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false); From 1af84ad8526cb544c3908edb5f5b863a343b4c67 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sun, 4 Jul 2010 13:20:09 +0200 Subject: [PATCH 057/269] added readme, wee --- .gitignore | 2 ++ README | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 .gitignore create mode 100644 README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..d6ff91a9ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +*.o diff --git a/README b/README new file mode 100644 index 0000000000..621fe8d60c --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +OpenEngine README +================= + +OpenEngine is a bunch of stand-alone game engine modules collected from the OpenMW project (see http://github.com/korslund/openmw or http://openmw.com ) and from certain other projects. + +It is currently a very early work in progress, and development will follow OpenMW closely for a while forward. + +OpenEngine will depend heavily on Mangle ( http://github.com/korslund/mangle/ ) and will thus aim to be backend agnostic. When finished it should work with a variety for free and commercial middleware libraries as backends for graphics, sound, physics, input and so on. + +All questions can be directed to Nicolay Korslund at korslund@gmail.com + +- Nicolay From 5d73b47cc06803f416f1da969ac0450e80ff5199 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sun, 4 Jul 2010 14:28:51 +0200 Subject: [PATCH 058/269] Added input component --- .gitignore | 1 + .gitmodules | 3 + input/dispatch_map.hpp | 75 ++++++++++++++++ input/dispatcher.hpp | 49 +++++++++++ input/func_binder.hpp | 104 +++++++++++++++++++++++ input/poller.hpp | 46 ++++++++++ input/tests/Makefile | 18 ++++ input/tests/dispatch_map_test.cpp | 54 ++++++++++++ input/tests/funcbind_test.cpp | 47 ++++++++++ input/tests/output/dispatch_map_test.out | 18 ++++ input/tests/output/funcbind_test.out | 15 ++++ input/tests/output/sdl_binder_test.out | 4 + input/tests/output/sdl_driver_test.out | 4 + input/tests/sdl_binder_test.cpp | 71 ++++++++++++++++ input/tests/sdl_driver_test.cpp | 31 +++++++ input/tests/test.sh | 18 ++++ mangle | 1 + testall.sh | 11 +++ 18 files changed, 570 insertions(+) create mode 100644 .gitmodules create mode 100644 input/dispatch_map.hpp create mode 100644 input/dispatcher.hpp create mode 100644 input/func_binder.hpp create mode 100644 input/poller.hpp create mode 100644 input/tests/Makefile create mode 100644 input/tests/dispatch_map_test.cpp create mode 100644 input/tests/funcbind_test.cpp create mode 100644 input/tests/output/dispatch_map_test.out create mode 100644 input/tests/output/funcbind_test.out create mode 100644 input/tests/output/sdl_binder_test.out create mode 100644 input/tests/output/sdl_driver_test.out create mode 100644 input/tests/sdl_binder_test.cpp create mode 100644 input/tests/sdl_driver_test.cpp create mode 100755 input/tests/test.sh create mode 160000 mangle create mode 100755 testall.sh diff --git a/.gitignore b/.gitignore index d6ff91a9ea..23a5e931b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *~ *.o +*_test diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..0a9a9121fd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "mangle"] + path = mangle + url = git://github.com/korslund/mangle diff --git a/input/dispatch_map.hpp b/input/dispatch_map.hpp new file mode 100644 index 0000000000..356660e755 --- /dev/null +++ b/input/dispatch_map.hpp @@ -0,0 +1,75 @@ +#ifndef _INPUT_DISPATCHMAP_H +#define _INPUT_DISPATCHMAP_H + +#include +#include +#include + +namespace Input { + +/** + DispatchMap is a simple connection system that connects incomming + signals with outgoing signals. + + The signals can be connected one-to-one, many-to-one, one-to-many + or many-to-many. + + The dispatch map is completely system agnostic. It is a pure data + structure and all signals are just integer indices. It does not + delegate any actions, but used together with Dispatcher it can be + used to build an event system. + */ +struct DispatchMap +{ + typedef std::set OutList; + typedef std::map InMap; + + typedef OutList::iterator Oit; + typedef InMap::iterator Iit; + + InMap map; + + void bind(int in, int out) + { + map[in].insert(out); + } + + void unbind(int in, int out) + { + Iit it = map.find(in); + if(it != map.end()) + { + it->second.erase(out); + + // If there are no more elements, then remove the entire list + if(it->second.empty()) + map.erase(it); + } + } + + /// Check if a given input is bound to anything + bool isBound(int in) const + { + return map.find(in) != map.end(); + } + + /** + Get the list of outputs bound to the given input. Only call this + on inputs that you know are bound to something. + + The returned set is only intended for immediate iteration. Do not + store references to it. + */ + const OutList &getList(int in) const + { + assert(isBound(in)); + InMap::const_iterator it = map.find(in); + assert(it != map.end()); + const OutList &out = it->second; + assert(!out.empty()); + return out; + } +}; + +} +#endif diff --git a/input/dispatcher.hpp b/input/dispatcher.hpp new file mode 100644 index 0000000000..107211a49e --- /dev/null +++ b/input/dispatcher.hpp @@ -0,0 +1,49 @@ +#ifndef _INPUT_DISPATCHER_H +#define _INPUT_DISPATCHER_H + +#include "dispatch_map.hpp" +#include "func_binder.hpp" +#include + +namespace Input { + +struct Dispatcher : Mangle::Input::Event +{ + DispatchMap map; + FuncBinder funcs; + + /** + Constructor. Takes the number of actions and passes it to + FuncBinder. + */ + Dispatcher(int actions) : funcs(actions) {} + + void bind(int action, int key) { map.bind(key, action); } + void unbind(int action, int key) { map.unbind(key, action); } + bool isBound(int key) const { return map.isBound(key); } + + /** + Instigate an event. It is translated through the dispatch map and + sent to the function bindings. + */ + typedef DispatchMap::OutList _O; + void event(Type type, int index, const void* p) + { + // No bindings, nothing happens + if(!isBound(index)) + return; + + // Only treat key-down events for now + if(type != EV_KeyDown) + return; + + // Get the mapped actions and execute them + const _O &list = map.getList(index); + _O::const_iterator it; + for(it = list.begin(); it != list.end(); it++) + funcs.call(*it, p); + } +}; + +} +#endif diff --git a/input/func_binder.hpp b/input/func_binder.hpp new file mode 100644 index 0000000000..20119b07be --- /dev/null +++ b/input/func_binder.hpp @@ -0,0 +1,104 @@ +#ifndef _INPUT_FUNCBINDER_H +#define _INPUT_FUNCBINDER_H + +#include +#include +#include +#include + +namespace Input { + +/** + An Action defines the user defined action corresponding to a + binding. + + The first parameter is the action index that invoked this call. You + can assign the same function to multiple actions, and this can help + you keep track of which action was invoked. + + The second parameter is an optional user-defined parameter, + represented by a void pointer. In many cases it is practical to + point this to temporaries (stack values), so make sure not to store + permanent references to it unless you've planning for this on the + calling side as well. + */ +typedef boost::function Action; + +/** + The FuncBinder is a simple struct that binds user-defined indices + to functions. It is useful for binding eg. keyboard events to + specific actions in your program, but can potentially have many + other uses as well. + */ +class FuncBinder +{ + struct FuncBinding + { + std::string name; + Action action; + }; + + std::vector bindings; + +public: + /** + Constructor. Initialize the struct by telling it how many action + indices you intend to bind. + + The indices you use should be 0 <= i < number. + */ + FuncBinder(int number) : bindings(number) {} + + /** + Bind an action to an index. + */ + void bind(int index, Action action, const std::string &name="") + { + assert(index >= 0 && index < (int)bindings.size()); + + FuncBinding &fb = bindings[index]; + fb.action = action; + fb.name = name; + } + + /** + Unbind an index, reverting a previous bind(). + */ + void unbind(int index) + { + assert(index >= 0 && index < (int)bindings.size()); + + bindings[index] = FuncBinding(); + } + + /** + Call a specific action. Takes an optional parameter that is + passed to the action. + */ + void call(int index, const void *p=NULL) const + { + assert(index >= 0 && index < (int)bindings.size()); + + const FuncBinding &fb = bindings[index]; + if(fb.action) fb.action(index, p); + } + + /// Check if a given index is bound to anything + bool isBound(int index) const + { + assert(index >= 0 && index < (int)bindings.size()); + + return !bindings[index].action.empty(); + } + + /// Return the name associated with an action (empty if not bound) + const std::string &getName(int index) const + { + assert(index >= 0 && index < (int)bindings.size()); + + return bindings[index].name; + } +}; + +} +#endif diff --git a/input/poller.hpp b/input/poller.hpp new file mode 100644 index 0000000000..794f79b741 --- /dev/null +++ b/input/poller.hpp @@ -0,0 +1,46 @@ +#ifndef _INPUT_POLLER_H +#define _INPUT_POLLER_H + +#include "dispatch_map.hpp" +#include + +namespace Input { + +/** The poller is used to check (poll) for keys rather than waiting + for events. */ +struct Poller +{ + DispatchMap map; + Mangle::Input::Driver &input; + + Poller(Mangle::Input::Driver &drv) + : input(drv) {} + + /** Bind or unbind a given action with a key. The action is the first + parameter, the key is the second. + */ + void bind(int in, int out) { map.bind(in, out); } + void unbind(int in, int out) { map.unbind(in, out); } + bool isBound(int in) const { return map.isBound(in); } + + /// Check whether a given action button is currently pressed. + typedef DispatchMap::OutList _O; + bool isDown(int index) const + { + // No bindings, no action + if(!isBound(index)) + return false; + + // Get all the keys bound to this action, and check them. + const _O &list = map.getList(index); + _O::const_iterator it; + for(it = list.begin(); it != list.end(); it++) + // If there's any match, we're good to go. + if(input.isDown(*it)) return true; + + return false; + } +}; + +} +#endif diff --git a/input/tests/Makefile b/input/tests/Makefile new file mode 100644 index 0000000000..91a0b26636 --- /dev/null +++ b/input/tests/Makefile @@ -0,0 +1,18 @@ +GCC=g++ + +all: funcbind_test dispatch_map_test sdl_driver_test sdl_binder_test + +funcbind_test: funcbind_test.cpp ../func_binder.hpp + $(GCC) $< -o $@ + +dispatch_map_test: dispatch_map_test.cpp ../dispatch_map.hpp + $(GCC) $< -o $@ + +sdl_driver_test: sdl_driver_test.cpp + $(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../ + +sdl_binder_test: sdl_binder_test.cpp + $(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../ + +clean: + rm *_test diff --git a/input/tests/dispatch_map_test.cpp b/input/tests/dispatch_map_test.cpp new file mode 100644 index 0000000000..5cc7e1ef29 --- /dev/null +++ b/input/tests/dispatch_map_test.cpp @@ -0,0 +1,54 @@ +#include +using namespace std; + +#include "../dispatch_map.hpp" + +using namespace Input; + +typedef DispatchMap::OutList OutList; +typedef OutList::const_iterator Cit; + +void showList(const DispatchMap::OutList &out) +{ + for(Cit it = out.begin(); + it != out.end(); it++) + { + cout << " " << *it << endl; + } +} + +void showAll(DispatchMap &map) +{ + cout << "\nPrinting everything:\n"; + for(DispatchMap::Iit it = map.map.begin(); + it != map.map.end(); it++) + { + cout << it->first << ":\n"; + showList(map.getList(it->first)); + } +} + +int main() +{ + cout << "Testing the dispatch map\n"; + + DispatchMap dsp; + + dsp.bind(1,9); + dsp.bind(2,-5); + dsp.bind(2,9); + dsp.bind(3,10); + dsp.bind(3,12); + dsp.bind(3,10); + + showAll(dsp); + + dsp.unbind(1,9); + dsp.unbind(5,8); + dsp.unbind(3,11); + dsp.unbind(3,12); + dsp.unbind(3,12); + + showAll(dsp); + return 0; +} diff --git a/input/tests/funcbind_test.cpp b/input/tests/funcbind_test.cpp new file mode 100644 index 0000000000..4716f8b81f --- /dev/null +++ b/input/tests/funcbind_test.cpp @@ -0,0 +1,47 @@ +#include +using namespace std; + +#include "../func_binder.hpp" + +void f1(int i, const void *p) +{ + cout << " F1 i=" << i << endl; + + if(p) + cout << " Got a nice gift: " + << *((const float*)p) << endl; +} + +void f2(int i, const void *p) +{ + cout << " F2 i=" << i << endl; +} + +using namespace Input; + +int main() +{ + cout << "This will test the function binding system\n"; + + FuncBinder bnd(5); + + bnd.bind(0, &f1, "This is action 1"); + bnd.bind(1, &f2); + bnd.bind(2, &f1, "This is action 3"); + bnd.bind(3, &f2, "This is action 4"); + + bnd.unbind(2); + + for(int i=0; i<5; i++) + { + cout << "Calling " << i << ": '" << bnd.getName(i) << "'\n"; + bnd.call(i); + if(!bnd.isBound(i)) cout << " (not bound)\n"; + } + + cout << "\nCalling with parameter:\n"; + float f = 3.1415; + bnd.call(0, &f); + + return 0; +} diff --git a/input/tests/output/dispatch_map_test.out b/input/tests/output/dispatch_map_test.out new file mode 100644 index 0000000000..01aa9d9d93 --- /dev/null +++ b/input/tests/output/dispatch_map_test.out @@ -0,0 +1,18 @@ +Testing the dispatch map + +Printing everything: +1: + 9 +2: + -5 + 9 +3: + 10 + 12 + +Printing everything: +2: + -5 + 9 +3: + 10 diff --git a/input/tests/output/funcbind_test.out b/input/tests/output/funcbind_test.out new file mode 100644 index 0000000000..862c5c9729 --- /dev/null +++ b/input/tests/output/funcbind_test.out @@ -0,0 +1,15 @@ +This will test the function binding system +Calling 0: 'This is action 1' + F1 i=0 +Calling 1: '' + F2 i=1 +Calling 2: '' + (not bound) +Calling 3: 'This is action 4' + F2 i=3 +Calling 4: '' + (not bound) + +Calling with parameter: + F1 i=0 + Got a nice gift: 3.1415 diff --git a/input/tests/output/sdl_binder_test.out b/input/tests/output/sdl_binder_test.out new file mode 100644 index 0000000000..fd4eb90e3e --- /dev/null +++ b/input/tests/output/sdl_binder_test.out @@ -0,0 +1,4 @@ +Hold the Q key to quit: +You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly + +Bye bye! diff --git a/input/tests/output/sdl_driver_test.out b/input/tests/output/sdl_driver_test.out new file mode 100644 index 0000000000..fd4eb90e3e --- /dev/null +++ b/input/tests/output/sdl_driver_test.out @@ -0,0 +1,4 @@ +Hold the Q key to quit: +You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly + +Bye bye! diff --git a/input/tests/sdl_binder_test.cpp b/input/tests/sdl_binder_test.cpp new file mode 100644 index 0000000000..715e797bb8 --- /dev/null +++ b/input/tests/sdl_binder_test.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include "../dispatcher.hpp" +#include "../poller.hpp" + +using namespace std; +using namespace Mangle::Input; +using namespace Input; + +enum Actions + { + A_Quit, + A_Left, + A_Right, + + A_LAST + }; + +bool quit=false; + +void doExit(int,const void*) +{ + quit = true; +} + +void goLeft(int,const void*) +{ + cout << "Going left\n"; +} + +int main(int argc, char** argv) +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); + SDLDriver input; + Dispatcher disp(A_LAST); + Poller poll(input); + + input.setEvent(&disp); + + disp.funcs.bind(A_Quit, &doExit); + disp.funcs.bind(A_Left, &goLeft); + + disp.bind(A_Quit, SDLK_q); + disp.bind(A_Left, SDLK_a); + disp.bind(A_Left, SDLK_LEFT); + + poll.bind(A_Right, SDLK_d); + poll.bind(A_Right, SDLK_RIGHT); + + cout << "Hold the Q key to quit:\n"; + //input->setEvent(&mycb); + while(!quit) + { + input.capture(); + if(poll.isDown(A_Right)) + cout << "We're going right!\n"; + SDL_Delay(20); + + if(argc == 1) + { + cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; + break; + } + } + cout << "\nBye bye!\n"; + + SDL_Quit(); + return 0; +} diff --git a/input/tests/sdl_driver_test.cpp b/input/tests/sdl_driver_test.cpp new file mode 100644 index 0000000000..1771bcfe40 --- /dev/null +++ b/input/tests/sdl_driver_test.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +using namespace std; +using namespace Mangle::Input; + +int main(int argc, char** argv) +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); + SDLDriver input; + + cout << "Hold the Q key to quit:\n"; + //input->setEvent(&mycb); + while(!input.isDown(SDLK_q)) + { + input.capture(); + SDL_Delay(20); + + if(argc == 1) + { + cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n"; + break; + } + } + cout << "\nBye bye!\n"; + + SDL_Quit(); + return 0; +} diff --git a/input/tests/test.sh b/input/tests/test.sh new file mode 100755 index 0000000000..2d07708adc --- /dev/null +++ b/input/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/mangle b/mangle new file mode 160000 index 0000000000..7583fc3f1b --- /dev/null +++ b/mangle @@ -0,0 +1 @@ +Subproject commit 7583fc3f1bfa6d0fde56c925da959a5e9e50031a diff --git a/testall.sh b/testall.sh new file mode 100755 index 0000000000..097fdabd5b --- /dev/null +++ b/testall.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +function run() +{ + echo "TESTING $1" + cd "$1/tests/" + ./test.sh + cd ../../ +} + +run input From fce290104e22a21f11ce3a525e8d9c49bec9fbc8 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 9 Jul 2010 21:21:04 +0200 Subject: [PATCH 059/269] Added gui/ and ogre/ from OpenMW --- gui/layout.hpp | 118 +++++++++++++++++++++++++++++++++++++++++ gui/manager.cpp | 43 +++++++++++++++ gui/manager.hpp | 33 ++++++++++++ input/dispatch_map.hpp | 4 +- input/dispatcher.hpp | 4 +- input/func_binder.hpp | 4 +- input/poller.hpp | 4 +- ogre/.gitignore | 1 + ogre/renderer.cpp | 80 ++++++++++++++++++++++++++++ ogre/renderer.hpp | 77 +++++++++++++++++++++++++++ 10 files changed, 360 insertions(+), 8 deletions(-) create mode 100644 gui/layout.hpp create mode 100644 gui/manager.cpp create mode 100644 gui/manager.hpp create mode 100644 ogre/.gitignore create mode 100644 ogre/renderer.cpp create mode 100644 ogre/renderer.hpp diff --git a/gui/layout.hpp b/gui/layout.hpp new file mode 100644 index 0000000000..7a1b6b86c7 --- /dev/null +++ b/gui/layout.hpp @@ -0,0 +1,118 @@ +#ifndef OENGINE_MYGUI_LAYOUT_H +#define OENGINE_MYGUI_LAYOUT_H + +#include +#include + +namespace GUI +{ + /** The Layout class is an utility class used to load MyGUI layouts + from xml files, and to manipulate member widgets. + */ + class Layout + { + public: + Layout(const std::string & _layout, MyGUI::WidgetPtr _parent = nullptr) + : mMainWidget(nullptr) + { initialise(_layout, _parent); } + virtual ~Layout() { shutdown(); } + + template + void getWidget(T * & _widget, const std::string & _name, bool _throw = true) + { + _widget = nullptr; + for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); + iter!=mListWindowRoot.end(); ++iter) + { + MyGUI::WidgetPtr find = (*iter)->findWidget(mPrefix + _name); + if (nullptr != find) + { + T * cast = find->castType(false); + if (nullptr != cast) + _widget = cast; + else if (_throw) + { + MYGUI_EXCEPT("Error cast : dest type = '" << T::getClassTypeName() + << "' source name = '" << find->getName() + << "' source type = '" << find->getTypeName() << "' in layout '" << mLayoutName << "'"); + } + return; + } + } + MYGUI_ASSERT( ! _throw, "widget name '" << _name << "' in layout '" << mLayoutName << "' not found."); + } + + void initialise(const std::string & _layout, + MyGUI::WidgetPtr _parent = nullptr) + { + const std::string MAIN_WINDOW = "_Main"; + mLayoutName = _layout; + + if (mLayoutName.empty()) + mMainWidget = _parent; + else + { + mPrefix = MyGUI::utility::toString(this, "_"); + mListWindowRoot = MyGUI::LayoutManager::getInstance().loadLayout(mLayoutName, mPrefix, _parent); + + const std::string main_name = mPrefix + MAIN_WINDOW; + for (MyGUI::VectorWidgetPtr::iterator iter=mListWindowRoot.begin(); iter!=mListWindowRoot.end(); ++iter) + { + if ((*iter)->getName() == main_name) + { + mMainWidget = (*iter); + break; + } + } + MYGUI_ASSERT(mMainWidget, "root widget name '" << MAIN_WINDOW << "' in layout '" << mLayoutName << "' not found."); + } + } + + void shutdown() + { + MyGUI::LayoutManager::getInstance().unloadLayout(mListWindowRoot); + mListWindowRoot.clear(); + } + + void setCoord(int x, int y, int w, int h) + { + mMainWidget->setCoord(x,y,w,h); + } + + void setVisible(bool b) + { + mMainWidget->setVisible(b); + } + + void setText(const std::string& name, const std::string& caption) + { + MyGUI::WidgetPtr pt; + getWidget(pt, name); + pt->setCaption(caption); + } + + void setTextColor(const std::string& name, float r, float g, float b) + { + MyGUI::WidgetPtr pt; + getWidget(pt, name); + MyGUI::StaticText *st = dynamic_cast(pt); + if(st != NULL) + st->setTextColour(MyGUI::Colour(b,g,r)); + } + + void setImage(const std::string& name, const std::string& imgName) + { + MyGUI::StaticImagePtr pt; + getWidget(pt, name); + pt->setImageTexture(imgName); + } + + protected: + + MyGUI::WidgetPtr mMainWidget; + std::string mPrefix; + std::string mLayoutName; + MyGUI::VectorWidgetPtr mListWindowRoot; + }; +} +#endif diff --git a/gui/manager.cpp b/gui/manager.cpp new file mode 100644 index 0000000000..e5e01e7d71 --- /dev/null +++ b/gui/manager.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "manager.hpp" + +using namespace GUI; + +void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging) +{ + assert(wnd); + assert(mgr); + + using namespace MyGUI; + + // Enable/disable MyGUI logging to stdout. (Logging to MyGUI.log is + // still enabled.) In order to do this we have to initialize the log + // manager before the main gui system itself, otherwise the main + // object will get the chance to spit out a few messages before we + // can able to disable it. + LogManager::initialise(); + LogManager::setSTDOutputEnabled(logging); + + // Set up OGRE platform. We might make this more generic later. + mPlatform = new OgrePlatform(); + mPlatform->initialise(wnd, mgr); + + // Create GUI + mGui = new Gui(); + mGui->initialise(); +} + +void MyGUIManager::shutdown() +{ + if(mGui) delete mGui; + if(mPlatform) + { + mPlatform->shutdown(); + delete mPlatform; + } + mGui = NULL; + mPlatform = NULL; +} diff --git a/gui/manager.hpp b/gui/manager.hpp new file mode 100644 index 0000000000..3669c0cb20 --- /dev/null +++ b/gui/manager.hpp @@ -0,0 +1,33 @@ +#ifndef OENGINE_MYGUI_MANAGER_H +#define OENGINE_MYGUI_MANAGER_H + +namespace MyGUI +{ + class OgrePlatform; + class Gui; +} + +namespace Ogre +{ + class RenderWindow; + class SceneManager; +} + +namespace GUI +{ + class MyGUIManager + { + MyGUI::OgrePlatform *mPlatform; + MyGUI::Gui *mGui; + + public: + MyGUIManager() : mPlatform(NULL), mGui(NULL) {} + MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false) + { setup(wnd,mgr,logging); } + ~MyGUIManager() { shutdown(); } + + void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false); + void shutdown(); + }; +} +#endif diff --git a/input/dispatch_map.hpp b/input/dispatch_map.hpp index 356660e755..a8e03e18dc 100644 --- a/input/dispatch_map.hpp +++ b/input/dispatch_map.hpp @@ -1,5 +1,5 @@ -#ifndef _INPUT_DISPATCHMAP_H -#define _INPUT_DISPATCHMAP_H +#ifndef OENGINE_INPUT_DISPATCHMAP_H +#define OENGINE_INPUT_DISPATCHMAP_H #include #include diff --git a/input/dispatcher.hpp b/input/dispatcher.hpp index 107211a49e..0a9cfd7956 100644 --- a/input/dispatcher.hpp +++ b/input/dispatcher.hpp @@ -1,5 +1,5 @@ -#ifndef _INPUT_DISPATCHER_H -#define _INPUT_DISPATCHER_H +#ifndef OENGINE_INPUT_DISPATCHER_H +#define OENGINE_INPUT_DISPATCHER_H #include "dispatch_map.hpp" #include "func_binder.hpp" diff --git a/input/func_binder.hpp b/input/func_binder.hpp index 20119b07be..234971bae3 100644 --- a/input/func_binder.hpp +++ b/input/func_binder.hpp @@ -1,5 +1,5 @@ -#ifndef _INPUT_FUNCBINDER_H -#define _INPUT_FUNCBINDER_H +#ifndef OENGINE_INPUT_FUNCBINDER_H +#define OENGINE_INPUT_FUNCBINDER_H #include #include diff --git a/input/poller.hpp b/input/poller.hpp index 794f79b741..9c93fe1bf7 100644 --- a/input/poller.hpp +++ b/input/poller.hpp @@ -1,5 +1,5 @@ -#ifndef _INPUT_POLLER_H -#define _INPUT_POLLER_H +#ifndef OENGINE_INPUT_POLLER_H +#define OENGINE_INPUT_POLLER_H #include "dispatch_map.hpp" #include diff --git a/ogre/.gitignore b/ogre/.gitignore new file mode 100644 index 0000000000..3367afdbbf --- /dev/null +++ b/ogre/.gitignore @@ -0,0 +1 @@ +old diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp new file mode 100644 index 0000000000..982f0f0500 --- /dev/null +++ b/ogre/renderer.cpp @@ -0,0 +1,80 @@ +#include "renderer.hpp" + +#include "OgreRoot.h" +#include "OgreRenderWindow.h" +#include "OgreLogManager.h" +#include "OgreLog.h" + +#include + +using namespace Ogre; +using namespace Render; + +void OgreRenderer::cleanup() +{ + if(mRoot) + delete mRoot; + mRoot = NULL; +} + +void OgreRenderer::screenshot(const std::string &file) +{ + mWindow->writeContentsToFile(file); +} + +bool OgreRenderer::configure(bool showConfig, + const std::string &pluginCfg, + bool _logging) +{ + // Set up logging first + new LogManager; + Log *log = LogManager::getSingleton().createLog("Ogre.log"); + logging = _logging; + + if(logging) + // Full log detail + log->setLogDetail(LL_BOREME); + else + // Disable logging + log->setDebugOutputEnabled(false); + + mRoot = new Root(pluginCfg, "ogre.cfg", ""); + + // Show the configuration dialog and initialise the system, if the + // showConfig parameter is specified. The settings are stored in + // ogre.cfg. If showConfig is false, the settings are assumed to + // already exist in ogre.cfg. + int result; + if(showConfig) + result = mRoot->showConfigDialog(); + else + result = mRoot->restoreConfig(); + + return !result; +} + +void OgreRenderer::createWindow(const std::string &title) +{ + assert(mRoot); + // Initialize OGRE window + mWindow = mRoot->initialise(true, title, ""); +} + +void OgreRenderer::createScene(const std::string camName, float fov, float nearClip) +{ + assert(mRoot); + assert(mWindow); + // Get the SceneManager, in this case a generic one + mScene = mRoot->createSceneManager(ST_GENERIC); + + // Create the camera + mCamera = mScene->createCamera(camName); + mCamera->setNearClipDistance(nearClip); + mCamera->setFOVy(Degree(fov)); + + // Create one viewport, entire window + mView = mWindow->addViewport(mCamera); + + // Alter the camera aspect ratio to match the viewport + mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight())); +} diff --git a/ogre/renderer.hpp b/ogre/renderer.hpp new file mode 100644 index 0000000000..12dada2d58 --- /dev/null +++ b/ogre/renderer.hpp @@ -0,0 +1,77 @@ +#ifndef OENGINE_OGRE_RENDERER_H +#define OENGINE_OGRE_RENDERER_H + +/* + Ogre renderer class + */ + +#include +#include + +namespace Ogre +{ + class Root; + class RenderWindow; + class SceneManager; + class Camera; + class Viewport; +} + +namespace Render +{ + class OgreRenderer + { + Ogre::Root *mRoot; + Ogre::RenderWindow *mWindow; + Ogre::SceneManager *mScene; + Ogre::Camera *mCamera; + Ogre::Viewport *mView; + bool logging; + + public: + OgreRenderer() + : mRoot(NULL), mWindow(NULL), mScene(NULL) {} + ~OgreRenderer() { cleanup(); } + + /** Configure the renderer. This will load configuration files and + set up the Root and logging classes. */ + bool configure(bool showConfig, // Show config dialog box? + const std::string &pluginCfg, // plugin.cfg file + bool _logging); // Enable or disable logging + + /// Create a window with the given title + void createWindow(const std::string &title); + + /// Set up the scene manager, camera and viewport + void createScene(const std::string camName="Camera",// Camera name + float fov=55, // Field of view angle + float nearClip=5 // Near clip distance + ); + + /// Kill the renderer. + void cleanup(); + + /// Start the main rendering loop + void start() { mRoot->startRendering(); } + + /// Write a screenshot to file + void screenshot(const std::string &file); + + /// Get the Root + Ogre::Root *getRoot() { return mRoot; } + + /// Get the rendering window + Ogre::RenderWindow *getWindow() { return mWindow; } + + /// Get the scene manager + Ogre::SceneManager *getScene() { return mScene; } + + /// Camera + Ogre::Camera *getCamera() { return mCamera; } + + /// Viewport + Ogre::Viewport *getViewport() { return mView; } + }; +} + +#endif From 18dc065715c12fd8793f8793c7bf834619527399 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 10 Jul 2010 12:32:55 +0200 Subject: [PATCH 060/269] Reworked Input::Event, EventList filter --- input/driver.hpp | 7 ++-- input/event.hpp | 25 ++++++++------ input/filters/eventlist.hpp | 45 ++++++++++++++++++++++++++ input/servers/ois_driver.cpp | 4 +-- input/tests/Makefile | 5 ++- input/tests/common.cpp | 5 +-- input/tests/evtlist_test.cpp | 45 ++++++++++++++++++++++++++ input/tests/output/evtlist_test.out | 12 +++++++ input/tests/output/ois_driver_test.out | 2 +- input/tests/output/sdl_driver_test.out | 2 +- rend2d/driver.hpp | 6 ++-- 11 files changed, 136 insertions(+), 22 deletions(-) create mode 100644 input/filters/eventlist.hpp create mode 100644 input/tests/evtlist_test.cpp create mode 100644 input/tests/output/evtlist_test.out diff --git a/input/driver.hpp b/input/driver.hpp index 1c1e950478..f4ba159c52 100644 --- a/input/driver.hpp +++ b/input/driver.hpp @@ -1,7 +1,6 @@ #ifndef MANGLE_INPUT_DRIVER_H #define MANGLE_INPUT_DRIVER_H -#include "../tools/shared_ptr.hpp" #include "event.hpp" namespace Mangle @@ -23,7 +22,7 @@ namespace Mangle */ struct Driver { - Driver() : event(NULL) {} + Driver() {} virtual ~Driver() {} /** Captures input and produces the relevant events from it. An @@ -46,7 +45,7 @@ namespace Mangle and *p parameters will be specific to each driver and to each input system. */ - void setEvent(Event *evt) + void setEvent(EventPtr evt) { event = evt; } /** Instigate an event. Is used internally for all events, but @@ -61,7 +60,7 @@ namespace Mangle private: /// Holds the event callback set byt setEvent() - Event *event; + EventPtr event; }; typedef boost::shared_ptr DriverPtr; diff --git a/input/event.hpp b/input/event.hpp index 5948653bd9..f7ee7e58b7 100644 --- a/input/event.hpp +++ b/input/event.hpp @@ -1,6 +1,8 @@ #ifndef MANGLE_INPUT_EVENT_H #define MANGLE_INPUT_EVENT_H +#include "../tools/shared_ptr.hpp" + namespace Mangle { namespace Input @@ -13,24 +15,29 @@ namespace Mangle /// Event types enum Type { - EV_Unknown = -1, // Unknown event type - EV_KeyDown = 1, // Key, mouse or other button was pressed - EV_KeyUp = 2, // Key, mouse or other button was released - EV_MouseMove = 3, // Mouse movement (all axis movement?) - EV_Other = 4 // Other event + EV_Unknown = 1, // Unknown event type + EV_KeyDown = 2, // Keyboard button was pressed + EV_KeyUp = 4, // Keyboard button was released + EV_MouseMove = 8, // Mouse movement + EV_MouseDown = 16, // Mouse button pressed + EV_MouseUp = 32, // Mouse button released + + EV_ALL = 63 // All events }; /** Called upon all events. The first parameter give the event type, the second gives additional data (usually the local - keysym as defined by the driver), and the pointer points to - the full custom event structure provided by the driver (the - type may vary depending on the EventType, this is defined in - the Driver documentation.) + keysym or button index as defined by the driver), and the + pointer points to the full custom event structure provided by + the driver (the type may vary depending on the EventType, + this is defined in the Driver documentation.) */ virtual void event(Type type, int index, const void *p) = 0; virtual ~Event() {} }; + + typedef boost::shared_ptr EventPtr; } } #endif diff --git a/input/filters/eventlist.hpp b/input/filters/eventlist.hpp new file mode 100644 index 0000000000..d7703fbc4f --- /dev/null +++ b/input/filters/eventlist.hpp @@ -0,0 +1,45 @@ +#ifndef MANGLE_INPUT_EVENTLIST_H +#define MANGLE_INPUT_EVENTLIST_H + +#include "../event.hpp" +#include + +namespace Mangle +{ + namespace Input + { + /** And Event handler that distributes each event to a list of + other handlers. Supports filtering events by their Type + parameter. + */ + struct EventList : Event + { + struct Filter + { + EventPtr evt; + int flags; + }; + std::vector list; + + void add(EventPtr e, int flags = EV_ALL) + { + Filter f; + f.evt = e; + f.flags = flags; + list.push_back(f); + } + + virtual void event(Type type, int index, const void *p) + { + std::vector::iterator it; + + for(it=list.begin(); it!=list.end(); it++) + { + if(type & it->flags) + it->evt->event(type,index,p); + } + } + }; + } +} +#endif diff --git a/input/servers/ois_driver.cpp b/input/servers/ois_driver.cpp index eabe9739d0..2071b91ea6 100644 --- a/input/servers/ois_driver.cpp +++ b/input/servers/ois_driver.cpp @@ -35,14 +35,14 @@ struct Mangle::Input::OISListener : OIS::KeyListener, OIS::MouseListener { // Mouse button events are handled as key events // TODO: Translate mouse buttons into pseudo-keysyms - drv.makeEvent(Event::EV_KeyDown, -1, &arg); + drv.makeEvent(Event::EV_MouseDown, id, &arg); return true; } bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) { // TODO: ditto - drv.makeEvent(Event::EV_KeyUp, -1, &arg); + drv.makeEvent(Event::EV_MouseUp, id, &arg); return true; } diff --git a/input/tests/Makefile b/input/tests/Makefile index b7d7b5b4ed..8b7663dd57 100644 --- a/input/tests/Makefile +++ b/input/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -all: sdl_driver_test ois_driver_test +all: sdl_driver_test ois_driver_test evtlist_test sdl_driver_test: sdl_driver_test.cpp $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL @@ -8,5 +8,8 @@ sdl_driver_test: sdl_driver_test.cpp ois_driver_test: ois_driver_test.cpp $(GCC) $< ../servers/ois_driver.cpp -o $@ -I/usr/local/include/OGRE/ -lOgreMain -lOIS -lboost_filesystem +evtlist_test: evtlist_test.cpp ../filters/eventlist.hpp ../event.hpp + $(GCC) $< -o $@ + clean: rm *_test diff --git a/input/tests/common.cpp b/input/tests/common.cpp index 47a0c53de0..0c7c76466b 100644 --- a/input/tests/common.cpp +++ b/input/tests/common.cpp @@ -12,11 +12,12 @@ struct MyCB : Event { cout << "got event: type=" << type << " index=" << i << endl; } -} mycb; +}; + void mainLoop(int argc, int quitKey) { cout << "Hold the Q key to quit:\n"; - input->setEvent(&mycb); + input->setEvent(EventPtr(new MyCB)); while(!input->isDown(quitKey)) { input->capture(); diff --git a/input/tests/evtlist_test.cpp b/input/tests/evtlist_test.cpp new file mode 100644 index 0000000000..fbd980cbd9 --- /dev/null +++ b/input/tests/evtlist_test.cpp @@ -0,0 +1,45 @@ +#include +#include "../filters/eventlist.hpp" + +using namespace std; +using namespace Mangle::Input; + +struct MyEvent : Event +{ + int ii; + MyEvent(int i) : ii(i) {} + + void event(Event::Type type, int i, const void *p) + { + cout << " #" << ii << " got event: type=" << type << " index=" << i << endl; + } +}; + +EventList lst; + +int iii=1; +void make(int flags) +{ + lst.add(EventPtr(new MyEvent(iii++)), flags); +} + +void send(Event::Type type) +{ + cout << "Sending type " << type << endl; + lst.event(type,0,NULL); +} + +int main() +{ + make(Event::EV_ALL); + make(Event::EV_KeyDown); + make(Event::EV_KeyUp | Event::EV_MouseDown); + + send(Event::EV_Unknown); + send(Event::EV_KeyDown); + send(Event::EV_KeyUp); + send(Event::EV_MouseDown); + + cout << "Enough of that\n"; + return 0; +} diff --git a/input/tests/output/evtlist_test.out b/input/tests/output/evtlist_test.out new file mode 100644 index 0000000000..180dcc58a8 --- /dev/null +++ b/input/tests/output/evtlist_test.out @@ -0,0 +1,12 @@ +Sending type 1 + #1 got event: type=1 index=0 +Sending type 2 + #1 got event: type=2 index=0 + #2 got event: type=2 index=0 +Sending type 4 + #1 got event: type=4 index=0 + #3 got event: type=4 index=0 +Sending type 16 + #1 got event: type=16 index=0 + #3 got event: type=16 index=0 +Enough of that diff --git a/input/tests/output/ois_driver_test.out b/input/tests/output/ois_driver_test.out index eb9c29bf47..7d273fd46d 100644 --- a/input/tests/output/ois_driver_test.out +++ b/input/tests/output/ois_driver_test.out @@ -1,5 +1,5 @@ Hold the Q key to quit: -got event: type=3 index=-1 +got event: type=8 index=-1 You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly Bye bye! diff --git a/input/tests/output/sdl_driver_test.out b/input/tests/output/sdl_driver_test.out index 54e941fb66..2df2e4014e 100644 --- a/input/tests/output/sdl_driver_test.out +++ b/input/tests/output/sdl_driver_test.out @@ -1,5 +1,5 @@ Hold the Q key to quit: -got event: type=-1 index=-1 +got event: type=1 index=-1 You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly Bye bye! diff --git a/rend2d/driver.hpp b/rend2d/driver.hpp index 7366e04625..08a15b0aeb 100644 --- a/rend2d/driver.hpp +++ b/rend2d/driver.hpp @@ -34,10 +34,12 @@ namespace Mangle /// Set the window title void setWindowTitle(const std::string &title) { setWindowTitle(title,title); } - /// Load sprite from an image file + /// Load sprite from an image file. Thows an exception on + /// failure. virtual Sprite* loadImage(const std::string &file) = 0; - /// Load a sprite from an image file stored in memory. + /// Load a sprite from an image file stored in memory. Throws + /// exception on failure. virtual Sprite* loadImage(const void* data, size_t size) = 0; /** @brief Set gamma value for all colors. From e55ef227fe3bd9ce8f05b1f4a7cf8a6f62643acb Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 10 Jul 2010 12:34:48 +0200 Subject: [PATCH 061/269] Updated to latest Mangle --- mangle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mangle b/mangle index 7583fc3f1b..18dc065715 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit 7583fc3f1bfa6d0fde56c925da959a5e9e50031a +Subproject commit 18dc065715c12fd8793f8793c7bf834619527399 From b9b306cd4c14147c78d9c59d6e1edd97820ef036 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 10 Jul 2010 13:25:04 +0200 Subject: [PATCH 062/269] Added event listeners (mouselook and gui injector) and exit listener for Ogre --- gui/events.cpp | 23 ++++++++++++++++++ gui/events.hpp | 26 +++++++++++++++++++++ ogre/exitlistener.hpp | 33 ++++++++++++++++++++++++++ ogre/mouselook.cpp | 36 +++++++++++++++++++++++++++++ ogre/mouselook.hpp | 54 +++++++++++++++++++++++++++++++++++++++++++ ogre/renderer.cpp | 5 ++++ ogre/renderer.hpp | 3 +-- 7 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 gui/events.cpp create mode 100644 gui/events.hpp create mode 100644 ogre/exitlistener.hpp create mode 100644 ogre/mouselook.cpp create mode 100644 ogre/mouselook.hpp diff --git a/gui/events.cpp b/gui/events.cpp new file mode 100644 index 0000000000..39b5e9f1cc --- /dev/null +++ b/gui/events.cpp @@ -0,0 +1,23 @@ +#include +#include + +using namespace MyGUI; +using namespace OIS; + +void EventInjector::event(Type type, int index, const void *p) +{ + if(enabled) return; + + KeyEvent *key = (KeyEvent*)p; + MouseEvent *mouse = (MouseEvent*)p; + MouseButtonID id = (MouseButtonID)index; + + switch(type) + { + case EV_KeyDown: gui->injectKeyPress(key); break; + case EV_KeyUp: gui->injectKeyRelease(key); break; + case EV_MouseDown: gui->injectMousePress(mouse, id); break; + case EV_MouseUp: gui->injectMouseRelease(mouse, id); break; + case EV_MouseMove: gui->injectMouseMove(mouse); break; + } +} diff --git a/gui/events.hpp b/gui/events.hpp new file mode 100644 index 0000000000..b994a422da --- /dev/null +++ b/gui/events.hpp @@ -0,0 +1,26 @@ +#ifndef OENGINE_MYGUI_EVENTS_H +#define OENGINE_MYGUI_EVENTS_H + +#include + +namespace MyGUI +{ + class Gui; +} + +namespace GUI +{ + /** Event handler that injects OIS events into MyGUI + */ + class EventInjector : Mangle::Input::Event + { + MyGUI::Gui *gui; + + public: + bool enabled; + + EventInjector(MyGUI::Gui *g) : gui(g), enabled(true) {} + void event(Type type, int index, const void *p); + }; +} +#endif diff --git a/ogre/exitlistener.hpp b/ogre/exitlistener.hpp new file mode 100644 index 0000000000..e1af9604f9 --- /dev/null +++ b/ogre/exitlistener.hpp @@ -0,0 +1,33 @@ +#ifndef OENGINE_OGRE_EXITLISTEN_H +#define OENGINE_OGRE_EXITLISTEN_H + +/* + This FrameListener simply exits the rendering loop when the window + is closed. You can also tell it to exit manually by setting the exit + member to true; + */ + +#include +#include + +namespace Render +{ + struct ExitListener : Ogre::FrameListener + { + Ogre::RenderWindow *window; + bool exit; + + ExitListener(Ogre::RenderWindow *wnd) + : window(wnd), exit(false) {} + + bool frameStarted(const FrameEvent &evt) + { + if(window->isClosed()) + exit = true; + + return !exit; + } + }; +} + +#endif diff --git a/ogre/mouselook.cpp b/ogre/mouselook.cpp new file mode 100644 index 0000000000..d4d15cee96 --- /dev/null +++ b/ogre/mouselook.cpp @@ -0,0 +1,36 @@ +#include "mouselook.hpp" + +#include +#include + +using namespace OIS; +using namespace Ogre; + +void MouseLookEvent::event(Type type, int index, const void *p) +{ + if(type != EV_MouseMove || camera == NULL) return; + + MouseEvent *arg = (MouseEvent*)(p); + + float x = arg->state.X.rel * sensX; + float y = arg->state.Y.rel * sensY; + + camera->yaw(Degree(-x)); + + if(flipProt) + { + // The camera before pitching + Quaternion nopitch = camera->getOrientation(); + + camera->pitch(Degree(-y)); + + // Apply some failsafe measures against the camera flipping + // upside down. Is the camera close to pointing straight up or + // down? + if(camera->getUp()[1] <= 0.1) + // If so, undo the last pitch + camera->setOrientation(nopitch); + } + else + camera->pitch(Degree(-y)); +} diff --git a/ogre/mouselook.hpp b/ogre/mouselook.hpp new file mode 100644 index 0000000000..0b11c0b794 --- /dev/null +++ b/ogre/mouselook.hpp @@ -0,0 +1,54 @@ +#ifndef OENGINE_OGRE_MOUSELOOK_H +#define OENGINE_OGRE_MOUSELOOK_H + +/* + A mouse-look class for Ogre. Accepts input events from Mangle::Input + and translates them. + + You can adjust the mouse sensibility and switch to a different + camera. The mouselook class also has an optional wrap protection + that keeps the camera from flipping upside down. + + You can disable the mouse looker at any time by calling + setCamera(NULL), and reenable it by setting the camera back. + + NOTE: The current implementation will ONLY work for native OIS + events. + */ + +#include + +namespace Ogre +{ + class Camera; +} + +namespace Render +{ + class MouseLookEvent : public Mangle::Input::Event + { + Ogre::Camera* camera; + float sensX, sensY; // Mouse sensibility + bool flipProt; // Flip protection + + public: + MouseLookEvent(Ogre::Camera *cam=NULL, + float sX=0.2, float sY=0.2, + bool prot=true) + : camera(cam) + , sensX(sX) + , sensY(sy) + , flipProt(prot) + {} + + void setCamera(Ogre::Camera *cam) + { camera = cam; } + void setSens(float sX, float sY) + { sensX = sX; sensY = sY; } + void setProt(bool p) { flipProt = p; } + + void event(Type type, int index, const void *p); + }; +} + +#endif diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index 982f0f0500..86382127c9 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -17,6 +17,11 @@ void OgreRenderer::cleanup() mRoot = NULL; } +void OgreRenderer::start() +{ + mRoot->startRendering(); +} + void OgreRenderer::screenshot(const std::string &file) { mWindow->writeContentsToFile(file); diff --git a/ogre/renderer.hpp b/ogre/renderer.hpp index 12dada2d58..ac44f5ee11 100644 --- a/ogre/renderer.hpp +++ b/ogre/renderer.hpp @@ -5,7 +5,6 @@ Ogre renderer class */ -#include #include namespace Ogre @@ -52,7 +51,7 @@ namespace Render void cleanup(); /// Start the main rendering loop - void start() { mRoot->startRendering(); } + void start(); /// Write a screenshot to file void screenshot(const std::string &file); From 9f21081c13f70bc41e20b50c19ed2dac9b458833 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 10 Jul 2010 13:29:44 +0200 Subject: [PATCH 063/269] Added ogre input capturer --- input/clients/ogre_input_capture.hpp | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 input/clients/ogre_input_capture.hpp diff --git a/input/clients/ogre_input_capture.hpp b/input/clients/ogre_input_capture.hpp new file mode 100644 index 0000000000..6a7beae4d2 --- /dev/null +++ b/input/clients/ogre_input_capture.hpp @@ -0,0 +1,29 @@ +#ifndef MANGLE_INPUT_OGREINPUTFRAME_H +#define MANGLE_INPUT_OGREINPUTFRAME_H + +/* + This Ogre FrameListener calls capture() on an input driver every frame. + */ + +#include +#include "../driver.hpp" + +namespace Mangle { +namespace Input { + + struct OgreInputCapture : Ogre::FrameListener + { + Mangle::Input::Driver &driver; + + ExitListener(Mangle::Input::Driver &drv) + : driver(drv) {} + + bool frameStarted(const FrameEvent &evt) + { + driver.capture(); + return true; + } + }; +} + +#endif From 5333b8e230e285e9183b0639188283bdbc15af6c Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 10 Jul 2010 13:41:43 +0200 Subject: [PATCH 064/269] Fixed namespaces, added OEngine:: to everything --- gui/events.cpp | 3 +++ gui/events.hpp | 3 ++- gui/layout.hpp | 3 ++- gui/manager.cpp | 2 +- gui/manager.hpp | 3 ++- input/dispatch_map.hpp | 4 ++-- input/dispatcher.hpp | 4 ++-- input/func_binder.hpp | 4 ++-- input/poller.hpp | 4 ++-- input/tests/dispatch_map_test.cpp | 2 +- input/tests/funcbind_test.cpp | 2 +- input/tests/sdl_binder_test.cpp | 16 ++++++++-------- ogre/exitlistener.hpp | 4 ++-- ogre/mouselook.cpp | 1 + ogre/mouselook.hpp | 4 ++-- ogre/renderer.cpp | 2 +- ogre/renderer.hpp | 4 ++-- 17 files changed, 36 insertions(+), 29 deletions(-) diff --git a/gui/events.cpp b/gui/events.cpp index 39b5e9f1cc..146d3ea802 100644 --- a/gui/events.cpp +++ b/gui/events.cpp @@ -1,8 +1,11 @@ #include #include +#include "events.hpp" + using namespace MyGUI; using namespace OIS; +using namespace OEngine::GUI; void EventInjector::event(Type type, int index, const void *p) { diff --git a/gui/events.hpp b/gui/events.hpp index b994a422da..ec483536af 100644 --- a/gui/events.hpp +++ b/gui/events.hpp @@ -8,6 +8,7 @@ namespace MyGUI class Gui; } +namespace OEngine { namespace GUI { /** Event handler that injects OIS events into MyGUI @@ -22,5 +23,5 @@ namespace GUI EventInjector(MyGUI::Gui *g) : gui(g), enabled(true) {} void event(Type type, int index, const void *p); }; -} +}} #endif diff --git a/gui/layout.hpp b/gui/layout.hpp index 7a1b6b86c7..f02ddbdff1 100644 --- a/gui/layout.hpp +++ b/gui/layout.hpp @@ -4,6 +4,7 @@ #include #include +namespace OEngine { namespace GUI { /** The Layout class is an utility class used to load MyGUI layouts @@ -114,5 +115,5 @@ namespace GUI std::string mLayoutName; MyGUI::VectorWidgetPtr mListWindowRoot; }; -} +}} #endif diff --git a/gui/manager.cpp b/gui/manager.cpp index e5e01e7d71..564a33cfed 100644 --- a/gui/manager.cpp +++ b/gui/manager.cpp @@ -4,7 +4,7 @@ #include "manager.hpp" -using namespace GUI; +using namespace OEngine::GUI; void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging) { diff --git a/gui/manager.hpp b/gui/manager.hpp index 3669c0cb20..5f67e9f91a 100644 --- a/gui/manager.hpp +++ b/gui/manager.hpp @@ -13,6 +13,7 @@ namespace Ogre class SceneManager; } +namespace OEngine { namespace GUI { class MyGUIManager @@ -29,5 +30,5 @@ namespace GUI void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false); void shutdown(); }; -} +}} #endif diff --git a/input/dispatch_map.hpp b/input/dispatch_map.hpp index a8e03e18dc..f0d4cabe92 100644 --- a/input/dispatch_map.hpp +++ b/input/dispatch_map.hpp @@ -5,6 +5,7 @@ #include #include +namespace OEngine { namespace Input { /** @@ -70,6 +71,5 @@ struct DispatchMap return out; } }; - -} +}} #endif diff --git a/input/dispatcher.hpp b/input/dispatcher.hpp index 0a9cfd7956..bba13893d3 100644 --- a/input/dispatcher.hpp +++ b/input/dispatcher.hpp @@ -5,6 +5,7 @@ #include "func_binder.hpp" #include +namespace OEngine { namespace Input { struct Dispatcher : Mangle::Input::Event @@ -44,6 +45,5 @@ struct Dispatcher : Mangle::Input::Event funcs.call(*it, p); } }; - -} +}} #endif diff --git a/input/func_binder.hpp b/input/func_binder.hpp index 234971bae3..d2bfd53e5b 100644 --- a/input/func_binder.hpp +++ b/input/func_binder.hpp @@ -6,6 +6,7 @@ #include #include +namespace OEngine { namespace Input { /** @@ -99,6 +100,5 @@ public: return bindings[index].name; } }; - -} +}} #endif diff --git a/input/poller.hpp b/input/poller.hpp index 9c93fe1bf7..c544aed529 100644 --- a/input/poller.hpp +++ b/input/poller.hpp @@ -4,6 +4,7 @@ #include "dispatch_map.hpp" #include +namespace OEngine { namespace Input { /** The poller is used to check (poll) for keys rather than waiting @@ -41,6 +42,5 @@ struct Poller return false; } }; - -} +}} #endif diff --git a/input/tests/dispatch_map_test.cpp b/input/tests/dispatch_map_test.cpp index 5cc7e1ef29..5f262494e5 100644 --- a/input/tests/dispatch_map_test.cpp +++ b/input/tests/dispatch_map_test.cpp @@ -3,7 +3,7 @@ using namespace std; #include "../dispatch_map.hpp" -using namespace Input; +using namespace OEngine::Input; typedef DispatchMap::OutList OutList; typedef OutList::const_iterator Cit; diff --git a/input/tests/funcbind_test.cpp b/input/tests/funcbind_test.cpp index 4716f8b81f..bb4ad34e18 100644 --- a/input/tests/funcbind_test.cpp +++ b/input/tests/funcbind_test.cpp @@ -17,7 +17,7 @@ void f2(int i, const void *p) cout << " F2 i=" << i << endl; } -using namespace Input; +using namespace OEngine::Input; int main() { diff --git a/input/tests/sdl_binder_test.cpp b/input/tests/sdl_binder_test.cpp index 715e797bb8..7de5f5d4fd 100644 --- a/input/tests/sdl_binder_test.cpp +++ b/input/tests/sdl_binder_test.cpp @@ -6,7 +6,7 @@ using namespace std; using namespace Mangle::Input; -using namespace Input; +using namespace OEngine::Input; enum Actions { @@ -34,17 +34,17 @@ int main(int argc, char** argv) SDL_Init(SDL_INIT_VIDEO); SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE); SDLDriver input; - Dispatcher disp(A_LAST); + Dispatcher *disp = new Dispatcher(A_LAST); Poller poll(input); - input.setEvent(&disp); + input.setEvent(EventPtr(disp)); - disp.funcs.bind(A_Quit, &doExit); - disp.funcs.bind(A_Left, &goLeft); + disp->funcs.bind(A_Quit, &doExit); + disp->funcs.bind(A_Left, &goLeft); - disp.bind(A_Quit, SDLK_q); - disp.bind(A_Left, SDLK_a); - disp.bind(A_Left, SDLK_LEFT); + disp->bind(A_Quit, SDLK_q); + disp->bind(A_Left, SDLK_a); + disp->bind(A_Left, SDLK_LEFT); poll.bind(A_Right, SDLK_d); poll.bind(A_Right, SDLK_RIGHT); diff --git a/ogre/exitlistener.hpp b/ogre/exitlistener.hpp index e1af9604f9..ed07ee55c8 100644 --- a/ogre/exitlistener.hpp +++ b/ogre/exitlistener.hpp @@ -10,6 +10,7 @@ #include #include +namespace OEngine { namespace Render { struct ExitListener : Ogre::FrameListener @@ -28,6 +29,5 @@ namespace Render return !exit; } }; -} - +}} #endif diff --git a/ogre/mouselook.cpp b/ogre/mouselook.cpp index d4d15cee96..84e6e03975 100644 --- a/ogre/mouselook.cpp +++ b/ogre/mouselook.cpp @@ -5,6 +5,7 @@ using namespace OIS; using namespace Ogre; +using namespace OEngine::Render; void MouseLookEvent::event(Type type, int index, const void *p) { diff --git a/ogre/mouselook.hpp b/ogre/mouselook.hpp index 0b11c0b794..b5c7849452 100644 --- a/ogre/mouselook.hpp +++ b/ogre/mouselook.hpp @@ -23,6 +23,7 @@ namespace Ogre class Camera; } +namespace OEngine { namespace Render { class MouseLookEvent : public Mangle::Input::Event @@ -49,6 +50,5 @@ namespace Render void event(Type type, int index, const void *p); }; -} - +}} #endif diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index 86382127c9..c60a29c77b 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -8,7 +8,7 @@ #include using namespace Ogre; -using namespace Render; +using namespace OEngine::Render; void OgreRenderer::cleanup() { diff --git a/ogre/renderer.hpp b/ogre/renderer.hpp index ac44f5ee11..f706b16f7e 100644 --- a/ogre/renderer.hpp +++ b/ogre/renderer.hpp @@ -16,6 +16,7 @@ namespace Ogre class Viewport; } +namespace OEngine { namespace Render { class OgreRenderer @@ -71,6 +72,5 @@ namespace Render /// Viewport Ogre::Viewport *getViewport() { return mView; } }; -} - +}} #endif From 0e30756ba17824df01ce53e7b5696a02972c6825 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 16 Jul 2010 14:25:19 +0200 Subject: [PATCH 065/269] Fixed for OpenMW compilation --- input/dispatcher.hpp | 9 +++++---- ogre/exitlistener.hpp | 6 +++++- ogre/mouselook.hpp | 4 +++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/input/dispatcher.hpp b/input/dispatcher.hpp index bba13893d3..930195856a 100644 --- a/input/dispatcher.hpp +++ b/input/dispatcher.hpp @@ -34,10 +34,6 @@ struct Dispatcher : Mangle::Input::Event if(!isBound(index)) return; - // Only treat key-down events for now - if(type != EV_KeyDown) - return; - // Get the mapped actions and execute them const _O &list = map.getList(index); _O::const_iterator it; @@ -45,5 +41,10 @@ struct Dispatcher : Mangle::Input::Event funcs.call(*it, p); } }; + +// This helps us play nice with Mangle's EventPtr, but it should +// really be defined for all the classes in OEngine. + typedef boost::shared_ptr DispatcherPtr; + }} #endif diff --git a/ogre/exitlistener.hpp b/ogre/exitlistener.hpp index ed07ee55c8..5a9d1ff68f 100644 --- a/ogre/exitlistener.hpp +++ b/ogre/exitlistener.hpp @@ -21,13 +21,17 @@ namespace Render ExitListener(Ogre::RenderWindow *wnd) : window(wnd), exit(false) {} - bool frameStarted(const FrameEvent &evt) + bool frameStarted(const Ogre::FrameEvent &evt) { if(window->isClosed()) exit = true; return !exit; } + + // Function equivalent of setting exit=true. Handy when you need a + // delegate to bind to an event. + void exitNow() { exit = true; } }; }} #endif diff --git a/ogre/mouselook.hpp b/ogre/mouselook.hpp index b5c7849452..6e09ff4a10 100644 --- a/ogre/mouselook.hpp +++ b/ogre/mouselook.hpp @@ -38,7 +38,7 @@ namespace Render bool prot=true) : camera(cam) , sensX(sX) - , sensY(sy) + , sensY(sY) , flipProt(prot) {} @@ -50,5 +50,7 @@ namespace Render void event(Type type, int index, const void *p); }; + + typedef boost::shared_ptr MouseLookEventPtr; }} #endif From 69a56e867749944dc0e86b9ef5c54bac4339b454 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 16 Jul 2010 14:54:41 +0200 Subject: [PATCH 066/269] Added more safety asserts to input dispatcher --- input/dispatcher.hpp | 13 +++++++++++-- input/func_binder.hpp | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/input/dispatcher.hpp b/input/dispatcher.hpp index 930195856a..dd6fb6bf6d 100644 --- a/input/dispatcher.hpp +++ b/input/dispatcher.hpp @@ -4,6 +4,7 @@ #include "dispatch_map.hpp" #include "func_binder.hpp" #include +#include namespace OEngine { namespace Input { @@ -19,8 +20,16 @@ struct Dispatcher : Mangle::Input::Event */ Dispatcher(int actions) : funcs(actions) {} - void bind(int action, int key) { map.bind(key, action); } - void unbind(int action, int key) { map.unbind(key, action); } + void bind(unsigned int action, int key) + { + assert(action < funcs.getSize()); + map.bind(key, action); + } + void unbind(unsigned int action, int key) + { + assert(action < funcs.getSize()); + map.unbind(key, action); + } bool isBound(int key) const { return map.isBound(key); } /** diff --git a/input/func_binder.hpp b/input/func_binder.hpp index d2bfd53e5b..7aa733edf8 100644 --- a/input/func_binder.hpp +++ b/input/func_binder.hpp @@ -50,6 +50,8 @@ public: */ FuncBinder(int number) : bindings(number) {} + unsigned int getSize() { return bindings.size(); } + /** Bind an action to an index. */ From c7b179d6546688208528c8eef681d42b7c1ec7be Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 16 Jul 2010 21:44:47 +0200 Subject: [PATCH 067/269] Added EV_Keyboard and EV_Mouse catch-all event flags --- input/event.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/input/event.hpp b/input/event.hpp index f7ee7e58b7..dc7b470887 100644 --- a/input/event.hpp +++ b/input/event.hpp @@ -18,9 +18,12 @@ namespace Mangle EV_Unknown = 1, // Unknown event type EV_KeyDown = 2, // Keyboard button was pressed EV_KeyUp = 4, // Keyboard button was released + EV_Keyboard = 6, // All keyboard events + EV_MouseMove = 8, // Mouse movement EV_MouseDown = 16, // Mouse button pressed EV_MouseUp = 32, // Mouse button released + EV_Mouse = 56, // All mouse events EV_ALL = 63 // All events }; From fedb1a80256a093511075aeb88408c7c56a136ce Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 16 Jul 2010 21:46:57 +0200 Subject: [PATCH 068/269] Fixed up the OIS->MyGUI event dispatcher --- gui/events.cpp | 59 +++++++++++++++++++++++++++++++++++++++---------- gui/events.hpp | 2 +- gui/manager.hpp | 2 ++ 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/gui/events.cpp b/gui/events.cpp index 146d3ea802..6e4d53309b 100644 --- a/gui/events.cpp +++ b/gui/events.cpp @@ -3,24 +3,59 @@ #include "events.hpp" -using namespace MyGUI; using namespace OIS; using namespace OEngine::GUI; void EventInjector::event(Type type, int index, const void *p) { - if(enabled) return; + if(!enabled) return; - KeyEvent *key = (KeyEvent*)p; - MouseEvent *mouse = (MouseEvent*)p; - MouseButtonID id = (MouseButtonID)index; - - switch(type) + if(type & EV_Keyboard) { - case EV_KeyDown: gui->injectKeyPress(key); break; - case EV_KeyUp: gui->injectKeyRelease(key); break; - case EV_MouseDown: gui->injectMousePress(mouse, id); break; - case EV_MouseUp: gui->injectMouseRelease(mouse, id); break; - case EV_MouseMove: gui->injectMouseMove(mouse); break; + KeyEvent *key = (KeyEvent*)p; + MyGUI::KeyCode code = MyGUI::KeyCode::Enum(key->key); + if(type == EV_KeyDown) + { + /* + This is just a first approximation. Apparently, OIS sucks + to such a degree that it's unable to provide any sort of + reliable unicode character on all platforms and for all + keys. At least that's what I surmise from the amount of + workaround that the MyGUI folks have put in place for + this. See Common/Input/OIS/InputManager.cpp in the MyGUI + sources for details. If this is indeed necessary (I + haven't tested that it is, although I have had dubious + experinces with OIS events in the past), then we should + probably adapt all that code here. Or even better, + directly into the OIS input manager in Mangle. + + Note that all this only affects the 'text' field, and + should thus only affect typed text in input boxes (which + is still pretty significant.) + */ + MyGUI::Char text = (MyGUI::Char)key->text; + gui->injectKeyPress(code,text); + } + else + { + gui->injectKeyRelease(code); + } + } + else if(type & EV_Mouse) + { + MouseEvent *mouse = (MouseEvent*)p; + MyGUI::MouseButton id = MyGUI::MouseButton::Enum(index); + + // I'm not sure these should be used directly, MyGUI demo code + // use local mouse position variables. + int mouseX = mouse->state.X.abs; + int mouseY = mouse->state.Y.abs; + + if(type == EV_MouseDown) + gui->injectMousePress(mouseX, mouseY, id); + else if(type == EV_MouseUp) + gui->injectMouseRelease(mouseX, mouseY, id); + else + gui->injectMouseMove(mouseX, mouseY, mouse->state.Z.abs); } } diff --git a/gui/events.hpp b/gui/events.hpp index ec483536af..f438848456 100644 --- a/gui/events.hpp +++ b/gui/events.hpp @@ -13,7 +13,7 @@ namespace GUI { /** Event handler that injects OIS events into MyGUI */ - class EventInjector : Mangle::Input::Event + class EventInjector : public Mangle::Input::Event { MyGUI::Gui *gui; diff --git a/gui/manager.hpp b/gui/manager.hpp index 5f67e9f91a..e59b4b54bd 100644 --- a/gui/manager.hpp +++ b/gui/manager.hpp @@ -29,6 +29,8 @@ namespace GUI void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false); void shutdown(); + + MyGUI::Gui *getGui() { return mGui; } }; }} #endif From 82a3c071e56f2df451618e1371424c39aa299690 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 17 Jul 2010 19:29:39 +0200 Subject: [PATCH 069/269] Added EventInjectorPtr definition --- gui/events.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/events.hpp b/gui/events.hpp index f438848456..17415906d0 100644 --- a/gui/events.hpp +++ b/gui/events.hpp @@ -23,5 +23,7 @@ namespace GUI EventInjector(MyGUI::Gui *g) : gui(g), enabled(true) {} void event(Type type, int index, const void *p); }; + + typedef boost::shared_ptr EventInjectorPtr; }} #endif From c04d72cbe380217c2d1d60f8a2c6e4810fe4c050 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 20 Jul 2010 20:22:15 +0200 Subject: [PATCH 070/269] Let gui/events handle mouse position locally, makes it remember position when disabled and reenabled. --- gui/events.cpp | 39 +++++++++++++++++++++++++++++---------- gui/events.hpp | 4 +++- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/gui/events.cpp b/gui/events.cpp index 6e4d53309b..73274ee505 100644 --- a/gui/events.cpp +++ b/gui/events.cpp @@ -1,11 +1,27 @@ #include #include +#include #include "events.hpp" using namespace OIS; using namespace OEngine::GUI; +EventInjector::EventInjector(MyGUI::Gui *g) + : gui(g), mouseX(0), mouseY(0), enabled(true) +{ + assert(gui); + maxX = gui->getViewWidth(); + maxY = gui->getViewHeight(); +} + +template +void setRange(X &x, X min, X max) +{ + if(x < min) x = min; + else if(x > max) x = max; +} + void EventInjector::event(Type type, int index, const void *p) { if(!enabled) return; @@ -17,13 +33,14 @@ void EventInjector::event(Type type, int index, const void *p) if(type == EV_KeyDown) { /* - This is just a first approximation. Apparently, OIS sucks - to such a degree that it's unable to provide any sort of - reliable unicode character on all platforms and for all - keys. At least that's what I surmise from the amount of - workaround that the MyGUI folks have put in place for + This is just a first approximation. Apparently, OIS is + unable to provide reliable unicode characters on all + platforms. At least that's what I surmise from the amount + of workaround that the MyGUI folks have put in place for this. See Common/Input/OIS/InputManager.cpp in the MyGUI - sources for details. If this is indeed necessary (I + sources for details. + + If the work they have done there is indeed necessary (I haven't tested that it is, although I have had dubious experinces with OIS events in the past), then we should probably adapt all that code here. Or even better, @@ -46,10 +63,12 @@ void EventInjector::event(Type type, int index, const void *p) MouseEvent *mouse = (MouseEvent*)p; MyGUI::MouseButton id = MyGUI::MouseButton::Enum(index); - // I'm not sure these should be used directly, MyGUI demo code - // use local mouse position variables. - int mouseX = mouse->state.X.abs; - int mouseY = mouse->state.Y.abs; + // Update mouse position + mouseX += mouse->state.X.rel; + mouseY += mouse->state.Y.rel; + + setRange(mouseX,0,maxX); + setRange(mouseY,0,maxY); if(type == EV_MouseDown) gui->injectMousePress(mouseX, mouseY, id); diff --git a/gui/events.hpp b/gui/events.hpp index 17415906d0..6ca83cf753 100644 --- a/gui/events.hpp +++ b/gui/events.hpp @@ -16,11 +16,13 @@ namespace GUI class EventInjector : public Mangle::Input::Event { MyGUI::Gui *gui; + int mouseX, mouseY; + int maxX, maxY; public: bool enabled; - EventInjector(MyGUI::Gui *g) : gui(g), enabled(true) {} + EventInjector(MyGUI::Gui *g); void event(Type type, int index, const void *p); }; From 4692375491fb16da59642022c8d7c891d68ba665 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 27 Jul 2010 12:12:06 +0200 Subject: [PATCH 071/269] Added convenience classes for connecting sound to Ogre --- input/clients/ogre_input_capture.hpp | 6 +-- sound/clients/ogre_listener_mover.hpp | 69 +++++++++++++++++++++++++++ sound/clients/ogre_output_updater.hpp | 31 ++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 sound/clients/ogre_listener_mover.hpp create mode 100644 sound/clients/ogre_output_updater.hpp diff --git a/input/clients/ogre_input_capture.hpp b/input/clients/ogre_input_capture.hpp index 6a7beae4d2..2e77dc10b1 100644 --- a/input/clients/ogre_input_capture.hpp +++ b/input/clients/ogre_input_capture.hpp @@ -15,15 +15,15 @@ namespace Input { { Mangle::Input::Driver &driver; - ExitListener(Mangle::Input::Driver &drv) + OgreInputCapture(Mangle::Input::Driver &drv) : driver(drv) {} - bool frameStarted(const FrameEvent &evt) + bool frameStarted(const Ogre::FrameEvent &evt) { driver.capture(); return true; } }; -} +}} #endif diff --git a/sound/clients/ogre_listener_mover.hpp b/sound/clients/ogre_listener_mover.hpp new file mode 100644 index 0000000000..d1cfcb65ce --- /dev/null +++ b/sound/clients/ogre_listener_mover.hpp @@ -0,0 +1,69 @@ +#ifndef MANGLE_SOUND_OGRELISTENERMOVER_H +#define MANGLE_SOUND_OGRELISTENERMOVER_H + +#include +#include +#include "../output.hpp" + +namespace Mangle { +namespace Sound { + + /** This class lets a sound listener (ie. the SoundFactory) track a + given camera in Ogre3D. The poisition and orientation of the + listener will be updated to match the camera whenever the camera + is moved. + */ + struct OgreListenerMover : Ogre::Camera::Listener + { + OgreListenerMover(Mangle::Sound::SoundFactoryPtr snd) + : soundFact(snd), camera(NULL) + {} + + /// Follow a camera. WARNING: This will OVERRIDE any other + /// MovableObject::Listener you may have attached to the camera. + void followCamera(Ogre::Camera *cam) + { + camera = cam; + camera->addListener(this); + } + + private: + Mangle::Sound::SoundFactoryPtr soundFact; + Ogre::Camera *camera; + Ogre::Vector3 pos, dir, up; + + /// From Camera::Listener. This is called once per + /// frame. Unfortunately, Ogre doesn't allow us to be notified + /// only when the camera itself has moved, so we must poll every + /// frame. + void cameraPreRenderScene(Ogre::Camera *cam) + { + assert(cam == camera); + + Ogre::Vector3 nPos, nDir, nUp; + + nPos = camera->getPosition(); + nDir = camera->getDirection(); + nUp = camera->getUp(); + + // Don't bother the sound system needlessly + if(nDir != dir || nPos != pos || nUp != up) + { + pos = nPos; + dir = nDir; + up = nUp; + + soundFact->setListenerPos(pos.x, pos.y, pos.z, + dir.x, dir.y, dir.z, + up.x, up.y, up.z); + } + } + + void cameraDestroyed(Ogre::Camera *cam) + { + assert(cam == camera); + camera = NULL; + } + }; +}} +#endif diff --git a/sound/clients/ogre_output_updater.hpp b/sound/clients/ogre_output_updater.hpp new file mode 100644 index 0000000000..5072e9f1e8 --- /dev/null +++ b/sound/clients/ogre_output_updater.hpp @@ -0,0 +1,31 @@ +#ifndef MANGLE_SOUND_OGREUPDATER_H +#define MANGLE_SOUND_OGREUPDATER_H + +/* + This Ogre FrameListener calls update on a SoundFactory + */ + +#include +#include "../output.hpp" +#include + +namespace Mangle { +namespace Sound { + + struct OgreOutputUpdater : Ogre::FrameListener + { + Mangle::Sound::SoundFactory &driver; + + OgreOutputUpdater(Mangle::Sound::SoundFactory &drv) + : driver(drv) + { assert(drv.needsUpdate); } + + bool frameStarted(const Ogre::FrameEvent &evt) + { + driver.update(); + return true; + } + }; +}} + +#endif From a202371ef19b9740dc4ca86632af18ab0c614078 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 30 Jul 2010 22:25:06 +0200 Subject: [PATCH 072/269] mangle update --- mangle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mangle b/mangle index 18dc065715..4692375491 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit 18dc065715c12fd8793f8793c7bf834619527399 +Subproject commit 4692375491fb16da59642022c8d7c891d68ba665 From 687f20344e86356148118a3db920ae0f6c69c573 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 3 Aug 2010 14:25:56 +0200 Subject: [PATCH 073/269] Added EventListPtr typedef --- input/filters/eventlist.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/input/filters/eventlist.hpp b/input/filters/eventlist.hpp index d7703fbc4f..b3e2ff8f24 100644 --- a/input/filters/eventlist.hpp +++ b/input/filters/eventlist.hpp @@ -40,6 +40,8 @@ namespace Mangle } } }; + + typedef boost::shared_ptr EventListPtr; } } #endif From 21d399cb813cdcb1e5854a268ed34913d1d4c8fa Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 3 Aug 2010 18:02:44 +0200 Subject: [PATCH 074/269] Modified Stream::PureFilter to allowed delayed setting of stream --- stream/filters/pure_filter.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stream/filters/pure_filter.hpp b/stream/filters/pure_filter.hpp index e4ae243750..fe95e20750 100644 --- a/stream/filters/pure_filter.hpp +++ b/stream/filters/pure_filter.hpp @@ -15,9 +15,13 @@ class PureFilter : public Stream StreamPtr src; public: + PureFilter() {} PureFilter(StreamPtr _src) - : src(_src) + { setStream(_src); } + + void setStream(StreamPtr _src) { + src = _src; isSeekable = src->isSeekable; hasPosition = src->hasPosition; hasSize = src->hasSize; From 71366d9a0773e8214d252e7c8c4a1e3830069fd1 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 4 Aug 2010 12:20:46 +0200 Subject: [PATCH 075/269] Added writing capability to streams. Created OutFileStream. --- stream/clients/ogre_datastream.hpp | 9 ++- stream/filters/pure_filter.hpp | 2 + stream/filters/slice_stream.hpp | 21 ++++++- stream/servers/outfile_stream.hpp | 41 +++++++++++++ stream/servers/std_ostream.hpp | 79 +++++++++++++++++++++++++ stream/servers/std_stream.hpp | 2 +- stream/stream.hpp | 14 ++++- stream/tests/.gitignore | 1 + stream/tests/Makefile | 5 +- stream/tests/file_write_test.cpp | 41 +++++++++++++ stream/tests/output/file_write_test.out | 12 ++++ vfs/clients/ogre_archive.hpp | 6 +- 12 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 stream/servers/outfile_stream.hpp create mode 100644 stream/servers/std_ostream.hpp create mode 100644 stream/tests/file_write_test.cpp create mode 100644 stream/tests/output/file_write_test.out diff --git a/stream/clients/ogre_datastream.hpp b/stream/clients/ogre_datastream.hpp index 7b4106001f..76a6f20cfb 100644 --- a/stream/clients/ogre_datastream.hpp +++ b/stream/clients/ogre_datastream.hpp @@ -23,6 +23,10 @@ class Mangle2OgreStream : public Ogre::DataStream // Get the size, if possible if(inp->hasSize) mSize = inp->size(); + + // Allow writing if inp supports it + if(inp->isWritable) + mAccess |= Ogre::DataStream::WRITE; } public: @@ -37,7 +41,10 @@ class Mangle2OgreStream : public Ogre::DataStream // Only implement the DataStream functions we have to implement size_t read(void *buf, size_t count) - { return inp->read(buf,count); } + { return inp->read(buf,count); } + + size_t write(const void *buf, size_t count) + { assert(inp->isWritable); return inp->write(buf,count); } void skip(long count) { diff --git a/stream/filters/pure_filter.hpp b/stream/filters/pure_filter.hpp index fe95e20750..ed1690d8c9 100644 --- a/stream/filters/pure_filter.hpp +++ b/stream/filters/pure_filter.hpp @@ -23,12 +23,14 @@ class PureFilter : public Stream { src = _src; isSeekable = src->isSeekable; + isWritable = src->isWritable; hasPosition = src->hasPosition; hasSize = src->hasSize; hasPtr = src->hasPtr; } size_t read(void *buf, size_t count) { return src->read(buf, count); } + size_t write(const void *buf, size_t count) { return src->write(buf,count); } void seek(size_t pos) { src->seek(pos); } size_t tell() const { return src->tell(); } size_t size() const { return src->size(); } diff --git a/stream/filters/slice_stream.hpp b/stream/filters/slice_stream.hpp index 49f241cfcf..20f202bdc6 100644 --- a/stream/filters/slice_stream.hpp +++ b/stream/filters/slice_stream.hpp @@ -27,6 +27,7 @@ class SliceStream : public Stream hasPosition = true; hasSize = true; hasPtr = src->hasPtr; + isWritable = src->isWritable; } size_t read(void *buf, size_t count) @@ -35,7 +36,7 @@ class SliceStream : public Stream if(count > length-pos) count = length-pos; - // Seek into place and reading + // Seek into place and start reading src->seek(offset+pos); count = src->read(buf, count); @@ -44,6 +45,24 @@ class SliceStream : public Stream return count; } + // Note that writing to a slice does not allow you to append data, + // you may only overwrite existing data. + size_t write(const void *buf, size_t count) + { + assert(isWritable); + // Check that we're not reading past our slice + if(count > length-pos) + count = length-pos; + + // Seek into place and action + src->seek(offset+pos); + count = src->write(buf, count); + + pos += count; + assert(pos <= length); + return count; + } + void seek(size_t _pos) { pos = _pos; diff --git a/stream/servers/outfile_stream.hpp b/stream/servers/outfile_stream.hpp new file mode 100644 index 0000000000..37504d78c3 --- /dev/null +++ b/stream/servers/outfile_stream.hpp @@ -0,0 +1,41 @@ +#ifndef MANGLE_STREAM_FILESERVER_H +#define MANGLE_STREAM_FILESERVER_H + +#include "std_ostream.hpp" +#include + +namespace Mangle { +namespace Stream { + +/** File stream based on std::ofstream, only supports writing. + */ +class OutFileStream : public StdOStream +{ + std::ofstream file; + + public: + /** + By default we overwrite the file. If append=true, then we will + open an existing file and seek to the end instead. + */ + OutFileStream(const std::string &name, bool append=false) + : StdOStream(&file) + { + std::ios::openmode mode = std::ios::binary; + if(append) + mode |= std::ios::app; + else + mode |= std::ios::trunc; + + file.open(name.c_str(), mode); + + if(file.fail()) + throw str_exception("OutFileStream: failed to open file " + name); + } + ~OutFileStream() { file.close(); } +}; + +typedef boost::shared_ptr OutFileStreamPtr; + +}} // namespaces +#endif diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp new file mode 100644 index 0000000000..32999e8c15 --- /dev/null +++ b/stream/servers/std_ostream.hpp @@ -0,0 +1,79 @@ +#ifndef MANGLE_STREAM_STDIOSERVER_H +#define MANGLE_STREAM_STDIOSERVER_H + +#include "../stream.hpp" +#include +#include "../../tools/str_exception.hpp" + +namespace Mangle { +namespace Stream { + +/** Simple wrapper for std::ostream, only supports output. + */ +class StdOStream : public Stream +{ + std::ostream *inf; + + static void fail(const std::string &msg) + { throw str_exception("StdOStream: " + msg); } + + public: + StdOStream(std::ostream *_inf) + : inf(_inf) + { + isSeekable = true; + hasPosition = true; + hasSize = true; + isWritable = true; + } + + size_t read(void*,size_t) + { + assert(0&&"reading not supported by StdOStream"); + } + + size_t write(const void* buf, size_t len) + { + inf->write((const char*)buf, len); + if(inf->fail()) + fail("error writing to stream"); + + // Unfortunately, stupid std::ostream doesn't have a pcount() to + // match gcount() for input. In general the std::iostream system + // is an idiotically designed stream library. + return len; + } + + void seek(size_t pos) + { + inf->seekp(pos); + if(inf->fail()) + fail("seek error"); + } + + size_t tell() const + // Hack around the fact that ifstream->tellp() isn't const + { return ((StdOStream*)this)->inf->tellp(); } + + size_t size() const + { + // Use the standard iostream size hack, terrible as it is. + std::streampos pos = inf->tellp(); + inf->seekp(0, std::ios::end); + size_t res = inf->tellp(); + inf->seekp(pos); + + if(inf->fail()) + fail("could not get stream size"); + + return res; + } + + bool eof() const + { return inf->eof(); } +}; + +typedef boost::shared_ptr StdOStreamPtr; + +}} // namespaces +#endif diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index bb8bb6cbc9..448306e5ca 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -8,7 +8,7 @@ namespace Mangle { namespace Stream { -/** Simplest wrapper for std::istream. +/** Simple wrapper for std::istream. */ class StdStream : public Stream { diff --git a/stream/stream.hpp b/stream/stream.hpp index a195fe3219..bc369f6cd5 100644 --- a/stream/stream.hpp +++ b/stream/stream.hpp @@ -23,13 +23,17 @@ class Stream /// If true, size() works bool hasSize; + /// If true, write() works. Writing through pointer operations is + /// not supported. + bool isWritable; + /// If true, the getPtr() functions work bool hasPtr; /// Initialize all bools to false by default Stream() : isSeekable(false), hasPosition(false), hasSize(false), - hasPtr(false) {} + isWritable(false), hasPtr(false) {} /// Virtual destructor virtual ~Stream() {} @@ -40,6 +44,14 @@ class Stream */ virtual size_t read(void* buf, size_t count) = 0; + /** Write a given number of bytes from the stream. Semantics is + similar to read(). Only valid if isWritable is true + + Since most implementations do NOT support writing we default to + an assert(0) here. + */ + virtual size_t write(const void *buf, size_t count) { assert(0); return 0; } + /// Seek to an absolute position in this stream. Not all streams are /// seekable. virtual void seek(size_t pos) = 0; diff --git a/stream/tests/.gitignore b/stream/tests/.gitignore index 8144904045..9dfd618e2c 100644 --- a/stream/tests/.gitignore +++ b/stream/tests/.gitignore @@ -1 +1,2 @@ *_test +test.file diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 8504de7414..924a252c35 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test +all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -15,6 +15,9 @@ audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_fi file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ +file_write_test: file_write_test.cpp ../stream.hpp ../servers/outfile_stream.hpp ../servers/std_ostream.hpp + $(GCC) $< -o $@ + memory_server_test: memory_server_test.cpp ../stream.hpp ../servers/memory_stream.hpp $(GCC) $< -o $@ diff --git a/stream/tests/file_write_test.cpp b/stream/tests/file_write_test.cpp new file mode 100644 index 0000000000..c6c61fccae --- /dev/null +++ b/stream/tests/file_write_test.cpp @@ -0,0 +1,41 @@ +#include "../servers/outfile_stream.hpp" +#include + +using namespace Mangle::Stream; +using namespace std; + +void print(Stream &str) +{ + cout << "size=" << str.size() + << " pos=" << str.tell() + << " eof=" << str.eof() + << endl; +} + +int main() +{ + { + cout << "\nCreating file\n"; + OutFileStream out("test.file"); + print(out); + out.write("hello",5); + print(out); + } + + { + cout << "\nAppending to file\n"; + OutFileStream out("test.file", true); + print(out); + out.write(" again\n",7); + print(out); + } + + { + cout << "\nOverwriting file\n"; + OutFileStream out("test.file"); + print(out); + out.write("overwrite!\n",11); + print(out); + } + return 0; +} diff --git a/stream/tests/output/file_write_test.out b/stream/tests/output/file_write_test.out new file mode 100644 index 0000000000..34b07c49f6 --- /dev/null +++ b/stream/tests/output/file_write_test.out @@ -0,0 +1,12 @@ + +Creating file +size=0 pos=0 eof=0 +size=5 pos=5 eof=0 + +Appending to file +size=5 pos=5 eof=0 +size=12 pos=12 eof=0 + +Overwriting file +size=0 pos=0 eof=0 +size=11 pos=11 eof=0 diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index a4986d7b9f..77191881a9 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -11,9 +11,9 @@ namespace VFS { /** An OGRE Archive implementation that wraps a Mangle::VFS filesystem. - This has been built and tested against OGRE 1.6.2. You might have - to make your own modifications if you're working with newer (or - older) versions. + This has been built and tested against OGRE 1.6.2, and has been + extended for OGRE 1.7. You might have to make your own + modifications if you're working with newer (or older) versions. */ class MangleArchive : public Ogre::Archive { From d3f0932b20762bc4ac342ade7926dffac2ccbd00 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 4 Aug 2010 12:47:48 +0200 Subject: [PATCH 076/269] Added Stream::flush() --- stream/filters/pure_filter.hpp | 1 + stream/filters/slice_stream.hpp | 1 + stream/servers/std_ostream.hpp | 11 +++++++---- stream/stream.hpp | 3 +++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/stream/filters/pure_filter.hpp b/stream/filters/pure_filter.hpp index ed1690d8c9..f0ce91f876 100644 --- a/stream/filters/pure_filter.hpp +++ b/stream/filters/pure_filter.hpp @@ -31,6 +31,7 @@ class PureFilter : public Stream size_t read(void *buf, size_t count) { return src->read(buf, count); } size_t write(const void *buf, size_t count) { return src->write(buf,count); } + void flush() { src->flush(); } void seek(size_t pos) { src->seek(pos); } size_t tell() const { return src->tell(); } size_t size() const { return src->size(); } diff --git a/stream/filters/slice_stream.hpp b/stream/filters/slice_stream.hpp index 20f202bdc6..6337b9d57d 100644 --- a/stream/filters/slice_stream.hpp +++ b/stream/filters/slice_stream.hpp @@ -72,6 +72,7 @@ class SliceStream : public Stream bool eof() const { return pos == length; } size_t tell() const { return pos; } size_t size() const { return length; } + void flush() { src->flush(); } const void *getPtr() { return getPtr(0, length); } const void *getPtr(size_t size) diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index 32999e8c15..9ec6609e96 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -37,13 +37,16 @@ class StdOStream : public Stream inf->write((const char*)buf, len); if(inf->fail()) fail("error writing to stream"); - - // Unfortunately, stupid std::ostream doesn't have a pcount() to - // match gcount() for input. In general the std::iostream system - // is an idiotically designed stream library. + // Just return len, but that is ok. The only cases where we would + // return less than len is when an error occured. return len; } + void flush() + { + inf->flush(); + } + void seek(size_t pos) { inf->seekp(pos); diff --git a/stream/stream.hpp b/stream/stream.hpp index bc369f6cd5..aa9c14a9ed 100644 --- a/stream/stream.hpp +++ b/stream/stream.hpp @@ -52,6 +52,9 @@ class Stream */ virtual size_t write(const void *buf, size_t count) { assert(0); return 0; } + /// Flush an output stream. Does nothing for non-writing streams. + virtual void flush() {} + /// Seek to an absolute position in this stream. Not all streams are /// seekable. virtual void seek(size_t pos) = 0; From 073cc246b6007099888b7718f23e5f04085fe30a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 4 Aug 2010 19:59:11 +0200 Subject: [PATCH 077/269] Fixed include guards on std::ostream wrappers --- stream/servers/outfile_stream.hpp | 4 ++-- stream/servers/std_ostream.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stream/servers/outfile_stream.hpp b/stream/servers/outfile_stream.hpp index 37504d78c3..2946ff853a 100644 --- a/stream/servers/outfile_stream.hpp +++ b/stream/servers/outfile_stream.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_STREAM_FILESERVER_H -#define MANGLE_STREAM_FILESERVER_H +#ifndef MANGLE_OSTREAM_FILESERVER_H +#define MANGLE_OSTREAM_FILESERVER_H #include "std_ostream.hpp" #include diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index 9ec6609e96..f655477ff2 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_STREAM_STDIOSERVER_H -#define MANGLE_STREAM_STDIOSERVER_H +#ifndef MANGLE_OSTREAM_STDIOSERVER_H +#define MANGLE_OSTREAM_STDIOSERVER_H #include "../stream.hpp" #include From e181b71cc9d1a647734547d1327ad1185eac5a90 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 5 Aug 2010 12:14:42 +0200 Subject: [PATCH 078/269] Minor fix to input_filter.hpp --- sound/filters/input_filter.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sound/filters/input_filter.hpp b/sound/filters/input_filter.hpp index 267858de13..00ee187660 100644 --- a/sound/filters/input_filter.hpp +++ b/sound/filters/input_filter.hpp @@ -10,13 +10,12 @@ namespace Sound { /** @brief This filter class adds file loading capabilities to a - Sound::SoundFactory class, by associating an SampleSourceLoader - with it. + Sound::SoundFactory class, by associating a SampleSourceLoader with + it. The class takes an existing SoundFactory able to load streams, and - associates an SampleSourceLoader with it. The combined class is - able to load files directly. -*/ + associates a SampleSourceLoader with it. The combined class is able + to load files directly. */ class InputFilter : public SoundFactory { protected: @@ -45,7 +44,7 @@ class InputFilter : public SoundFactory // Both these should be true, or the use of this class is pretty // pointless canLoadSource = snd->canLoadSource; - canLoadFile = canLoadSource; + canLoadFile = inp->canLoadFile; assert(canLoadSource && canLoadFile); } From 3a6912b04c94866a4698c2fedf5f83f9f85cf792 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 5 Aug 2010 12:35:17 +0200 Subject: [PATCH 079/269] Added pure sound filters --- sound/filters/pure_filter.hpp | 67 +++++++++++++++++++++++++++++++++++ sound/output.hpp | 5 +-- 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 sound/filters/pure_filter.hpp diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp new file mode 100644 index 0000000000..5238d788a7 --- /dev/null +++ b/sound/filters/pure_filter.hpp @@ -0,0 +1,67 @@ +#ifndef MANGLE_SOUND_OUTPUT_PUREFILTER_H +#define MANGLE_SOUND_OUTPUT_PUREFILTER_H + +#include "../output.hpp" + +namespace Mangle +{ + namespace Sound + { + // For use in writing other filters + class SoundFilter : public Sound + { + SoundPtr client; + + public: + SoundFilter(SoundPtr c) : client(c) {} + void play() { client->play(); } + void stop() { client->stop(); } + void pause() { client->pause(); } + bool isPlaying() const { return client->isPlaying(); } + void setVolume(float f) { client->setVolume(f); } + void setPan(float f) { client->setPan(f); } + void setPos(float x, float y, float z) + { client->setPos(x,y,z); } + void setRepeat(bool b) { client->setRepeat(b); } + void setStreaming(bool b) { client->setStreaming(b); } + + // The clone() function is not implemented here, as you will + // almost certainly want to override it yourself + }; + + class FactoryFilter : public SoundFactory + { + SoundFactoryPtr client; + + public: + FactoryFilter(SoundFactoryPtr c) : client(c) + { + needsUpdate = client->needsUpdate; + has3D = client->has3D; + canLoadFile = client->canLoadFile; + canLoadStream = client->canLoadStream; + canLoadSource = client->canLoadSource; + } + + SoundPtr loadRaw(SampleSourcePtr input) + { return client->loadRaw(input); } + + SoundPtr load(Stream::StreamPtr input) + { return client->load(input); } + + SoundPtr load(const std::string &file) + { return client->load(file); } + + void update() + { client->update(); } + + void setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) + { + client->setListenerPos(x,y,z,fx,fy,fz,ux,uy,uz); + } + }; + } +} +#endif diff --git a/sound/output.hpp b/sound/output.hpp index 2ead277bcc..404dcf4f8d 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -2,8 +2,9 @@ #define MANGLE_SOUND_OUTPUT_H #include -#include "source.hpp" +#include +#include "source.hpp" #include "../stream/stream.hpp" namespace Mangle { @@ -149,7 +150,7 @@ class SoundFactory buffers and similar tasks. Implementations that do not need this should set needsUpdate to false. */ - virtual void update() = 0; + virtual void update() { assert(0); } /// Set listener position (coordinates, front and up vectors) /** From 87f6e739759244cef8e9730b8f94e628b8d64681 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 11 Aug 2010 11:59:08 +0200 Subject: [PATCH 080/269] pure sound filter: made 'client' protected --- sound/filters/pure_filter.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp index 5238d788a7..ffa49d35ab 100644 --- a/sound/filters/pure_filter.hpp +++ b/sound/filters/pure_filter.hpp @@ -10,6 +10,7 @@ namespace Mangle // For use in writing other filters class SoundFilter : public Sound { + protected: SoundPtr client; public: @@ -31,6 +32,7 @@ namespace Mangle class FactoryFilter : public SoundFactory { + protected: SoundFactoryPtr client; public: From 6443c161464e8959d6ee0c28e1ccebf9a6f57af9 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 11 Aug 2010 14:16:56 +0200 Subject: [PATCH 081/269] Made SoundManager and test --- mangle | 2 +- misc/list.hpp | 110 +++++++++++++++ sound/sndmanager.cpp | 208 +++++++++++++++++++++++++++++ sound/sndmanager.hpp | 93 +++++++++++++ sound/tests/Makefile | 13 ++ sound/tests/sound_manager_test.cpp | 50 +++++++ sound/tests/test.sh | 18 +++ 7 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 misc/list.hpp create mode 100644 sound/sndmanager.cpp create mode 100644 sound/sndmanager.hpp create mode 100644 sound/tests/Makefile create mode 100644 sound/tests/sound_manager_test.cpp create mode 100755 sound/tests/test.sh diff --git a/mangle b/mangle index 4692375491..87f6e73975 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit 4692375491fb16da59642022c8d7c891d68ba665 +Subproject commit 87f6e739759244cef8e9730b8f94e628b8d64681 diff --git a/misc/list.hpp b/misc/list.hpp new file mode 100644 index 0000000000..07e5ddc8d1 --- /dev/null +++ b/misc/list.hpp @@ -0,0 +1,110 @@ +#ifndef MISC_LIST_H +#define MISC_LIST_H + +#include + +namespace Misc{ + +/* + This is just a suggested data structure for List. You can use + anything that has next and prev pointers. + */ +template +struct ListElem +{ + X data; + ListElem *next; + ListElem *prev; +}; + +/* + A generic class that contains a doubly linked list of elements. It + does not do any allocation of elements, it just keeps pointers to + them. +*/ +template +struct List +{ + List() : head(0), tail(0), totalNum(0) {} + + // Insert an element at the end of the list. The element cannot be + // part of any other list when this is called. + void insert(Elem *p) + { + if(tail) + { + // There are existing elements. Insert the node at the end of + // the list. + assert(head && totalNum > 0); + tail->next = p; + } + else + { + // This is the first element + assert(head == 0 && totalNum == 0); + head = p; + } + + // These have to be done in either case + p->prev = tail; + p->next = 0; + tail = p; + + totalNum++; + } + + // Remove element from the list. The element MUST be part of the + // list when this is called. + void remove(Elem *p) + { + assert(totalNum > 0); + + if(p->next) + { + // There's an element following us. Set it up correctly. + p->next->prev = p->prev; + assert(tail && tail != p); + } + else + { + // We're the tail + assert(tail == p); + tail = p->prev; + } + + // Now do exactly the same for the previous element + if(p->prev) + { + p->prev->next = p->next; + assert(head && head != p); + } + else + { + assert(head == p); + head = p->next; + } + + totalNum--; + } + + // Pop the first element off the list + Elem *pop() + { + Elem *res = getHead(); + if(res) remove(res); + return res; + } + + Elem* getHead() { return head; } + Elem* getTail() { return tail; } + unsigned int getNum() { return totalNum; } + +private: + + Elem *head; + Elem *tail; + unsigned int totalNum; +}; + +} +#endif diff --git a/sound/sndmanager.cpp b/sound/sndmanager.cpp new file mode 100644 index 0000000000..d05dc1a49a --- /dev/null +++ b/sound/sndmanager.cpp @@ -0,0 +1,208 @@ +#include "sndmanager.hpp" + +#include +#include + +using namespace OEngine::Sound; +using namespace Mangle::Sound; + +/** This is our own internal implementation of the + Mangle::Sound::Sound interface. This class links a SoundPtr to + itself and prevents itself from being deleted as long as the sound + is playing. + */ +struct OEngine::Sound::ManagedSound : SoundFilter +{ +private: + /** Who's your daddy? This is set if and only if we are listed + internally in the given SoundManager. + + It may be NULL if the manager has been deleted but the user + keeps their own SoundPtrs to the object. + */ + SoundManager *mgr; + + /** Keep a weak pointer to ourselves, which we convert into a + 'strong' pointer when we are playing. When 'self' is pointing to + ourselves, the object will never be deleted. + + This is used to make sure the sound is not deleted while + playing, unless it is explicitly ordered to do so by the + manager. + + TODO: This kind of construct is useful. If we need it elsewhere + later, template it. It would be generally useful in any system + where we poll to check if a resource is still needed, but where + manual references are allowed. + */ + typedef boost::weak_ptr WSoundPtr; + WSoundPtr weak; + SoundPtr self; + + // Keep this object from being deleted + void lock() + { + self = SoundPtr(weak); + } + + // Release the lock. This may or may not delete the object. Never do + // anything after calling unlock()! + void unlock() + { + self.reset(); + } + +public: + // Used for putting ourselves in linked lists + ManagedSound *next, *prev; + + /** Detach this sound from its manager. This means that the manager + will no longer know we exist. Typically only called when either + the sound or the manager is about to get deleted. + + Since this means update() will no longer be called, we also have + to unlock the sound manually since it will no longer be able to + do that itself. This means that the sound may be deleted, even + if it is still playing, when the manager is deleted. + + However, you are still allowed to keep and manage your own + SoundPtr references, but the lock/unlock system is disabled + after the manager is gone. + */ + void detach() + { + if(mgr) + { + mgr->detach(this); + mgr = NULL; + } + + // Unlock must be last command. Object may get deleted at this + // point. + unlock(); + } + + ManagedSound(SoundPtr snd, SoundManager *mg) + : SoundFilter(snd), mgr(mg) + {} + ~ManagedSound() { detach(); } + + // Needed to set up the weak pointer + void setup(SoundPtr self) + { + weak = WSoundPtr(self); + } + + // Override play() to mark the object as locked + void play() + { + SoundFilter::play(); + + // Lock the object so that it is not deleted while playing. Only + // do this if we have a manager, otherwise the object will never + // get unlocked. + if(mgr) lock(); + } + + // Override stop() and pause() + + // Called regularly by the manager + void update() + { + // If we're no longer playing, don't force object retention. + if(!isPlaying()) + unlock(); + + // unlock() may delete the object, so don't do anything below this + // point. + } + + // Not implemented yet + SoundPtr clone() const + { return SoundPtr(); } +}; + +struct SoundManager::SoundManagerList +{ +private: + // A linked list of ManagedSound objects. + typedef Misc::List SoundList; + SoundList list; + +public: + // Add a new sound to the list + void addNew(ManagedSound* snd) + { + list.insert(snd); + } + + // Remove a sound from the list + void remove(ManagedSound *snd) + { + list.remove(snd); + } + + // Number of sounds in the list + int numSounds() { return list.getNum(); } + + // Update all sounds + void updateAll() + { + for(ManagedSound *s = list.getHead(); s != NULL; s=s->next) + s->update(); + } + + // Detach and unlock all sounds + void detachAll() + { + for(ManagedSound *s = list.getHead(); s != NULL; s=s->next) + s->detach(); + } +}; + +SoundManager::SoundManager(SoundFactoryPtr fact) + : FactoryFilter(fact) +{ + needsUpdate = true; + list = new SoundManagerList; +} + +SoundManager::~SoundManager() +{ + // Detach all sounds + list->detachAll(); +} + +SoundPtr SoundManager::wrap(SoundPtr client) +{ + // Create and set up the sound wrapper + ManagedSound *snd = new ManagedSound(client,this); + SoundPtr ptr(snd); + snd->setup(ptr); + + // Add ourselves to the list of all sounds + list->addNew(snd); + + return ptr; +} + +// Remove the sound from this manager. +void SoundManager::detach(ManagedSound *sound) +{ + list->remove(sound); +} + +int SoundManager::numSounds() +{ + return list->numSounds(); +} + +void SoundManager::update() +{ + // Update all the sounds we own + list->updateAll(); + + // Update the source if it needs it + if(client->needsUpdate) + client->update(); +} diff --git a/sound/sndmanager.hpp b/sound/sndmanager.hpp new file mode 100644 index 0000000000..5ea0c4fc37 --- /dev/null +++ b/sound/sndmanager.hpp @@ -0,0 +1,93 @@ +#ifndef OENGINE_SOUND_MANAGER_H +#define OENGINE_SOUND_MANAGER_H + +#include + +namespace OEngine +{ + namespace Sound + { + using namespace Mangle::Sound; + + class ManagedSound; + + /** A manager of Mangle::Sounds. + + The sound manager is a wrapper around the more low-level + SoundFactory - although it is also itself an implementation of + SoundFactory. It will: + - keep a list of all created sounds + - let you iterate the list + - keep references to playing sounds so you don't have to + - auto-release references to sounds that are finished playing + (ie. deleting them if you're not referencing them) + */ + class SoundManager : public FactoryFilter + { + // Shove the implementation details into the cpp file. + struct SoundManagerList; + SoundManagerList *list; + + // Create a new sound wrapper based on the given source sound. + SoundPtr wrap(SoundPtr snd); + + /** Internal function. Will completely disconnect the given + sound from this manager. Called from ManagedSound. + */ + friend class ManagedSound; + void detach(ManagedSound *sound); + public: + SoundManager(SoundFactoryPtr fact); + ~SoundManager(); + void update(); + + /// Get number of sounds currently managed by this manager. + int numSounds(); + + SoundPtr loadRaw(SampleSourcePtr input) + { return wrap(client->loadRaw(input)); } + + SoundPtr load(Mangle::Stream::StreamPtr input) + { return wrap(client->load(input)); } + + SoundPtr load(const std::string &file) + { return wrap(client->load(file)); } + + // Play a sound immediately, and release when done unless you + // keep the returned SoundPtr. + SoundPtr play(Mangle::Stream::StreamPtr sound) + { + SoundPtr snd = load(sound); + snd->play(); + return snd; + } + + SoundPtr play(const std::string &sound) + { + SoundPtr snd = load(sound); + snd->play(); + return snd; + } + + // Ditto for 3D sounds + SoundPtr play3D(Mangle::Stream::StreamPtr sound, float x, float y, float z) + { + SoundPtr snd = load(sound); + snd->setPos(x,y,z); + snd->play(); + return snd; + } + + SoundPtr play3D(const std::string &sound, float x, float y, float z) + { + SoundPtr snd = load(sound); + snd->setPos(x,y,z); + snd->play(); + return snd; + } + }; + + typedef boost::shared_ptr SoundManagerPtr; + } +} +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile new file mode 100644 index 0000000000..9e84cb5803 --- /dev/null +++ b/sound/tests/Makefile @@ -0,0 +1,13 @@ +GCC=g++ -I../ + +all: sound_manager_test + +L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +L_OPENAL=$(shell pkg-config --libs openal) +L_AUDIERE=-laudiere + +sound_manager_test: sound_manager_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../.. + +clean: + rm *_test diff --git a/sound/tests/sound_manager_test.cpp b/sound/tests/sound_manager_test.cpp new file mode 100644 index 0000000000..7c09cd583d --- /dev/null +++ b/sound/tests/sound_manager_test.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include +#include + +#include + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; +using namespace OEngine::Sound; + +const std::string sound = "../../mangle/sound/tests/cow.wav"; + +int main() +{ + SoundFactoryPtr oaf(new OpenAL_Audiere_Factory); + SoundManagerPtr mg(new SoundManager(oaf)); + + cout << "Playing " << sound << "\n"; + + assert(mg->numSounds() == 0); + + /** Start the sound playing, and then let the pointer go out of + scope. Lower-level players (like 'oaf' above) will immediately + delete the sound. SoundManager OTOH will keep it until it's + finished. + */ + mg->play(sound); + + assert(mg->numSounds() == 1); + + // Loop while there are still sounds to manage + int i=0; + while(mg->numSounds() != 0) + { + i++; + assert(mg->numSounds() == 1); + usleep(10000); + if(mg->needsUpdate) + mg->update(); + } + cout << "Done playing.\n"; + + assert(mg->numSounds() == 0); + + return 0; +} diff --git a/sound/tests/test.sh b/sound/tests/test.sh new file mode 100755 index 0000000000..2d07708adc --- /dev/null +++ b/sound/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done From e2e5a2138da72e63f8d7c7cd626cf4ffadad6888 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 11 Aug 2010 14:38:20 +0200 Subject: [PATCH 082/269] Added clone() to ManagedSound --- sound/sndmanager.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/sndmanager.cpp b/sound/sndmanager.cpp index d05dc1a49a..3bf8555b34 100644 --- a/sound/sndmanager.cpp +++ b/sound/sndmanager.cpp @@ -104,8 +104,6 @@ public: if(mgr) lock(); } - // Override stop() and pause() - // Called regularly by the manager void update() { @@ -117,9 +115,12 @@ public: // point. } - // Not implemented yet SoundPtr clone() const - { return SoundPtr(); } + { + // Cloning only works when we have a manager. + assert(mgr); + return mgr->wrap(client->clone()); + } }; struct SoundManager::SoundManagerList From 2aa41dfffec4c84ff3a47208aaaa0bf425f417b2 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 12 Aug 2010 14:49:58 +0200 Subject: [PATCH 083/269] Added tests for 3d sound and pausing. --- sound/tests/Makefile | 5 ++- sound/tests/output/sound_3d_test.out | 3 ++ sound/tests/output/sound_manager_test.out | 5 +++ sound/tests/sound_3d_test.cpp | 46 +++++++++++++++++++++++ sound/tests/sound_manager_test.cpp | 27 ++++++++++++- 5 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 sound/tests/output/sound_3d_test.out create mode 100644 sound/tests/output/sound_manager_test.out create mode 100644 sound/tests/sound_3d_test.cpp diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 9e84cb5803..04952167f7 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: sound_manager_test +all: sound_manager_test sound_3d_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) @@ -9,5 +9,8 @@ L_AUDIERE=-laudiere sound_manager_test: sound_manager_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../.. +sound_3d_test: sound_3d_test.cpp ../../mangle/sound/sources/audiere_source.cpp ../../mangle/sound/outputs/openal_out.cpp ../../mangle/stream/clients/audiere_file.cpp ../sndmanager.cpp + $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) -I../.. + clean: rm *_test diff --git a/sound/tests/output/sound_3d_test.out b/sound/tests/output/sound_3d_test.out new file mode 100644 index 0000000000..a443c84f02 --- /dev/null +++ b/sound/tests/output/sound_3d_test.out @@ -0,0 +1,3 @@ +Playing at 0,0,0 +Playing at 1,1,0 +Playing at -1,0,0 diff --git a/sound/tests/output/sound_manager_test.out b/sound/tests/output/sound_manager_test.out new file mode 100644 index 0000000000..2b458493d2 --- /dev/null +++ b/sound/tests/output/sound_manager_test.out @@ -0,0 +1,5 @@ +Playing ../../mangle/sound/tests/cow.wav +Replaying +pause +restart +Done playing. diff --git a/sound/tests/sound_3d_test.cpp b/sound/tests/sound_3d_test.cpp new file mode 100644 index 0000000000..f5b197fd0b --- /dev/null +++ b/sound/tests/sound_3d_test.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include +#include + +#include + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; +using namespace OEngine::Sound; + +const std::string sound = "../../mangle/sound/tests/cow.wav"; + +SoundManagerPtr m; + +// Play and wait for finish +void play(float x, float y, float z) +{ + cout << "Playing at " << x << "," << y << "," << z << endl; + + SoundPtr snd = m->play3D(sound,x,y,z); + + while(snd->isPlaying()) + { + usleep(10000); + m->update(); + } +} + +int main() +{ + SoundFactoryPtr oaf(new OpenAL_Audiere_Factory); + SoundManagerPtr mg(new SoundManager(oaf)); + m = mg; + + mg->setListenerPos(0,0,0,0,1,0,0,0,1); + + play(0,0,0); + play(1,1,0); + play(-1,0,0); + + return 0; +} diff --git a/sound/tests/sound_manager_test.cpp b/sound/tests/sound_manager_test.cpp index 7c09cd583d..3794c4a3cf 100644 --- a/sound/tests/sound_manager_test.cpp +++ b/sound/tests/sound_manager_test.cpp @@ -33,15 +33,38 @@ int main() assert(mg->numSounds() == 1); // Loop while there are still sounds to manage - int i=0; while(mg->numSounds() != 0) { - i++; assert(mg->numSounds() == 1); usleep(10000); if(mg->needsUpdate) mg->update(); } + + SoundPtr snd = mg->play(sound); + cout << "Replaying\n"; + int i = 0; + while(mg->numSounds() != 0) + { + assert(mg->numSounds() == 1); + usleep(10000); + if(mg->needsUpdate) + mg->update(); + + if(i++ == 70) + { + cout << "pause\n"; + snd->pause(); + } + if(i == 130) + { + cout << "restart\n"; + snd->play(); + // Let the sound go out of scope + snd.reset(); + } + } + cout << "Done playing.\n"; assert(mg->numSounds() == 0); From b9d4dc448bc3be908653f9dea3c3450fb85ed107 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 12 Aug 2010 15:46:11 +0200 Subject: [PATCH 084/269] Switched to local include for OpenMW --- sound/sndmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sndmanager.cpp b/sound/sndmanager.cpp index 3bf8555b34..3911432366 100644 --- a/sound/sndmanager.cpp +++ b/sound/sndmanager.cpp @@ -1,6 +1,6 @@ #include "sndmanager.hpp" -#include +#include "../misc/list.hpp" #include using namespace OEngine::Sound; From fb2d077ca9935374dd46576e643d111feb325c90 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 12 Aug 2010 15:52:04 +0200 Subject: [PATCH 085/269] Minor fixes --- sound/outputs/openal_out.cpp | 2 +- stream/clients/audiere_file.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 81e2223627..c95c02ff6c 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -146,7 +146,7 @@ SoundPtr OpenAL_Sound::clone() const // Constructor used for cloned sounds OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) - : refCnt(ref), bufferID(buf) + : bufferID(buf), refCnt(ref) { // Increase the reference count assert(ref != NULL); diff --git a/stream/clients/audiere_file.cpp b/stream/clients/audiere_file.cpp index 1d8e5b7065..16bc7891aa 100644 --- a/stream/clients/audiere_file.cpp +++ b/stream/clients/audiere_file.cpp @@ -8,7 +8,7 @@ bool AudiereFile::seek(int pos, SeekMode mode) assert(inp->isSeekable); assert(inp->hasPosition); - int newPos; + size_t newPos; switch(mode) { From 86a811c736810ef156fd2788be74951c5416ca03 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 12 Aug 2010 16:50:20 +0200 Subject: [PATCH 086/269] Another minor fix --- sound/clients/ogre_output_updater.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/clients/ogre_output_updater.hpp b/sound/clients/ogre_output_updater.hpp index 5072e9f1e8..b73168c759 100644 --- a/sound/clients/ogre_output_updater.hpp +++ b/sound/clients/ogre_output_updater.hpp @@ -14,15 +14,15 @@ namespace Sound { struct OgreOutputUpdater : Ogre::FrameListener { - Mangle::Sound::SoundFactory &driver; + Mangle::Sound::SoundFactoryPtr driver; - OgreOutputUpdater(Mangle::Sound::SoundFactory &drv) + OgreOutputUpdater(Mangle::Sound::SoundFactoryPtr drv) : driver(drv) - { assert(drv.needsUpdate); } + { assert(drv->needsUpdate); } bool frameStarted(const Ogre::FrameEvent &evt) { - driver.update(); + driver->update(); return true; } }; From f3fa05ba2f596c21611dccc9d4b0b21c6a133792 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 14:11:05 +0200 Subject: [PATCH 087/269] Added a couple more OpenAL error checks --- sound/outputs/openal_out.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index c95c02ff6c..acefb260c1 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -11,11 +11,23 @@ using namespace Mangle::Sound; static void fail(const std::string &msg) { throw str_exception("OpenAL exception: " + msg); } -static void checkALError(const std::string &msg) +/* + Check for AL error. Since we're always calling this with string + literals, and it only makes sense to optimize for the non-error + case, the parameter is const char* rather than std::string. + + This way we don't force the compiler to create a string object each + time we're called (since the string is never used unless there's an + error), although a good compiler might have optimized that away in + any case. + */ +static void checkALError(const char *where) { ALenum err = alGetError(); if(err != AL_NO_ERROR) { + std::string msg = where; + const ALchar* errmsg = alGetString(err); if(errmsg) fail("\"" + std::string(alGetString(err)) + "\" while " + msg); @@ -154,7 +166,9 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) // Create a source alGenSources(1, &inst); + checkALError("creating instance (clone)"); alSourcei(inst, AL_BUFFER, bufferID); + checkALError("assigning buffer (clone)"); } // Constructor used for original (non-cloned) sounds @@ -166,6 +180,7 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) // Set up the OpenAL buffer alGenBuffers(1, &bufferID); + checkALError("generating buffer"); assert(bufferID != 0); // Does the stream support pointer operations? @@ -187,7 +202,9 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) // Create a source alGenSources(1, &inst); + checkALError("creating source"); alSourcei(inst, AL_BUFFER, bufferID); + checkALError("assigning buffer"); // Create a cheap reference counter for the buffer refCnt = new int; @@ -208,6 +225,7 @@ OpenAL_Sound::~OpenAL_Sound() // We're the last owner. Delete the buffer and the counter // itself. alDeleteBuffers(1, &bufferID); + checkALError("deleting buffer"); delete refCnt; } } From f95ea1677cc5c52790e1342aa6d3d6bba950b8be Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 14:43:13 +0200 Subject: [PATCH 088/269] Compiler warning fix --- sound/sources/stream_source.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sources/stream_source.hpp b/sound/sources/stream_source.hpp index e4863545e9..43c605a004 100644 --- a/sound/sources/stream_source.hpp +++ b/sound/sources/stream_source.hpp @@ -9,8 +9,8 @@ namespace Sound { /// A class for reading raw samples directly from a stream. class Stream2Samples : public SampleSource { - int32_t rate, channels, bits; Mangle::Stream::StreamPtr inp; + int32_t rate, channels, bits; public: Stream2Samples(Mangle::Stream::StreamPtr _inp, int32_t _rate, int32_t _channels, int32_t _bits) From 1ba1998223ad88ce89018e7b72adc7c0560bf2d6 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 16:12:57 +0200 Subject: [PATCH 089/269] Added niceified openal+ffmpeg handler, with test --- sound/filters/openal_audiere.hpp | 4 +- sound/filters/openal_ffmpeg.hpp | 23 ++++++++++ sound/tests/Makefile | 5 ++- sound/tests/openal_ffmpeg_test.cpp | 52 +++++++++++++++++++++++ sound/tests/output/openal_ffmpeg_test.out | 2 + 5 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 sound/filters/openal_ffmpeg.hpp create mode 100644 sound/tests/openal_ffmpeg_test.cpp create mode 100644 sound/tests/output/openal_ffmpeg_test.out diff --git a/sound/filters/openal_audiere.hpp b/sound/filters/openal_audiere.hpp index 5b62bd11b1..5b9b518249 100644 --- a/sound/filters/openal_audiere.hpp +++ b/sound/filters/openal_audiere.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_FFMPEG_OPENAL_H -#define MANGLE_FFMPEG_OPENAL_H +#ifndef MANGLE_AUDIERE_OPENAL_H +#define MANGLE_AUDIERE_OPENAL_H #include "input_filter.hpp" #include "../sources/audiere_source.hpp" diff --git a/sound/filters/openal_ffmpeg.hpp b/sound/filters/openal_ffmpeg.hpp new file mode 100644 index 0000000000..42c76af0cd --- /dev/null +++ b/sound/filters/openal_ffmpeg.hpp @@ -0,0 +1,23 @@ +#ifndef MANGLE_FFMPEG_OPENAL_H +#define MANGLE_FFMPEG_OPENAL_H + +#include "input_filter.hpp" +#include "../sources/ffmpeg_source.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds ffmpeg decoding to OpenAL. +class OpenAL_FFMpeg_Factory : public InputFilter +{ + public: + OpenAL_FFMpeg_Factory() + { + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new FFMpegLoader)); + } +}; + +}} +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 79c2ecaf81..a23f18025f 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) L_OPENAL=$(shell pkg-config --libs openal) @@ -9,6 +9,9 @@ L_AUDIERE=-laudiere openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) +openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) + openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) diff --git a/sound/tests/openal_ffmpeg_test.cpp b/sound/tests/openal_ffmpeg_test.cpp new file mode 100644 index 0000000000..d4b8e93003 --- /dev/null +++ b/sound/tests/openal_ffmpeg_test.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_ffmpeg.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_FFMpeg_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("owl.ogg"); + play("cow.wav", true); + return 0; +} diff --git a/sound/tests/output/openal_ffmpeg_test.out b/sound/tests/output/openal_ffmpeg_test.out new file mode 100644 index 0000000000..96e1db0f9a --- /dev/null +++ b/sound/tests/output/openal_ffmpeg_test.out @@ -0,0 +1,2 @@ +Playing cow.wav +Playing owl.ogg From 160e8655d2503ad93992200d6d131186ede22aa3 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 16:52:29 +0200 Subject: [PATCH 090/269] Even more -Wall fixes - and added -Wall to test makefiles to actually catch them... --- input/tests/Makefile | 2 +- rend2d/tests/Makefile | 2 +- sound/sources/ffmpeg_source.cpp | 9 +++++---- sound/sources/ffmpeg_source.hpp | 2 +- sound/tests/Makefile | 2 +- stream/tests/Makefile | 2 +- vfs/clients/ogre_archive.hpp | 4 +++- vfs/tests/Makefile | 2 +- vfs/tests/dummy_test.cpp | 2 +- 9 files changed, 15 insertions(+), 12 deletions(-) diff --git a/input/tests/Makefile b/input/tests/Makefile index 8b7663dd57..8760adfe73 100644 --- a/input/tests/Makefile +++ b/input/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ +GCC=g++ -Wall all: sdl_driver_test ois_driver_test evtlist_test diff --git a/rend2d/tests/Makefile b/rend2d/tests/Makefile index c2e16c7565..efbbda5d67 100644 --- a/rend2d/tests/Makefile +++ b/rend2d/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ +GCC=g++ -Wall all: sdl_test diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index bfb4282346..2633515d82 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -97,8 +97,8 @@ size_t FFMpegSource::read(void *data, size_t length) // First, copy over any stored data we might be sitting on { - int s = storage.size(); - int copy = s; + size_t s = storage.size(); + size_t copy = s; if(s) { // Make sure there's room @@ -111,6 +111,7 @@ size_t FFMpegSource::read(void *data, size_t length) left -= copy; // Is there anything left in the storage? + assert(s>= copy); s -= copy; if(s) { @@ -133,7 +134,7 @@ size_t FFMpegSource::read(void *data, size_t length) break; // We only allow one stream per file at the moment - assert(StreamNum == packet.stream_index); + assert((int)StreamNum == packet.stream_index); // Decode the packet int len = AVCODEC_MAX_AUDIO_FRAME_SIZE; @@ -151,7 +152,7 @@ size_t FFMpegSource::read(void *data, size_t length) if(len > 0) { // copy = how many bytes do we copy now - int copy = len; + size_t copy = len; if(copy > left) copy = left; diff --git a/sound/sources/ffmpeg_source.hpp b/sound/sources/ffmpeg_source.hpp index f0b5342acc..5bb478d741 100644 --- a/sound/sources/ffmpeg_source.hpp +++ b/sound/sources/ffmpeg_source.hpp @@ -18,7 +18,7 @@ class FFMpegSource : public SampleSource { AVFormatContext *FmtCtx; AVCodecContext *CodecCtx; - int StreamNum; + unsigned int StreamNum; std::vector storage; diff --git a/sound/tests/Makefile b/sound/tests/Makefile index a23f18025f..cb647db735 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ +GCC=g++ -I../ -Wall all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test diff --git a/stream/tests/Makefile b/stream/tests/Makefile index 924a252c35..e64b900762 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ +GCC=g++ -I../ -Wall all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index 77191881a9..7cba6cfc8e 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -22,7 +22,9 @@ class MangleArchive : public Ogre::Archive public: MangleArchive(VFSPtr _vfs, const std::string &name, const std::string &archType = "Mangle") - : vfs(_vfs), Ogre::Archive(name, archType) {} + : Ogre::Archive(name, archType) + , vfs(_vfs) + {} bool isCaseSensitive() const { return vfs->isCaseSensitive; } diff --git a/vfs/tests/Makefile b/vfs/tests/Makefile index e0577b64ee..a5d1d17120 100644 --- a/vfs/tests/Makefile +++ b/vfs/tests/Makefile @@ -1,4 +1,4 @@ -GCC=g++ -I../ +GCC=g++ -I../ -Wall all: dummy_test ogre_client_test ogre_resource_test ogre_server_test physfs_server_test diff --git a/vfs/tests/dummy_test.cpp b/vfs/tests/dummy_test.cpp index 8d7fc7dd63..541010c6a3 100644 --- a/vfs/tests/dummy_test.cpp +++ b/vfs/tests/dummy_test.cpp @@ -17,7 +17,7 @@ void print(FileInfoPtr inf) { print(*inf); } void print(FileInfoList &lst) { - for(int i=0; i Date: Fri, 13 Aug 2010 19:27:45 +0200 Subject: [PATCH 091/269] Attempting to work around ffmpeg's inconsistent include path nonsense. --- sound/sources/ffmpeg_source.hpp | 4 ++-- sound/tests/Makefile | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/sources/ffmpeg_source.hpp b/sound/sources/ffmpeg_source.hpp index 5bb478d741..d422b98090 100644 --- a/sound/sources/ffmpeg_source.hpp +++ b/sound/sources/ffmpeg_source.hpp @@ -7,8 +7,8 @@ extern "C" { -#include -#include +#include +#include } namespace Mangle { diff --git a/sound/tests/Makefile b/sound/tests/Makefile index cb647db735..39b17be189 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -3,6 +3,7 @@ GCC=g++ -I../ -Wall all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) +I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere @@ -10,7 +11,7 @@ openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../ou $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp - $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) + $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG) openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) @@ -19,7 +20,7 @@ audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../.. $(GCC) $^ -o $@ $(L_AUDIERE) ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp - $(GCC) $^ -o $@ $(L_FFMPEG) + $(GCC) $^ -o $@ $(L_FFMPEG) $(I_FFMPEG) clean: rm *_test From 5b8e8d6b48847cd99d14064e8c7a1115efdd4fed Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 21:36:05 +0200 Subject: [PATCH 092/269] Added mpg123 sound source --- sound/filters/openal_mpg123.hpp | 24 ++++++ sound/sources/mpg123_source.cpp | 121 +++++++++++++++++++++++++++++ sound/sources/mpg123_source.hpp | 47 +++++++++++ sound/tests/Makefile | 5 +- sound/tests/openal_mpg123_test.cpp | 53 +++++++++++++ 5 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 sound/filters/openal_mpg123.hpp create mode 100644 sound/sources/mpg123_source.cpp create mode 100644 sound/sources/mpg123_source.hpp create mode 100644 sound/tests/openal_mpg123_test.cpp diff --git a/sound/filters/openal_mpg123.hpp b/sound/filters/openal_mpg123.hpp new file mode 100644 index 0000000000..bfd926c0bb --- /dev/null +++ b/sound/filters/openal_mpg123.hpp @@ -0,0 +1,24 @@ +#ifndef MANGLE_MPG123_OPENAL_H +#define MANGLE_MPG123_OPENAL_H + +#include "input_filter.hpp" +#include "../sources/mpg123_source.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds mpg123 decoding to OpenAL. Only supports +/// MP3 files. +class OpenAL_Mpg123_Factory : public InputFilter +{ + public: + OpenAL_Mpg123_Factory() + { + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new Mpg123Loader)); + } +}; + +}} +#endif diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp new file mode 100644 index 0000000000..3bf56a4684 --- /dev/null +++ b/sound/sources/mpg123_source.cpp @@ -0,0 +1,121 @@ +#include "mpg123_source.hpp" + +#include "../../tools/str_exception.hpp" + +#include + +using namespace Mangle::Stream; + +/* + TODOs: + + - mpg123 impressively enough supports custom stream reading. Which + means we could (and SHOULD!) support reading from Mangle::Streams + as well. But I'll save it til I need it. + + An alternative way to do this is through feeding (which they also + support), but that's more messy. + + - the library also supports output, via various other sources, + including ALSO, OSS, PortAudio, PulseAudio and SDL. Using this + library as a pure output library (if that is possible) would be a + nice shortcut over using those libraries - OTOH it's another + dependency. But it also means we could scavenge the mpg123 source + for these parts if we want them. + + - we could implement seek(), tell() and size(), but they aren't + really necessary. Further more, since the returned size is only a + guess, it is not safe to rely on it. + */ + +static void fail(const std::string &msg) +{ throw str_exception("Mangle::Mpg123 exception: " + msg); } + +static void checkError(int err, void *mh = NULL) +{ + if(err != MPG123_OK) + { + std::string msg; + if(mh) msg = mpg123_strerror((mpg123_handle*)mh); + else msg = mpg123_plain_strerror(err); + fail(msg); + } +} + +using namespace Mangle::Sound; + +void Mpg123Source::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits) +{ + // Use the values we found in the constructor + *pRate = rate; + *pChannels = channels; + *pBits = bits; +} + +size_t Mpg123Source::read(void *data, size_t length) +{ + size_t done; + // This is extraordinarily nice. I like this library. + int err = mpg123_read((mpg123_handle*)mh, (unsigned char*)data, length, &done); + assert(done <= length); + if(err == MPG123_DONE) + isEof = true; + else + checkError(err, mh); + return done; +} + +Mpg123Loader::Mpg123Loader(bool setup) +{ + // Do as we're told + if(setup) + { + int err = mpg123_init(); + checkError(err); + } + didSetup = setup; +} + +Mpg123Loader::~Mpg123Loader() +{ + // Deinitialize the library on exit + if(didSetup) + mpg123_exit(); +} + +Mpg123Source::Mpg123Source(const std::string &file) +{ + int err; + + // Create a new handle + mh = mpg123_new(NULL, &err); + if(mh == NULL) + checkError(err, mh); + + mpg123_handle *mhh = (mpg123_handle*)mh; + + // Open the file + err = mpg123_open(mhh, file.c_str()); + checkError(err, mh); + + // Get the format + int encoding; + err = mpg123_getformat(mhh, &rate, &channels, &encoding); + checkError(err, mh); + if(encoding != MPG123_ENC_SIGNED_16) + fail("Bad encoding"); + + // This is the only bit size we support. + bits = 16; + + // Ensure the output format does not change. (The tutorial on the + // mpg123 site did this, I assume it's kosher.) + mpg123_format_none(mhh); + mpg123_format(mhh,rate,channels,encoding); +} + +Mpg123Source::~Mpg123Source() +{ + mpg123_close((mpg123_handle*)mh); + mpg123_delete((mpg123_handle*)mh); +} diff --git a/sound/sources/mpg123_source.hpp b/sound/sources/mpg123_source.hpp new file mode 100644 index 0000000000..1ac16b5306 --- /dev/null +++ b/sound/sources/mpg123_source.hpp @@ -0,0 +1,47 @@ +#ifndef MANGLE_SOUND_MPG123_SOURCE_H +#define MANGLE_SOUND_MPG123_SOURCE_H + +#include "../source.hpp" +#include + +namespace Mangle { +namespace Sound { + +/// A sample source that decodes files using libmpg123. Only supports +/// MP3 files. +class Mpg123Source : public SampleSource +{ + void *mh; + long int rate; + int channels, bits; + + public: + /// Decode the given sound file + Mpg123Source(const std::string &file); + + /// Needed by SSL_Template but not yet supported + Mpg123Source(Mangle::Stream::StreamPtr data) + { assert(0); } + + ~Mpg123Source(); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); +}; + +#include "loadertemplate.hpp" + +/// A factory that loads Mpg123Sources from file and stream +struct Mpg123Loader : SSL_Template +{ + /** Sets up libmpg123 for you, and closes it on destruction. If you + want to do this yourself, send setup=false. + */ + Mpg123Loader(bool setup=true); + ~Mpg123Loader(); +private: + bool didSetup; +}; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 39b17be189..1c6c1fda5d 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -Wall -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat @@ -13,6 +13,9 @@ openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../ou openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_FFMPEG) $(L_OPENAL) $(I_FFMPEG) +openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ -lmpg123 ${L_OPENAL} + openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) diff --git a/sound/tests/openal_mpg123_test.cpp b/sound/tests/openal_mpg123_test.cpp new file mode 100644 index 0000000000..a2826b7551 --- /dev/null +++ b/sound/tests/openal_mpg123_test.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_mpg123.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_Mpg123_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main(int argc, char**argv) +{ + if(argc != 2) + cout << "Please specify an MP3 file\n"; + else + play(argv[1]); + return 0; +} From 85fa6d392319e72cbe359c55c7651faafcebdd2b Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 13 Aug 2010 22:59:56 +0200 Subject: [PATCH 093/269] Constness fix (or rather, hack) --- sound/sources/mpg123_source.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 3bf56a4684..26e3fd7f47 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -94,8 +94,8 @@ Mpg123Source::Mpg123Source(const std::string &file) mpg123_handle *mhh = (mpg123_handle*)mh; - // Open the file - err = mpg123_open(mhh, file.c_str()); + // Open the file (hack around constness) + err = mpg123_open(mhh, (char*)file.c_str()); checkError(err, mh); // Get the format From 49f0e4b75f375c75724b650ba86a2ff840970c71 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 14 Aug 2010 13:02:41 +0200 Subject: [PATCH 094/269] Added setPitch to Sound --- sound/filters/pure_filter.hpp | 1 + sound/output.hpp | 3 +++ sound/outputs/openal_out.cpp | 6 ++++++ sound/outputs/openal_out.hpp | 1 + 4 files changed, 11 insertions(+) diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp index ffa49d35ab..9d19ffd18e 100644 --- a/sound/filters/pure_filter.hpp +++ b/sound/filters/pure_filter.hpp @@ -23,6 +23,7 @@ namespace Mangle void setPan(float f) { client->setPan(f); } void setPos(float x, float y, float z) { client->setPos(x,y,z); } + void setPitch(float p) { client->setPitch(p); } void setRepeat(bool b) { client->setRepeat(b); } void setStreaming(bool b) { client->setStreaming(b); } diff --git a/sound/output.hpp b/sound/output.hpp index 404dcf4f8d..35182e58cc 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -48,6 +48,9 @@ class Sound /// Set left/right pan. -1.0 is left, 0.0 is center and 1.0 is right. virtual void setPan(float) = 0; + /// Set pitch (1.0 is normal speed) + virtual void setPitch(float) = 0; + /// Set the position. May not work with all backends. virtual void setPos(float x, float y, float z) = 0; diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index acefb260c1..363097c3e8 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -146,6 +146,12 @@ void OpenAL_Sound::setPos(float x, float y, float z) checkALError("setting position"); } +void OpenAL_Sound::setPitch(float pitch) +{ + alSourcef(inst, AL_PITCH, pitch); + checkALError("setting pitch"); +} + void OpenAL_Sound::setRepeat(bool rep) { alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE); diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index 18f32f2a9c..084128e89b 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -36,6 +36,7 @@ class OpenAL_Sound : public Sound bool isPlaying() const; void setVolume(float); void setPos(float x, float y, float z); + void setPitch(float); void setRepeat(bool); void setStreaming(bool) {} // Not implemented yet SoundPtr clone() const; From 3db61c8bdde65910e43a3a06b34738296960e9e8 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sat, 14 Aug 2010 18:50:42 +0200 Subject: [PATCH 095/269] Added sound range function --- sound/filters/pure_filter.hpp | 2 ++ sound/output.hpp | 4 ++++ sound/outputs/openal_out.cpp | 7 +++++++ sound/outputs/openal_out.hpp | 5 +++++ sound/tests/output/openal_mpg123_test.out | 1 + 5 files changed, 19 insertions(+) create mode 100644 sound/tests/output/openal_mpg123_test.out diff --git a/sound/filters/pure_filter.hpp b/sound/filters/pure_filter.hpp index 9d19ffd18e..1e8b9f92c2 100644 --- a/sound/filters/pure_filter.hpp +++ b/sound/filters/pure_filter.hpp @@ -25,6 +25,8 @@ namespace Mangle { client->setPos(x,y,z); } void setPitch(float p) { client->setPitch(p); } void setRepeat(bool b) { client->setRepeat(b); } + void setRange(float a, float b=0, float c=0) + { client->setRange(a,b,c); } void setStreaming(bool b) { client->setStreaming(b); } // The clone() function is not implemented here, as you will diff --git a/sound/output.hpp b/sound/output.hpp index 35182e58cc..596e08c587 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -51,6 +51,10 @@ class Sound /// Set pitch (1.0 is normal speed) virtual void setPitch(float) = 0; + /// Set range factors for 3D sounds. The meaning of the fields + /// depend on implementation. + virtual void setRange(float a, float b=0.0, float c=0.0) = 0; + /// Set the position. May not work with all backends. virtual void setPos(float x, float y, float z) = 0; diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 363097c3e8..f0d5b1c0e4 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -140,6 +140,13 @@ void OpenAL_Sound::setVolume(float volume) checkALError("setting volume"); } +void OpenAL_Sound::setRange(float a, float b, float) +{ + alSourcef(inst, AL_REFERENCE_DISTANCE, a); + alSourcef(inst, AL_MAX_DISTANCE, b); + checkALError("setting sound ranges"); +} + void OpenAL_Sound::setPos(float x, float y, float z) { alSource3f(inst, AL_POSITION, x, y, z); diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index 084128e89b..c22be0de74 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -41,6 +41,11 @@ class OpenAL_Sound : public Sound void setStreaming(bool) {} // Not implemented yet SoundPtr clone() const; + // a = AL_REFERENCE_DISTANCE + // b = AL_MAX_DISTANCE + // c = ignored + void setRange(float a, float b=0.0, float c=0.0); + /// Not implemented void setPan(float) {} }; diff --git a/sound/tests/output/openal_mpg123_test.out b/sound/tests/output/openal_mpg123_test.out new file mode 100644 index 0000000000..e55dabbb1d --- /dev/null +++ b/sound/tests/output/openal_mpg123_test.out @@ -0,0 +1 @@ +Please specify an MP3 file From a69938364fdbabee5c2c56248906a59225e43cff Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 14:13:13 +0200 Subject: [PATCH 096/269] Rewrote audiere to use new sample_reader --- sound/sources/audiere_source.cpp | 77 +++----------------------------- sound/sources/audiere_source.hpp | 21 +++------ sound/sources/sample_reader.cpp | 75 +++++++++++++++++++++++++++++++ sound/sources/sample_reader.hpp | 42 +++++++++++++++++ sound/tests/Makefile | 4 +- 5 files changed, 130 insertions(+), 89 deletions(-) create mode 100644 sound/sources/sample_reader.cpp create mode 100644 sound/sources/sample_reader.hpp diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 6fde40c075..1f11272b0c 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -27,65 +27,6 @@ void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) } } -/* - Get data. Since Audiere operates with frames, not bytes, there's a - little conversion magic going on here. We need to make sure we're - reading a whole number of frames - if not, we need to store the - remainding part of the last frame and remember it for the next read - operation. - */ -size_t AudiereSource::read(void *_data, size_t length) -{ - if(isEof) return 0; - - char *data = (char*)_data; - - // Move the remains from the last operation first - if(pullSize) - { - // pullSize is how much was stored the last time, so skip that. - memcpy(data, pullOver+pullSize, PSIZE-pullSize); - length -= pullSize; - data += pullSize; - } - - // Determine the overshoot up front - pullSize = length % frameSize; - - // Number of whole frames - int frames = length / frameSize; - - // Read the data - int res = sample->read(frames, data); - - if(res < frames) - isEof = true; - - // Are we missing data? If we're at the end of the stream, then this - // doesn't apply. - if(!isEof && pullSize) - { - // Read one more sample - if(sample->read(1, pullOver) != 0) - { - // Then, move as much of it as we can fit into the output - // data - memcpy(data+length-pullSize, pullOver, pullSize); - } - else - // Failed reading, we're out of data - isEof = true; - } - - // If we're at the end of the stream, then no data remains to be - // pulled over - if(isEof) - pullSize = 0; - - // Return the total number of bytes stored - return frameSize*res + pullSize; -} - // --- Constructors --- AudiereSource::AudiereSource(const std::string &file) @@ -95,7 +36,7 @@ AudiereSource::AudiereSource(const std::string &file) if(!sample) fail("Couldn't load file " + file); - setup(); + doSetup(); } AudiereSource::AudiereSource(StreamPtr input) @@ -106,15 +47,15 @@ AudiereSource::AudiereSource(StreamPtr input) if(!sample) fail("Couldn't load stream"); - setup(); + doSetup(); } AudiereSource::AudiereSource(audiere::SampleSourcePtr src) : sample(src) -{ assert(sample); setup(); } +{ assert(sample); doSetup(); } // Common function called from all constructors -void AudiereSource::setup() +void AudiereSource::doSetup() { assert(sample); @@ -122,14 +63,8 @@ void AudiereSource::setup() int channels, rate; sample->getFormat(channels, rate, fmt); - pullSize = 0; - - // Calculate the size of one frame - frameSize = GetSampleSize(fmt) * channels; - - // Make sure that our pullover hack will work. Increase this size if - // this doesn't work in all cases. - assert(frameSize <= PSIZE); + // Calculate the size of one frame, and pass it to SampleReader. + setup(GetSampleSize(fmt) * channels); isSeekable = sample->isSeekable(); hasPosition = true; diff --git a/sound/sources/audiere_source.hpp b/sound/sources/audiere_source.hpp index 476546af3a..d0189b710e 100644 --- a/sound/sources/audiere_source.hpp +++ b/sound/sources/audiere_source.hpp @@ -1,7 +1,7 @@ #ifndef MANGLE_SOUND_AUDIERE_SOURCE_H #define MANGLE_SOUND_AUDIERE_SOURCE_H -#include "../source.hpp" +#include "sample_reader.hpp" #include @@ -9,24 +9,14 @@ namespace Mangle { namespace Sound { /// A sample source that decodes files using Audiere -class AudiereSource : public SampleSource +class AudiereSource : public SampleReader { audiere::SampleSourcePtr sample; - // Number of bytes we cache between reads. This should correspond to - // the maximum possible value of frameSize. - static const int PSIZE = 10; + size_t readSamples(void *data, size_t length) + { return sample->read(length, data); } - // Size of one frame, in bytes - int frameSize; - - // Temporary storage for unevenly read samples. See the comment for - // read() in the .cpp file. - char pullOver[PSIZE]; - // How much of the above buffer is in use - int pullSize; - - void setup(); + void doSetup(); public: /// Decode the given sound file @@ -39,7 +29,6 @@ class AudiereSource : public SampleSource AudiereSource(audiere::SampleSourcePtr src); void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); - size_t read(void *data, size_t length); void seek(size_t pos) { sample->setPosition(pos/frameSize); } size_t tell() const { return sample->getPosition()*frameSize; } diff --git a/sound/sources/sample_reader.cpp b/sound/sources/sample_reader.cpp new file mode 100644 index 0000000000..7cf5b0e00e --- /dev/null +++ b/sound/sources/sample_reader.cpp @@ -0,0 +1,75 @@ +#include "sample_reader.hpp" + +#include + +using namespace Mangle::Sound; + +void SampleReader::setup(int size) +{ + pullSize = 0; + frameSize = size; + pullOver = new char[size]; +} + +SampleReader::~SampleReader() +{ + if(pullOver) + delete[] pullOver; +} + +size_t SampleReader::read(void *_data, size_t length) +{ + if(isEof) return 0; + char *data = (char*)_data; + + // Move the remains from the last operation first + if(pullSize) + { + // pullSize is how much was stored the last time. The data is + // stored at the end of the buffer. + memcpy(data, pullOver+(frameSize-pullSize), pullSize); + length -= pullSize; + data += pullSize; + pullSize = 0; + } + + // Number of whole frames + size_t frames = length / frameSize; + + // Read the data + size_t res = readSamples(data, frames); + + // Total bytes read + size_t num = res*frameSize; + data += num; + + if(res < frames) + { + // End of stream. + isEof = true; + // Determine how much we read + return data-(char*)_data; + } + + // Determine the overshoot + pullSize = length - num; + + // Are we missing data? + if(pullSize) + { + // Fill in one sample + res = readSamples(pullOver,1); + if(res) + { + // Move as much as we can into the output buffer + memcpy(data, pullOver, pullSize); + data += pullSize; + } + else + // Failed reading, we're out of data + isEof = true; + } + + // Return the total number of bytes stored + return data-(char*)_data; +} diff --git a/sound/sources/sample_reader.hpp b/sound/sources/sample_reader.hpp new file mode 100644 index 0000000000..16b98bee39 --- /dev/null +++ b/sound/sources/sample_reader.hpp @@ -0,0 +1,42 @@ +#ifndef MANGLE_SOUND_SAMPLE_READER_H +#define MANGLE_SOUND_SAMPLE_READER_H + +#include "../source.hpp" + +namespace Mangle { +namespace Sound { + + /* This is a helper base class for other SampleSource + implementations. Certain sources (like Audiere and libsndfile) + insist on reading whole samples rather than bytes. This class + compensates for that, and allows you to read bytes rather than + samples. + */ +class SampleReader : public SampleSource +{ + // Pullover buffer + char* pullOver; + + // How much of the above buffer is in use. + int pullSize; + +protected: + // Size of one frame, in bytes. This is also the size of the + // pullOver buffer. + int frameSize; + + // MUST be called by base class constructor. The parameter gives the + // size of one sample/frame, in bytes. + void setup(int); + + // Read the given number of samples, in multiples of frameSize. Does + // not have to set or respect isEof. + virtual size_t readSamples(void *data, size_t num) = 0; + + public: + SampleReader() : pullOver(NULL) {} + ~SampleReader(); + size_t read(void *data, size_t length); +}; +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 1c6c1fda5d..0ad946fa9f 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -7,7 +7,7 @@ I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere -openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp +openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outputs/openal_out.cpp @@ -19,7 +19,7 @@ openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outpu openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) -audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp +audiere_source_test: audiere_source_test.cpp ../sources/audiere_source.cpp ../../stream/clients/audiere_file.cpp ../sources/sample_reader.cpp $(GCC) $^ -o $@ $(L_AUDIERE) ffmpeg_source_test: ffmpeg_source_test.cpp ../sources/ffmpeg_source.cpp From ddfbcecfcd08b8739d0c356268242490935cec24 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 14:42:23 +0200 Subject: [PATCH 097/269] Added support for libsndfile (sound input) --- sound/filters/openal_sndfile.hpp | 24 ++++++++++ sound/sources/libsndfile.cpp | 50 +++++++++++++++++++++ sound/sources/libsndfile.hpp | 36 +++++++++++++++ sound/tests/Makefile | 5 ++- sound/tests/openal_sndfile_test.cpp | 52 ++++++++++++++++++++++ sound/tests/output/openal_sndfile_test.out | 2 + 6 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 sound/filters/openal_sndfile.hpp create mode 100644 sound/sources/libsndfile.cpp create mode 100644 sound/sources/libsndfile.hpp create mode 100644 sound/tests/openal_sndfile_test.cpp create mode 100644 sound/tests/output/openal_sndfile_test.out diff --git a/sound/filters/openal_sndfile.hpp b/sound/filters/openal_sndfile.hpp new file mode 100644 index 0000000000..fd7e780259 --- /dev/null +++ b/sound/filters/openal_sndfile.hpp @@ -0,0 +1,24 @@ +#ifndef MANGLE_SNDFILE_OPENAL_H +#define MANGLE_SNDFILE_OPENAL_H + +#include "input_filter.hpp" +#include "../sources/libsndfile.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that adds libsnd decoding to OpenAL. libsndfile +/// supports most formats except MP3. +class OpenAL_SndFile_Factory : public InputFilter +{ + public: + OpenAL_SndFile_Factory() + { + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(new SndFileLoader)); + } +}; + +}} +#endif diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp new file mode 100644 index 0000000000..502d8e5d1c --- /dev/null +++ b/sound/sources/libsndfile.cpp @@ -0,0 +1,50 @@ +#include "libsndfile.hpp" + +#include "../../tools/str_exception.hpp" +#include + +using namespace Mangle::Stream; + +static void fail(const std::string &msg) +{ throw str_exception("Mangle::libsndfile: " + msg); } + +using namespace Mangle::Sound; + +void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) +{ + *_rate = rate; + *_channels = channels; + *_bits = bits; +} + +size_t SndFileSource::readSamples(void *data, size_t length) +{ + // Read frames. We count channels as part of the frame. Even though + // libsndfile does not, since it still requires the number of frames + // read to be a multiple of channels. + return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels); +} + +SndFileSource::SndFileSource(const std::string &file) +{ + SF_INFO info; + info.format = 0; + handle = sf_open(file.c_str(), SFM_READ, &info); + if(handle == NULL) + fail("Failed to open " + file); + + // I THINK that using sf_read_short forces the library to convert to + // 16 bits no matter what, but the libsndfile docs aren't exactly + // very clear on this point. + channels = info.channels; + rate = info.samplerate; + bits = 16; + + // 16 bits per sample times number of channels + setup(2*channels); +} + +SndFileSource::~SndFileSource() +{ + sf_close((SNDFILE*)handle); +} diff --git a/sound/sources/libsndfile.hpp b/sound/sources/libsndfile.hpp new file mode 100644 index 0000000000..7286cf0fe4 --- /dev/null +++ b/sound/sources/libsndfile.hpp @@ -0,0 +1,36 @@ +#ifndef MANGLE_SOUND_SNDFILE_SOURCE_H +#define MANGLE_SOUND_SNDFILE_SOURCE_H + +#include "sample_reader.hpp" + +namespace Mangle { +namespace Sound { + +/// A sample source that decodes files using libsndfile. Supports most +/// formats except mp3. +class SndFileSource : public SampleReader +{ + void *handle; + int channels, rate, bits; + + size_t readSamples(void *data, size_t length); + + public: + /// Decode the given sound file + SndFileSource(const std::string &file); + + /// Decode the given sound stream (not supported) + SndFileSource(Mangle::Stream::StreamPtr src) { assert(0); } + + ~SndFileSource(); + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); +}; + +#include "loadertemplate.hpp" + +/// A factory that loads SndFileSources from file and stream +typedef SSL_Template SndFileLoader; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 0ad946fa9f..365e4a05cf 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,6 +1,6 @@ GCC=g++ -I../ -Wall -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat @@ -16,6 +16,9 @@ openal_ffmpeg_test: openal_ffmpeg_test.cpp ../sources/ffmpeg_source.cpp ../outpu openal_mpg123_test: openal_mpg123_test.cpp ../sources/mpg123_source.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ -lmpg123 ${L_OPENAL} +openal_sndfile_test: openal_sndfile_test.cpp ../sources/libsndfile.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ -lsndfile ${L_OPENAL} + openal_output_test: openal_output_test.cpp ../outputs/openal_out.cpp $(GCC) $^ -o $@ $(L_OPENAL) diff --git a/sound/tests/openal_sndfile_test.cpp b/sound/tests/openal_sndfile_test.cpp new file mode 100644 index 0000000000..bd5f117a59 --- /dev/null +++ b/sound/tests/openal_sndfile_test.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_sndfile.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_SndFile_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("owl.ogg"); + play("cow.wav", true); + return 0; +} diff --git a/sound/tests/output/openal_sndfile_test.out b/sound/tests/output/openal_sndfile_test.out new file mode 100644 index 0000000000..96e1db0f9a --- /dev/null +++ b/sound/tests/output/openal_sndfile_test.out @@ -0,0 +1,2 @@ +Playing cow.wav +Playing owl.ogg From b0ded6a3186ba541be0c759a27afe9a55e296b1f Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 16:26:24 +0200 Subject: [PATCH 098/269] Minor changes to sample_reader --- sound/sources/sample_reader.hpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sound/sources/sample_reader.hpp b/sound/sources/sample_reader.hpp index 16b98bee39..1cf9f5f681 100644 --- a/sound/sources/sample_reader.hpp +++ b/sound/sources/sample_reader.hpp @@ -11,22 +11,28 @@ namespace Sound { insist on reading whole samples rather than bytes. This class compensates for that, and allows you to read bytes rather than samples. + + There are two ways for subclasses to use this class. EITHER call + setup() with the size of frameSize. This will allocate a buffer, + which the destructor frees. OR set frameSize manually and + manipulate the pullOver pointer yourself. In that case you MUST + reset it to NULL if you don't want the destructor to call + delete[] on it. */ class SampleReader : public SampleSource { - // Pullover buffer - char* pullOver; - // How much of the above buffer is in use. int pullSize; protected: + // Pullover buffer + char* pullOver; + // Size of one frame, in bytes. This is also the size of the // pullOver buffer. int frameSize; - // MUST be called by base class constructor. The parameter gives the - // size of one sample/frame, in bytes. + // The parameter gives the size of one sample/frame, in bytes. void setup(int); // Read the given number of samples, in multiples of frameSize. Does @@ -34,7 +40,7 @@ protected: virtual size_t readSamples(void *data, size_t num) = 0; public: - SampleReader() : pullOver(NULL) {} + SampleReader() : pullOver(NULL), pullSize(0) {} ~SampleReader(); size_t read(void *data, size_t length); }; From 56ecc6585b2a362d90993f65d4d40507cc0ec40f Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 16:52:33 +0200 Subject: [PATCH 099/269] Added a manager for sndfile + mpg123, sorting on file type. --- sound/filters/openal_sndfile_mpg123.hpp | 33 +++++++++ sound/filters/source_splicer.hpp | 90 +++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 sound/filters/openal_sndfile_mpg123.hpp create mode 100644 sound/filters/source_splicer.hpp diff --git a/sound/filters/openal_sndfile_mpg123.hpp b/sound/filters/openal_sndfile_mpg123.hpp new file mode 100644 index 0000000000..02600ee947 --- /dev/null +++ b/sound/filters/openal_sndfile_mpg123.hpp @@ -0,0 +1,33 @@ +#ifndef MANGLE_MPG123_OPENAL_H +#define MANGLE_MPG123_OPENAL_H + +#include "input_filter.hpp" +#include "source_splicer.hpp" +#include "../sources/mpg123_source.hpp" +#include "../sources/libsndfile.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/// A InputFilter that uses OpenAL for output, and mpg123 (for MP3) + +/// libsndfile (for everything else) to decode files. Can only load +/// from the file system, and uses the file name to differentiate +/// between mp3 and non-mp3 types. +class OpenAL_SndFile_Mpg123_Factory : public InputFilter +{ + public: + OpenAL_SndFile_Mpg123_Factory() + { + SourceSplicer *splice = new SourceSplicer; + + splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader)); + splice->setDefault(SampleSourceLoaderPtr(new SndFileLoader)); + + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(splice)); + } +}; + +}} +#endif diff --git a/sound/filters/source_splicer.hpp b/sound/filters/source_splicer.hpp new file mode 100644 index 0000000000..69e8d024ab --- /dev/null +++ b/sound/filters/source_splicer.hpp @@ -0,0 +1,90 @@ +#ifndef MANGLE_SOUND_SOURCE_SPLICE_H +#define MANGLE_SOUND_SOURCE_SPLICE_H + +#include "../source.hpp" +#include "../../tools/str_exception.hpp" +#include +#include +#include + +namespace Mangle +{ + namespace Sound + { + class SourceSplicer : public SampleSourceLoader + { + struct SourceType + { + std::string type; + SampleSourceLoaderPtr loader; + }; + + typedef std::list TypeList; + TypeList list; + SampleSourceLoaderPtr catchAll; + + static bool isMatch(char a, char b) + { + if(a >= 'A' && a <= 'Z') + a += 'a' - 'A'; + if(b >= 'A' && b <= 'Z') + b += 'a' - 'A'; + return a == b; + } + + public: + SourceSplicer() + { + canLoadStream = false; + canLoadFile = true; + } + + void add(const std::string &type, SampleSourceLoaderPtr fact) + { + SourceType tp; + tp.type = type; + tp.loader = fact; + list.push_back(tp); + } + + void setDefault(SampleSourceLoaderPtr def) + { + catchAll = def; + } + + SampleSourcePtr load(const std::string &file) + { + // Search the list for this file type. + for(TypeList::iterator it = list.begin(); + it != list.end(); it++) + { + const std::string &t = it->type; + + int diff = file.size() - t.size(); + if(diff < 0) continue; + + bool match = true; + for(int i=0; iloader->load(file); + } + // If not found, use the catch-all + if(catchAll) + return catchAll->load(file); + + throw str_exception("No handler for sound file " + file); + } + + SampleSourcePtr load(Stream::StreamPtr input) { assert(0); } + }; + } +} + +#endif From a0aafb9b1fb2462833fed205ab490bd84f073580 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 17:01:11 +0200 Subject: [PATCH 100/269] bleh --- sound/sources/sample_reader.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/sources/sample_reader.hpp b/sound/sources/sample_reader.hpp index 1cf9f5f681..89ddf1f652 100644 --- a/sound/sources/sample_reader.hpp +++ b/sound/sources/sample_reader.hpp @@ -40,7 +40,7 @@ protected: virtual size_t readSamples(void *data, size_t num) = 0; public: - SampleReader() : pullOver(NULL), pullSize(0) {} + SampleReader() : pullSize(0), pullOver(NULL) {} ~SampleReader(); size_t read(void *data, size_t length); }; From e8f01ae108c4ccc424801e4bee29e7836c5d0d67 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 17:04:44 +0200 Subject: [PATCH 101/269] Fixed include guard --- sound/filters/openal_sndfile_mpg123.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/filters/openal_sndfile_mpg123.hpp b/sound/filters/openal_sndfile_mpg123.hpp index 02600ee947..6e5db4d0e1 100644 --- a/sound/filters/openal_sndfile_mpg123.hpp +++ b/sound/filters/openal_sndfile_mpg123.hpp @@ -1,5 +1,5 @@ -#ifndef MANGLE_MPG123_OPENAL_H -#define MANGLE_MPG123_OPENAL_H +#ifndef MANGLE_SNDFILE_MPG123_OPENAL_H +#define MANGLE_SNDFILE_MPG123_OPENAL_H #include "input_filter.hpp" #include "source_splicer.hpp" From c982f701cacdd2932bfdc22b168f54221a549b62 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 16 Aug 2010 17:07:27 +0200 Subject: [PATCH 102/269] Minor fix again --- sound/filters/source_splicer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/filters/source_splicer.hpp b/sound/filters/source_splicer.hpp index 69e8d024ab..9fd49d1260 100644 --- a/sound/filters/source_splicer.hpp +++ b/sound/filters/source_splicer.hpp @@ -64,7 +64,7 @@ namespace Mangle if(diff < 0) continue; bool match = true; - for(int i=0; i Date: Tue, 17 Aug 2010 11:25:26 +0200 Subject: [PATCH 103/269] Moved OpenAL_Sound to cpp file, added delayed buffer creation (prelude for streaming) --- sound/output.hpp | 2 +- sound/outputs/openal_out.cpp | 131 ++++++++++++++++++++++++++--- sound/outputs/openal_out.hpp | 66 ++------------- sound/sources/mpg123_source.cpp | 7 +- sound/tests/openal_output_test.cpp | 16 +++- stream/filters/buffer_stream.hpp | 9 +- 6 files changed, 145 insertions(+), 86 deletions(-) diff --git a/sound/output.hpp b/sound/output.hpp index 596e08c587..a9012b9584 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -72,7 +72,7 @@ class Sound /** Playback status is not cloned, only the sound data itself. Back-ends can use this as a means of sharing data and saving memory. */ - virtual SoundPtr clone() const = 0; + virtual SoundPtr clone() = 0; /// Virtual destructor virtual ~Sound() {} diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index f0d5b1c0e4..a49840f4e5 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -4,6 +4,9 @@ #include "../../stream/filters/buffer_stream.hpp" #include "../../tools/str_exception.hpp" +#include +#include + using namespace Mangle::Sound; // ---- Helper functions and classes ---- @@ -69,17 +72,91 @@ static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) fail("Unsupported input format"); } +/// OpenAL sound output +class OpenAL_Sound : public Sound +{ + ALuint inst; + ALuint bufferID; + + // Poor mans reference counting. Might improve this later. When + // NULL, the buffer has not been set up yet. + int *refCnt; + + bool streaming; + + // Input stream + SampleSourcePtr input; + + void setupBuffer(); + + public: + /// Read samples from the given input buffer + OpenAL_Sound(SampleSourcePtr input); + + /// Play an existing buffer, with a given ref counter. Used + /// internally for cloning. + OpenAL_Sound(ALuint buf, int *ref); + + ~OpenAL_Sound(); + + void play(); + void stop(); + void pause(); + bool isPlaying() const; + void setVolume(float); + void setPos(float x, float y, float z); + void setPitch(float); + void setRepeat(bool); + void setStreaming(bool s) { streaming = s; } + SoundPtr clone(); + + // a = AL_REFERENCE_DISTANCE + // b = AL_MAX_DISTANCE + // c = ignored + void setRange(float a, float b=0.0, float c=0.0); + + /// Not implemented + void setPan(float) {} +}; + // ---- OpenAL_Factory ---- -OpenAL_Factory::OpenAL_Factory(bool doSetup) - : didSetup(doSetup) +SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input) { - needsUpdate = false; + return SoundPtr(new OpenAL_Sound(input)); +} + +void OpenAL_Factory::update() +{ +} + +void OpenAL_Factory::setListenerPos(float x, float y, float z, + float fx, float fy, float fz, + float ux, float uy, float uz) +{ + ALfloat orient[6]; + orient[0] = fx; + orient[1] = fy; + orient[2] = fz; + orient[3] = ux; + orient[4] = uy; + orient[5] = uz; + alListener3f(AL_POSITION, x, y, z); + alListenerfv(AL_ORIENTATION, orient); +} + +OpenAL_Factory::OpenAL_Factory(bool doSetup) + : device(NULL), context(NULL), didSetup(doSetup) +{ + needsUpdate = true; has3D = true; canLoadFile = false; canLoadStream = false; canLoadSource = true; + ALCdevice *Device; + ALCcontext *Context; + if(doSetup) { // Set up sound system @@ -90,6 +167,9 @@ OpenAL_Factory::OpenAL_Factory(bool doSetup) fail("Failed to initialize context or device"); alcMakeContextCurrent(Context); + + device = Device; + context = Context; } } @@ -99,8 +179,8 @@ OpenAL_Factory::~OpenAL_Factory() if(didSetup) { alcMakeContextCurrent(NULL); - if(Context) alcDestroyContext(Context); - if(Device) alcCloseDevice(Device); + if(context) alcDestroyContext((ALCcontext*)context); + if(device) alcCloseDevice((ALCdevice*)device); } } @@ -108,6 +188,7 @@ OpenAL_Factory::~OpenAL_Factory() void OpenAL_Sound::play() { + setupBuffer(); alSourcePlay(inst); checkALError("starting playback"); } @@ -164,14 +245,16 @@ void OpenAL_Sound::setRepeat(bool rep) alSourcei(inst, AL_LOOPING, rep?AL_TRUE:AL_FALSE); } -SoundPtr OpenAL_Sound::clone() const +SoundPtr OpenAL_Sound::clone() { + setupBuffer(); + assert(!streaming && "cloning streamed sounds not supported"); return SoundPtr(new OpenAL_Sound(bufferID, refCnt)); } // Constructor used for cloned sounds OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) - : bufferID(buf), refCnt(ref) + : bufferID(buf), refCnt(ref), streaming(false) { // Increase the reference count assert(ref != NULL); @@ -185,12 +268,35 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) } // Constructor used for original (non-cloned) sounds -OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) +OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input) + : bufferID(0), refCnt(NULL), streaming(false), input(_input) { + // Create a source + alGenSources(1, &inst); + checkALError("creating source"); + + // By default, the sound starts out in a buffer-less mode. We don't + // create a buffer until the sound is played. This gives the user + // the chance to call setStreaming(true) first. +} + +void OpenAL_Sound::setupBuffer() +{ + if(refCnt != NULL) return; + + assert(input); + // Get the format int fmt, rate; getALFormat(input, fmt, rate); + if(streaming) + { + // To be done + } + + // NON-STREAMING + // Set up the OpenAL buffer alGenBuffers(1, &bufferID); checkALError("generating buffer"); @@ -205,17 +311,16 @@ OpenAL_Sound::OpenAL_Sound(SampleSourcePtr input) else { // Read the entire stream into a temporary buffer first - Mangle::Stream::BufferStream buf(input); + Mangle::Stream::BufferStream buf(input, streaming?1024*1024:32*1024); // Then copy that into OpenAL alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate); } - checkALError("loading sound buffer"); - // Create a source - alGenSources(1, &inst); - checkALError("creating source"); + // We're done with the input stream, release the pointer + input.reset(); + alSourcei(inst, AL_BUFFER, bufferID); checkALError("assigning buffer"); diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index c22be0de74..f3828ff1bb 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -3,57 +3,13 @@ #include "../output.hpp" -#include -#include -#include - namespace Mangle { namespace Sound { -/// OpenAL sound output -class OpenAL_Sound : public Sound -{ - protected: - ALuint inst; - ALuint bufferID; - - // Poor mans reference counting. Might improve this later. - int *refCnt; - - public: - /// Read samples from the given input buffer - OpenAL_Sound(SampleSourcePtr input); - - /// Play an existing buffer, with a given ref counter. Used - /// internally for cloning. - OpenAL_Sound(ALuint buf, int *ref); - - ~OpenAL_Sound(); - - void play(); - void stop(); - void pause(); - bool isPlaying() const; - void setVolume(float); - void setPos(float x, float y, float z); - void setPitch(float); - void setRepeat(bool); - void setStreaming(bool) {} // Not implemented yet - SoundPtr clone() const; - - // a = AL_REFERENCE_DISTANCE - // b = AL_MAX_DISTANCE - // c = ignored - void setRange(float a, float b=0.0, float c=0.0); - - /// Not implemented - void setPan(float) {} -}; - class OpenAL_Factory : public SoundFactory { - ALCdevice *Device; - ALCcontext *Context; + void *device; + void *context; bool didSetup; public: @@ -65,24 +21,12 @@ class OpenAL_Factory : public SoundFactory SoundPtr load(const std::string &file) { assert(0); } SoundPtr load(Stream::StreamPtr input) { assert(0); } - SoundPtr loadRaw(SampleSourcePtr input) - { return SoundPtr(new OpenAL_Sound(input)); } + SoundPtr loadRaw(SampleSourcePtr input); - void update() {} + void update(); void setListenerPos(float x, float y, float z, float fx, float fy, float fz, - float ux, float uy, float uz) - { - ALfloat orient[6]; - orient[0] = fx; - orient[1] = fy; - orient[2] = fz; - orient[3] = ux; - orient[4] = uy; - orient[5] = uz; - alListener3f(AL_POSITION, x, y, z); - alListenerfv(AL_ORIENTATION, orient); - } + float ux, float uy, float uz); }; }} // namespaces diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 26e3fd7f47..327279b85a 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -17,14 +17,13 @@ using namespace Mangle::Stream; support), but that's more messy. - the library also supports output, via various other sources, - including ALSO, OSS, PortAudio, PulseAudio and SDL. Using this + including ALSA, OSS, PortAudio, PulseAudio and SDL. Using this library as a pure output library (if that is possible) would be a nice shortcut over using those libraries - OTOH it's another - dependency. But it also means we could scavenge the mpg123 source - for these parts if we want them. + dependency. - we could implement seek(), tell() and size(), but they aren't - really necessary. Further more, since the returned size is only a + really necessary. Furthermore, since the returned size is only a guess, it is not safe to rely on it. */ diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp index bc781c1a43..c5b99f56fe 100644 --- a/sound/tests/openal_output_test.cpp +++ b/sound/tests/openal_output_test.cpp @@ -25,15 +25,23 @@ int main() cout << "Playing\n"; - // This initializes OpenAL for us, and serves no other purpose. OpenAL_Factory mg; - OpenAL_Sound snd(source); + SoundPtr snd = mg.loadRaw(source); + try { - snd.play(); + // Try setting all kinds of stuff before playing. OpenAL_Sound + // uses delayed buffer loading, but these should still work + // without a buffer. + snd->stop(); + snd->pause(); + snd->setVolume(0.8); + snd->setPitch(0.9); - while(snd.isPlaying()) + snd->play(); + + while(snd->isPlaying()) { usleep(10000); } diff --git a/stream/filters/buffer_stream.hpp b/stream/filters/buffer_stream.hpp index 63b70000e1..f037212a38 100644 --- a/stream/filters/buffer_stream.hpp +++ b/stream/filters/buffer_stream.hpp @@ -16,7 +16,11 @@ class BufferStream : public MemoryStream std::vector buffer; public: - BufferStream(StreamPtr input) + /* + input = stream to copy + ADD = each read increment (for streams without size()) + */ + BufferStream(StreamPtr input, size_t ADD = 32*1024) { assert(input); @@ -37,7 +41,6 @@ class BufferStream : public MemoryStream { // We DON'T know how big the stream is. We'll have to read // it in increments. - const unsigned int ADD = 32*1024; size_t len=0, newlen; while(!input->eof()) @@ -52,7 +55,7 @@ class BufferStream : public MemoryStream // If we read less than expected, we should be at the // end of the stream - assert(read == ADD || input->eof()); + assert(read == ADD || (read < ADD && input->eof())); } // Downsize to match the real length From 2e2f8e9725fd1a27a82d0ad5c6c0e296e715eb60 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 17 Aug 2010 11:27:06 +0200 Subject: [PATCH 104/269] Mangle update --- mangle | 2 +- sound/sndmanager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mangle b/mangle index 87f6e73975..932465442b 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit 87f6e739759244cef8e9730b8f94e628b8d64681 +Subproject commit 932465442bd97c3bcb3a8630414c16bdd887fa9f diff --git a/sound/sndmanager.cpp b/sound/sndmanager.cpp index 3911432366..fe5ad80915 100644 --- a/sound/sndmanager.cpp +++ b/sound/sndmanager.cpp @@ -115,7 +115,7 @@ public: // point. } - SoundPtr clone() const + SoundPtr clone() { // Cloning only works when we have a manager. assert(mgr); From cd4ed4e6bfb23d736c4b1f30d6096ec164ba937b Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 17 Aug 2010 13:17:39 +0200 Subject: [PATCH 105/269] Implemented OpenAL streaming. Fixed bugs in SampleReader and elsewhere. --- sound/outputs/openal_out.cpp | 201 ++++++++++++++++++++++++----- sound/outputs/openal_out.hpp | 11 ++ sound/sources/libsndfile.cpp | 6 +- sound/sources/sample_reader.cpp | 38 +++++- sound/tests/openal_output_test.cpp | 5 + stream/servers/std_stream.hpp | 2 +- 6 files changed, 217 insertions(+), 46 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index a49840f4e5..166b104e80 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -11,6 +11,20 @@ using namespace Mangle::Sound; // ---- Helper functions and classes ---- +// Static buffer used to shuffle sound data from the input into +// OpenAL. The data is only stored temporarily and then immediately +// shuffled off to the library. This is not thread safe, but it works +// fine with multiple sounds in one thread. It could be made thread +// safe simply by using thread local storage. +const size_t BSIZE = 32*1024; +static char tmp_buffer[BSIZE]; + +// Number of buffers used (per sound) for streaming sounds. Each +// buffer is of size BSIZE. Increasing this will make streaming sounds +// more fault tolerant against temporary lapses in call to update(), +// but will also increase memory usage. 4 should be ok. +const int STREAM_BUF_NUM = 4; + static void fail(const std::string &msg) { throw str_exception("OpenAL exception: " + msg); } @@ -73,10 +87,18 @@ static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) } /// OpenAL sound output -class OpenAL_Sound : public Sound +class Mangle::Sound::OpenAL_Sound : public Sound { ALuint inst; - ALuint bufferID; + + // Buffers. Only the first is used for non-streaming sounds. + ALuint bufferID[4]; + + // Number of buffers used + int bufNum; + + // Parameters used for filling buffers + int fmt, rate; // Poor mans reference counting. Might improve this later. When // NULL, the buffer has not been set up yet. @@ -87,18 +109,74 @@ class OpenAL_Sound : public Sound // Input stream SampleSourcePtr input; + OpenAL_Factory *owner; + bool ownerAlive; + + // Used for streamed sound list + OpenAL_Sound *next, *prev; + void setupBuffer(); + // Fill data into the given buffer and queue it, if there is any + // data left to queue. Assumes the buffer is already unqueued, if + // necessary. + void queueBuffer(ALuint buf) + { + // If there is no more data, do nothing + if(!input) return; + if(input->eof()) + { + input.reset(); + return; + } + + // Get some new data + size_t bytes = input->read(tmp_buffer, BSIZE); + if(bytes == 0) + { + input.reset(); + return; + } + + // Move data into the OpenAL buffer + alBufferData(buf, fmt, tmp_buffer, bytes, rate); + // Queue it + alSourceQueueBuffers(inst, 1, &buf); + checkALError("Queueing buffer data"); + } + public: /// Read samples from the given input buffer - OpenAL_Sound(SampleSourcePtr input); + OpenAL_Sound(SampleSourcePtr input, OpenAL_Factory *fact); /// Play an existing buffer, with a given ref counter. Used /// internally for cloning. - OpenAL_Sound(ALuint buf, int *ref); + OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact); ~OpenAL_Sound(); + // Must be called regularly on streamed sounds + void update() + { + if(!streaming) return; + if(!input) return; + + // Get the number of processed buffers + ALint count; + alGetSourcei(inst, AL_BUFFERS_PROCESSED, &count); + checkALError("getting number of unprocessed buffers"); + + for(int i=0; iupdate(); +} + +void OpenAL_Factory::notifyStreaming(OpenAL_Sound *snd) +{ + // Add the sound to the streaming list + streaming.push_back(snd); +} + +void OpenAL_Factory::notifyDelete(OpenAL_Sound *snd) +{ + // Remove the sound from the stream list + streaming.remove(snd); +} + OpenAL_Factory::~OpenAL_Factory() { + // Notify remaining streamed sounds that we're dying + StreamList::iterator it = streaming.begin(); + for(;it != streaming.end(); it++) + (*it)->notifyOwnerDeath(); + // Deinitialize sound system if(didSetup) { @@ -249,27 +355,31 @@ SoundPtr OpenAL_Sound::clone() { setupBuffer(); assert(!streaming && "cloning streamed sounds not supported"); - return SoundPtr(new OpenAL_Sound(bufferID, refCnt)); + return SoundPtr(new OpenAL_Sound(bufferID[0], refCnt, owner)); } // Constructor used for cloned sounds -OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref) - : bufferID(buf), refCnt(ref), streaming(false) +OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact) + : refCnt(ref), streaming(false), owner(fact), ownerAlive(false) { // Increase the reference count assert(ref != NULL); *refCnt++; + // Set up buffer + bufferID[0] = buf; + bufNum = 1; + // Create a source alGenSources(1, &inst); checkALError("creating instance (clone)"); - alSourcei(inst, AL_BUFFER, bufferID); + alSourcei(inst, AL_BUFFER, bufferID[0]); checkALError("assigning buffer (clone)"); } // Constructor used for original (non-cloned) sounds -OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input) - : bufferID(0), refCnt(NULL), streaming(false), input(_input) +OpenAL_Sound::OpenAL_Sound(SampleSourcePtr _input, OpenAL_Factory *fact) + : refCnt(NULL), streaming(false), input(_input), owner(fact), ownerAlive(false) { // Create a source alGenSources(1, &inst); @@ -287,46 +397,60 @@ void OpenAL_Sound::setupBuffer() assert(input); // Get the format - int fmt, rate; getALFormat(input, fmt, rate); + // Create a cheap reference counter for the buffer + refCnt = new int; + *refCnt = 1; + + if(streaming) bufNum = STREAM_BUF_NUM; + else bufNum = 1; + + // Set up the OpenAL buffer(s) + alGenBuffers(bufNum, bufferID); + checkALError("generating buffer(s)"); + assert(bufferID[0] != 0); + + // STREAMING. if(streaming) { - // To be done + // Just queue all the buffers with data and exit. queueBuffer() + // will work correctly also in the case where there is not + // enough data to fill all the buffers. + for(int i=0; inotifyStreaming(this); + ownerAlive = true; + + return; } - // NON-STREAMING - - // Set up the OpenAL buffer - alGenBuffers(1, &bufferID); - checkALError("generating buffer"); - assert(bufferID != 0); + // NON-STREAMING. We have to load all the data and shove it into the + // buffer. // Does the stream support pointer operations? if(input->hasPtr) { // If so, we can read the data directly from the stream - alBufferData(bufferID, fmt, input->getPtr(), input->size(), rate); + alBufferData(bufferID[0], fmt, input->getPtr(), input->size(), rate); } else { // Read the entire stream into a temporary buffer first - Mangle::Stream::BufferStream buf(input, streaming?1024*1024:32*1024); + Mangle::Stream::BufferStream buf(input, 128*1024); // Then copy that into OpenAL - alBufferData(bufferID, fmt, buf.getPtr(), buf.size(), rate); + alBufferData(bufferID[0], fmt, buf.getPtr(), buf.size(), rate); } - checkALError("loading sound buffer"); + checkALError("loading sound data"); // We're done with the input stream, release the pointer input.reset(); - alSourcei(inst, AL_BUFFER, bufferID); + alSourcei(inst, AL_BUFFER, bufferID[0]); checkALError("assigning buffer"); - - // Create a cheap reference counter for the buffer - refCnt = new int; - *refCnt = 1; } OpenAL_Sound::~OpenAL_Sound() @@ -337,12 +461,19 @@ OpenAL_Sound::~OpenAL_Sound() // Return sound alDeleteSources(1, &inst); + // Notify the factory that we quit. You will hear from our union + // rep. The bool check is to handle cases where the manager goes out + // of scope before the sounds do. In that case, don't try to contact + // the factory. + if(ownerAlive) + owner->notifyDelete(this); + // Decrease the reference counter if((-- *refCnt) == 0) { - // We're the last owner. Delete the buffer and the counter + // We're the last owner. Delete the buffer(s) and the counter // itself. - alDeleteBuffers(1, &bufferID); + alDeleteBuffers(bufNum, bufferID); checkALError("deleting buffer"); delete refCnt; } diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index f3828ff1bb..1460e4ccc6 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -2,16 +2,27 @@ #define MANGLE_SOUND_OPENAL_OUT_H #include "../output.hpp" +#include namespace Mangle { namespace Sound { +class OpenAL_Sound; + class OpenAL_Factory : public SoundFactory { void *device; void *context; bool didSetup; + // List of streaming sounds that need to be updated every frame. + typedef std::list StreamList; + StreamList streaming; + + friend class OpenAL_Sound; + void notifyStreaming(OpenAL_Sound*); + void notifyDelete(OpenAL_Sound*); + public: /// Initialize object. Pass true (default) if you want the /// constructor to set up the current ALCdevice and ALCcontext for diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp index 502d8e5d1c..2e34264fb1 100644 --- a/sound/sources/libsndfile.cpp +++ b/sound/sources/libsndfile.cpp @@ -19,9 +19,9 @@ void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) size_t SndFileSource::readSamples(void *data, size_t length) { - // Read frames. We count channels as part of the frame. Even though - // libsndfile does not, since it still requires the number of frames - // read to be a multiple of channels. + // Read frames. We count channels as part of the frame, even though + // libsndfile does not. This is because the library still requires + // the number of frames read to be a multiple of channels. return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels); } diff --git a/sound/sources/sample_reader.cpp b/sound/sources/sample_reader.cpp index 7cf5b0e00e..fb4be9d5a3 100644 --- a/sound/sources/sample_reader.cpp +++ b/sound/sources/sample_reader.cpp @@ -22,15 +22,36 @@ size_t SampleReader::read(void *_data, size_t length) if(isEof) return 0; char *data = (char*)_data; - // Move the remains from the last operation first + // Pullsize holds the number of bytes that were copied "extra" at + // the end of LAST round. If non-zero, it also means there is data + // left in the pullOver buffer. if(pullSize) { - // pullSize is how much was stored the last time. The data is - // stored at the end of the buffer. - memcpy(data, pullOver+(frameSize-pullSize), pullSize); - length -= pullSize; - data += pullSize; - pullSize = 0; + // Amount of data left + size_t doRead = frameSize - pullSize; + assert(doRead > 0); + + // Make sure we don't read more than we're supposed to + if(doRead > length) doRead = length; + + memcpy(data, pullOver+pullSize, doRead); + + // Update the number of bytes now copied + pullSize += doRead; + assert(pullSize <= frameSize); + + if(pullSize < frameSize) + { + // There is STILL data left in the pull buffer, and we've + // done everything we were supposed to. Leave it and return. + assert(doRead == length); + return doRead; + } + + // Set up variables for further reading below. No need to update + // pullSize, it is overwritten anyway. + length -= doRead; + data += doRead; } // Number of whole frames @@ -38,6 +59,7 @@ size_t SampleReader::read(void *_data, size_t length) // Read the data size_t res = readSamples(data, frames); + assert(res <= frames); // Total bytes read size_t num = res*frameSize; @@ -53,12 +75,14 @@ size_t SampleReader::read(void *_data, size_t length) // Determine the overshoot pullSize = length - num; + assert(pullSize < frameSize && pullSize >= 0); // Are we missing data? if(pullSize) { // Fill in one sample res = readSamples(pullOver,1); + assert(res == 1 || res == 0); if(res) { // Move as much as we can into the output buffer diff --git a/sound/tests/openal_output_test.cpp b/sound/tests/openal_output_test.cpp index c5b99f56fe..a8059ec652 100644 --- a/sound/tests/openal_output_test.cpp +++ b/sound/tests/openal_output_test.cpp @@ -39,11 +39,16 @@ int main() snd->setVolume(0.8); snd->setPitch(0.9); + // Also test streaming, since all the other examples test + // non-streaming sounds. + snd->setStreaming(true); + snd->play(); while(snd->isPlaying()) { usleep(10000); + mg.update(); } } catch(exception &e) diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index 448306e5ca..971c954158 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -29,7 +29,7 @@ class StdStream : public Stream size_t read(void* buf, size_t len) { inf->read((char*)buf, len); - if(inf->fail()) + if(inf->bad()) fail("error reading from stream"); return inf->gcount(); } From 27bef840916581c0dd85b9c8b6ab4109e000dbac Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 17 Aug 2010 16:03:51 +0200 Subject: [PATCH 106/269] Tested if exchanging sf_read with sf_readf in libsndfile help OpenMW crash (it did not) --- sound/sources/libsndfile.cpp | 6 ++---- sound/sources/mpg123_source.cpp | 2 +- sound/tests/openal_mpg123_test.cpp | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp index 2e34264fb1..9ac7ee4656 100644 --- a/sound/sources/libsndfile.cpp +++ b/sound/sources/libsndfile.cpp @@ -19,10 +19,8 @@ void SndFileSource::getInfo(int32_t *_rate, int32_t *_channels, int32_t *_bits) size_t SndFileSource::readSamples(void *data, size_t length) { - // Read frames. We count channels as part of the frame, even though - // libsndfile does not. This is because the library still requires - // the number of frames read to be a multiple of channels. - return channels*sf_read_short((SNDFILE*)handle, (short*)data, length*channels); + // readf_* reads entire frames, including channels + return sf_readf_short((SNDFILE*)handle, (short*)data, length); } SndFileSource::SndFileSource(const std::string &file) diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 327279b85a..8a0dbd1029 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -102,7 +102,7 @@ Mpg123Source::Mpg123Source(const std::string &file) err = mpg123_getformat(mhh, &rate, &channels, &encoding); checkError(err, mh); if(encoding != MPG123_ENC_SIGNED_16) - fail("Bad encoding"); + fail("Unsupported encoding in " + file); // This is the only bit size we support. bits = 16; diff --git a/sound/tests/openal_mpg123_test.cpp b/sound/tests/openal_mpg123_test.cpp index a2826b7551..fef1a5605a 100644 --- a/sound/tests/openal_mpg123_test.cpp +++ b/sound/tests/openal_mpg123_test.cpp @@ -29,6 +29,7 @@ void play(const char* name, bool stream=false) else snd = mg.load(name); + snd->setStreaming(true); snd->play(); while(snd->isPlaying()) From 8f154ac622e8d69cafe9bfecf5ab65494c443e6d Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 18 Aug 2010 12:32:38 +0200 Subject: [PATCH 107/269] Added custom WAV loader --- sound/filters/openal_various.hpp | 40 +++++++++ sound/sources/wav_source.cpp | 95 ++++++++++++++++++++++ sound/sources/wav_source.hpp | 49 +++++++++++ sound/tests/Makefile | 8 +- sound/tests/openal_various_test.cpp | 51 ++++++++++++ sound/tests/output/openal_various_test.out | 1 + sound/tests/output/wav_source_test.out | 12 +++ sound/tests/wav_source_test.cpp | 48 +++++++++++ 8 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 sound/filters/openal_various.hpp create mode 100644 sound/sources/wav_source.cpp create mode 100644 sound/sources/wav_source.hpp create mode 100644 sound/tests/openal_various_test.cpp create mode 100644 sound/tests/output/openal_various_test.out create mode 100644 sound/tests/output/wav_source_test.out create mode 100644 sound/tests/wav_source_test.cpp diff --git a/sound/filters/openal_various.hpp b/sound/filters/openal_various.hpp new file mode 100644 index 0000000000..87030f4b6e --- /dev/null +++ b/sound/filters/openal_various.hpp @@ -0,0 +1,40 @@ +#ifndef MANGLE_VARIOUS_OPENAL_H +#define MANGLE_VARIOUS_OPENAL_H + +#include "input_filter.hpp" +#include "source_splicer.hpp" +#include "../sources/mpg123_source.hpp" +#include "../sources/wav_source.hpp" +#include "../outputs/openal_out.hpp" + +namespace Mangle { +namespace Sound { + +/** A InputFilter that uses OpenAL for output, and load input from + various individual sources, depending on file extension. Currently + supports: + + MP3: mpg123 + WAV: custom wav loader (PCM only) + + This could be an alternative to using eg. libsndfile or other 3rd + party decoder libraries. (We implemented this for OpenMW because + we were experiencing crashes when using libsndfile.) + */ +class OpenAL_Various_Factory : public InputFilter +{ + public: + OpenAL_Various_Factory() + { + SourceSplicer *splice = new SourceSplicer; + + splice->add("mp3", SampleSourceLoaderPtr(new Mpg123Loader)); + splice->add("wav", SampleSourceLoaderPtr(new WavLoader)); + + set(SoundFactoryPtr(new OpenAL_Factory), + SampleSourceLoaderPtr(splice)); + } +}; + +}} +#endif diff --git a/sound/sources/wav_source.cpp b/sound/sources/wav_source.cpp new file mode 100644 index 0000000000..5ee87d492a --- /dev/null +++ b/sound/sources/wav_source.cpp @@ -0,0 +1,95 @@ +#include "wav_source.hpp" + +#include "../../tools/str_exception.hpp" +#include "../../stream/servers/file_stream.hpp" + +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +static void fail(const std::string &msg) +{ throw str_exception("Mangle::Wav exception: " + msg); } + +void WavSource::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits) +{ + // Use the values we found in the constructor + *pRate = rate; + *pChannels = channels; + *pBits = bits; +} + +void WavSource::seek(size_t pos) +{ + // Seek the stream and set 'left' + assert(isSeekable); + if(pos > total) pos = total; + input->seek(dataOffset + pos); + left = total-pos; +} + +size_t WavSource::read(void *data, size_t length) +{ + if(length > left) + length = left; + input->read(data, length); + return length; +} + +void WavSource::open(Mangle::Stream::StreamPtr data) +{ + input = data; + + hasPosition = true; + hasSize = true; + // If we can check position and seek in the input stream, then we + // can seek the wav data too. + isSeekable = input->isSeekable && input->hasPosition; + + // Read header + unsigned int val; + + input->read(&val,4); // header + if(val != 0x46464952) // "RIFF" + fail("Not a WAV file"); + + input->read(&val,4); // size (ignored) + input->read(&val,4); // file format + if(val != 0x45564157) // "WAVE" + fail("Not a valid WAV file"); + + input->read(&val,4); // "fmt " + input->read(&val,4); // chunk size (must be 16) + if(val != 16) + fail("Unsupported WAV format"); + + input->read(&val,2); + if(val != 1) + fail("Non-PCM (compressed) WAV files not supported"); + + // Sound data specification + channels = 0; + input->read(&channels,2); + input->read(&rate, 4); + + // Skip next 6 bytes + input->read(&val, 4); + input->read(&val, 2); + + // Bits per sample + bits = 0; + input->read(&bits,2); + + input->read(&val,4); // Data header + if(val != 0x61746164) // "data" + fail("Expected data block"); + + // Finally, read the data size + input->read(&total,4); + left = total; + + // Store the beginning of the data block for later + if(input->hasPosition) + dataOffset = input->tell(); +} + +WavSource::WavSource(const std::string &file) +{ open(StreamPtr(new FileStream(file))); } diff --git a/sound/sources/wav_source.hpp b/sound/sources/wav_source.hpp new file mode 100644 index 0000000000..227f4da733 --- /dev/null +++ b/sound/sources/wav_source.hpp @@ -0,0 +1,49 @@ +#ifndef MANGLE_SOUND_WAV_SOURCE_H +#define MANGLE_SOUND_WAV_SOURCE_H + +#include "../source.hpp" +#include + +namespace Mangle { +namespace Sound { + +/// WAV file decoder. Has no external library dependencies. +class WavSource : public SampleSource +{ + // Sound info + uint32_t rate, channels, bits; + + // Total size (of output) and bytes left + uint32_t total, left; + + // Offset in input of the beginning of the data block + size_t dataOffset; + + Mangle::Stream::StreamPtr input; + + void open(Mangle::Stream::StreamPtr); + + public: + /// Decode the given sound file + WavSource(const std::string&); + + /// Decode from stream + WavSource(Mangle::Stream::StreamPtr s) + { open(s); } + + void getInfo(int32_t *rate, int32_t *channels, int32_t *bits); + size_t read(void *data, size_t length); + + void seek(size_t); + size_t tell() const { return total-left; } + size_t size() const { return total; } + bool eof() const { return left > 0; } +}; + +#include "loadertemplate.hpp" + +/// A factory that loads WavSources from file and stream +typedef SSL_Template WavLoader; + +}} // Namespace +#endif diff --git a/sound/tests/Makefile b/sound/tests/Makefile index 365e4a05cf..6fcac72da7 100644 --- a/sound/tests/Makefile +++ b/sound/tests/Makefile @@ -1,12 +1,18 @@ GCC=g++ -I../ -Wall -all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test +all: audiere_source_test ffmpeg_source_test openal_output_test openal_audiere_test openal_ffmpeg_test openal_mpg123_test openal_sndfile_test wav_source_test openal_various_test L_FFMPEG=$(shell pkg-config --libs libavcodec libavformat) I_FFMPEG=-I/usr/include/libavcodec -I/usr/include/libavformat L_OPENAL=$(shell pkg-config --libs openal) L_AUDIERE=-laudiere +wav_source_test: wav_source_test.cpp ../sources/wav_source.cpp + $(GCC) $^ -o $@ + +openal_various_test: openal_various_test.cpp ../sources/mpg123_source.cpp ../sources/wav_source.cpp ../outputs/openal_out.cpp + $(GCC) $^ -o $@ -lmpg123 ${L_OPENAL} + openal_audiere_test: openal_audiere_test.cpp ../sources/audiere_source.cpp ../sources/sample_reader.cpp ../outputs/openal_out.cpp ../../stream/clients/audiere_file.cpp $(GCC) $^ -o $@ $(L_AUDIERE) $(L_OPENAL) diff --git a/sound/tests/openal_various_test.cpp b/sound/tests/openal_various_test.cpp new file mode 100644 index 0000000000..9426a672ec --- /dev/null +++ b/sound/tests/openal_various_test.cpp @@ -0,0 +1,51 @@ +#include +#include + +#include "../../stream/servers/file_stream.hpp" +#include "../filters/openal_various.hpp" + +using namespace std; +using namespace Mangle::Stream; +using namespace Mangle::Sound; + +OpenAL_Various_Factory mg; + +void play(const char* name, bool stream=false) +{ + // Only load streams if the backend supports it + if(stream && !mg.canLoadStream) + return; + + cout << "Playing " << name; + if(stream) cout << " (from stream)"; + cout << "\n"; + + SoundPtr snd; + + try + { + if(stream) + snd = mg.load(StreamPtr(new FileStream(name))); + else + snd = mg.load(name); + + snd->play(); + + while(snd->isPlaying()) + { + usleep(10000); + if(mg.needsUpdate) mg.update(); + } + } + catch(exception &e) + { + cout << " ERROR: " << e.what() << "\n"; + } +} + +int main() +{ + play("cow.wav"); + play("cow.wav", true); + return 0; +} diff --git a/sound/tests/output/openal_various_test.out b/sound/tests/output/openal_various_test.out new file mode 100644 index 0000000000..f25a555138 --- /dev/null +++ b/sound/tests/output/openal_various_test.out @@ -0,0 +1 @@ +Playing cow.wav diff --git a/sound/tests/output/wav_source_test.out b/sound/tests/output/wav_source_test.out new file mode 100644 index 0000000000..b6fc8e6fc8 --- /dev/null +++ b/sound/tests/output/wav_source_test.out @@ -0,0 +1,12 @@ +Source size: 37502 +rate=11025 +channels=1 +bits=16 +Reading entire buffer into memory + +Reading cow.raw +Size: 37502 + +Comparing... + +Done diff --git a/sound/tests/wav_source_test.cpp b/sound/tests/wav_source_test.cpp new file mode 100644 index 0000000000..749af18496 --- /dev/null +++ b/sound/tests/wav_source_test.cpp @@ -0,0 +1,48 @@ +#include + +#include "../sources/wav_source.hpp" +#include "../../stream/servers/file_stream.hpp" + +#include +#include + +using namespace std; +using namespace Mangle::Sound; +using namespace Mangle::Stream; + +int main() +{ + WavSource wav("cow.wav"); + + cout << "Source size: " << wav.size() << endl; + int rate, channels, bits; + wav.getInfo(&rate, &channels, &bits); + cout << "rate=" << rate << "\nchannels=" << channels + << "\nbits=" << bits << endl; + + cout << "Reading entire buffer into memory\n"; + void *buf = malloc(wav.size()); + wav.read(buf, wav.size()); + + cout << "\nReading cow.raw\n"; + FileStream tmp("cow.raw"); + cout << "Size: " << tmp.size() << endl; + void *buf2 = malloc(tmp.size()); + tmp.read(buf2, tmp.size()); + + cout << "\nComparing...\n"; + if(tmp.size() != wav.size()) + { + cout << "SIZE MISMATCH!\n"; + assert(0); + } + + if(memcmp(buf, buf2, wav.size()) != 0) + { + cout << "CONTENT MISMATCH!\n"; + assert(0); + } + + cout << "\nDone\n"; + return 0; +} From 200fab03efaa2cbe1b8fce4a742b0195f8912295 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 18 Aug 2010 12:59:21 +0200 Subject: [PATCH 108/269] Improved WAV error checking --- sound/filters/openal_various.hpp | 5 ++--- sound/sources/mpg123_source.cpp | 5 ----- sound/sources/wav_source.cpp | 5 ++++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/sound/filters/openal_various.hpp b/sound/filters/openal_various.hpp index 87030f4b6e..945b3dabda 100644 --- a/sound/filters/openal_various.hpp +++ b/sound/filters/openal_various.hpp @@ -17,9 +17,8 @@ namespace Sound { MP3: mpg123 WAV: custom wav loader (PCM only) - This could be an alternative to using eg. libsndfile or other 3rd - party decoder libraries. (We implemented this for OpenMW because - we were experiencing crashes when using libsndfile.) + This could be an alternative to using eg. 3rd party decoder + libraries like libsndfile. */ class OpenAL_Various_Factory : public InputFilter { diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index 8a0dbd1029..b0eeb77bbd 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -106,11 +106,6 @@ Mpg123Source::Mpg123Source(const std::string &file) // This is the only bit size we support. bits = 16; - - // Ensure the output format does not change. (The tutorial on the - // mpg123 site did this, I assume it's kosher.) - mpg123_format_none(mhh); - mpg123_format(mhh,rate,channels,encoding); } Mpg123Source::~Mpg123Source() diff --git a/sound/sources/wav_source.cpp b/sound/sources/wav_source.cpp index 5ee87d492a..8e3e8558cf 100644 --- a/sound/sources/wav_source.cpp +++ b/sound/sources/wav_source.cpp @@ -30,7 +30,10 @@ size_t WavSource::read(void *data, size_t length) { if(length > left) length = left; - input->read(data, length); + size_t read = input->read(data, length); + if(read < length) + // Something went wrong + fail("WAV read error"); return length; } From 3324f6494c021e3dc69cd76ace5ff25a52e4bcce Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 19 Aug 2010 15:42:00 +0200 Subject: [PATCH 109/269] Added weak_ptr typedef to Sound (WSoundPtr), since this is commonly used --- sound/output.hpp | 1 + tools/shared_ptr.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/output.hpp b/sound/output.hpp index a9012b9584..9bbdebb2c0 100644 --- a/sound/output.hpp +++ b/sound/output.hpp @@ -26,6 +26,7 @@ namespace Sound { */ class Sound; typedef boost::shared_ptr SoundPtr; +typedef boost::weak_ptr WSoundPtr; class Sound { diff --git a/tools/shared_ptr.hpp b/tools/shared_ptr.hpp index da0b399bd2..3d073fc24f 100644 --- a/tools/shared_ptr.hpp +++ b/tools/shared_ptr.hpp @@ -1,3 +1,3 @@ // This file should include whatever it needs to define the boost/tr1 -// shared_ptr<> template. +// shared_ptr<> and weak_ptr<> templates. #include From 6ff6d7556c760bfb784249b63c5610e53f891a9e Mon Sep 17 00:00:00 2001 From: athile Date: Mon, 30 Aug 2010 02:23:52 +0100 Subject: [PATCH 110/269] Merge branch 'master', remote branch 'origin' From 4a31f4e0f874b6e0715b7c195a44f5a3975fdb7e Mon Sep 17 00:00:00 2001 From: athile Date: Mon, 30 Aug 2010 03:09:44 +0100 Subject: [PATCH 111/269] Windows fixes --- sound/source.hpp | 4 ++-- stream/clients/audiere_file.hpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/source.hpp b/sound/source.hpp index 1eb42a2837..8d2a801bcb 100644 --- a/sound/source.hpp +++ b/sound/source.hpp @@ -28,8 +28,8 @@ class SampleSource : public Stream::Stream // Disabled functions by default. You can still override them in // subclasses. void seek(size_t pos) { assert(0); } - size_t tell() const { assert(0); } - size_t size() const { assert(0); } + size_t tell() const { assert(0); return 0; } + size_t size() const { assert(0); return 0; } }; typedef boost::shared_ptr SampleSourcePtr; diff --git a/stream/clients/audiere_file.hpp b/stream/clients/audiere_file.hpp index 691525ad92..61e26f21b2 100644 --- a/stream/clients/audiere_file.hpp +++ b/stream/clients/audiere_file.hpp @@ -23,14 +23,14 @@ class AudiereFile : public audiere::RefImplementation : inp(_inp) {} /// Read 'count' bytes, return bytes successfully read - int read(void *buf, int count) + int ADR_CALL read(void *buf, int count) { return inp->read(buf,count); } /// Seek, relative to specified seek mode. Returns true if successful. - bool seek(int pos, audiere::File::SeekMode mode); + bool ADR_CALL seek(int pos, audiere::File::SeekMode mode); /// Get current position - int tell() + int ADR_CALL tell() { assert(inp->hasPosition); return inp->tell(); } }; From 2407f21c47abe218b9347df0fc9e8e9aebc02e3f Mon Sep 17 00:00:00 2001 From: athile Date: Mon, 30 Aug 2010 10:34:32 +0100 Subject: [PATCH 112/269] WIP Windows build --- sound/outputs/openal_out.cpp | 4 ++-- sound/outputs/openal_out.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 166b104e80..666dbe2557 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -4,8 +4,8 @@ #include "../../stream/filters/buffer_stream.hpp" #include "../../tools/str_exception.hpp" -#include -#include +#include "al.h" +#include "alc.h" using namespace Mangle::Sound; diff --git a/sound/outputs/openal_out.hpp b/sound/outputs/openal_out.hpp index 1460e4ccc6..44d03ecf81 100644 --- a/sound/outputs/openal_out.hpp +++ b/sound/outputs/openal_out.hpp @@ -30,8 +30,8 @@ class OpenAL_Factory : public SoundFactory OpenAL_Factory(bool doSetup = true); ~OpenAL_Factory(); - SoundPtr load(const std::string &file) { assert(0); } - SoundPtr load(Stream::StreamPtr input) { assert(0); } + SoundPtr load(const std::string &file) { assert(0); return SoundPtr(); } + SoundPtr load(Stream::StreamPtr input) { assert(0); return SoundPtr(); } SoundPtr loadRaw(SampleSourcePtr input); void update(); From 4b2e2f7503dead8ccedeff48677ed563aa8aa202 Mon Sep 17 00:00:00 2001 From: athile Date: Mon, 30 Aug 2010 11:37:48 +0100 Subject: [PATCH 113/269] Windows fixes --- gui/events.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/events.cpp b/gui/events.cpp index 73274ee505..713db85858 100644 --- a/gui/events.cpp +++ b/gui/events.cpp @@ -11,8 +11,8 @@ EventInjector::EventInjector(MyGUI::Gui *g) : gui(g), mouseX(0), mouseY(0), enabled(true) { assert(gui); - maxX = gui->getViewWidth(); - maxY = gui->getViewHeight(); + maxX = gui->getViewSize().width; + maxY = gui->getViewSize().height; } template From 3df4362b33edc5d7f89dcd902c7f6194ab4bc048 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 30 Aug 2010 20:30:09 +0200 Subject: [PATCH 114/269] Various improvements to misc/list.hpp --- misc/list.hpp | 104 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 18 deletions(-) diff --git a/misc/list.hpp b/misc/list.hpp index 07e5ddc8d1..b08b57494d 100644 --- a/misc/list.hpp +++ b/misc/list.hpp @@ -6,27 +6,23 @@ namespace Misc{ /* - This is just a suggested data structure for List. You can use - anything that has next and prev pointers. - */ -template -struct ListElem -{ - X data; - ListElem *next; - ListElem *prev; -}; - -/* - A generic class that contains a doubly linked list of elements. It - does not do any allocation of elements, it just keeps pointers to - them. + A simple and completely allocation-less doubly linked list. The + class only manages pointers to and between elements. It leaving all + memory management to the user. */ template struct List { List() : head(0), tail(0), totalNum(0) {} + // Empty the list. + void reset() + { + head = 0; + tail = 0; + totalNum = 0; + } + // Insert an element at the end of the list. The element cannot be // part of any other list when this is called. void insert(Elem *p) @@ -95,9 +91,81 @@ struct List return res; } - Elem* getHead() { return head; } - Elem* getTail() { return tail; } - unsigned int getNum() { return totalNum; } + // Swap the contents of this list with another of the same type + void swap(List &other) + { + Elem *tmp; + + tmp = head; + head = other.head; + other.head = tmp; + + tmp = tail; + tail = other.tail; + other.tail = tmp; + + unsigned int tmp2 = totalNum; + totalNum = other.totalNum; + other.totalNum = tmp2; + } + + /* Absorb the contents of another list. All the elements from the + list are moved to the end of this list, and the other list is + cleared. + */ + void absorb(List &other) + { + assert(&other != this); + if(other.totalNum) + { + absorb(other.head, other.tail, other.totalNum); + other.reset(); + } + assert(other.totalNum == 0); + } + + /* Absorb a range of elements, endpoints included. The elements are + assumed NOT to belong to any list, but they ARE assumed to be + connected with a chain between them. + + The connection MUST run all the way from 'first' to 'last' + through the ->next pointers, and vice versa through ->prev + pointers. + + The parameter 'num' must give the exact number of elements in the + chain. + + Passing first == last, num == 1 is allowed and is equivalent to + calling insert(). + */ + void absorb(Elem* first, Elem *last, int num) + { + assert(first && last && num>=1); + if(tail) + { + // There are existing elements. Insert the first node at the + // end of the list. + assert(head && totalNum > 0); + tail->next = first; + } + else + { + // This is the first element + assert(head == 0 && totalNum == 0); + head = first; + } + + // These have to be done in either case + first->prev = tail; + last->next = 0; + tail = last; + + totalNum += num; + } + + Elem* getHead() const { return head; } + Elem* getTail() const { return tail; } + unsigned int getNum() const { return totalNum; } private: From b486f12a5f1126457f375a5c7170164d7729cce5 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 30 Aug 2010 23:51:47 +0200 Subject: [PATCH 115/269] Fixed crash bug in sndmanager.cpp --- mangle | 2 +- sound/sndmanager.cpp | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mangle b/mangle index 932465442b..3324f6494c 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit 932465442bd97c3bcb3a8630414c16bdd887fa9f +Subproject commit 3324f6494c021e3dc69cd76ace5ff25a52e4bcce diff --git a/sound/sndmanager.cpp b/sound/sndmanager.cpp index fe5ad80915..10a503bf4e 100644 --- a/sound/sndmanager.cpp +++ b/sound/sndmanager.cpp @@ -149,15 +149,26 @@ public: // Update all sounds void updateAll() { - for(ManagedSound *s = list.getHead(); s != NULL; s=s->next) - s->update(); + ManagedSound *s = list.getHead(); + while(s) + { + ManagedSound *cur = s; + // Propagate first, since update() may delete object + s = s->next; + cur->update(); + } } // Detach and unlock all sounds void detachAll() { - for(ManagedSound *s = list.getHead(); s != NULL; s=s->next) - s->detach(); + ManagedSound *s = list.getHead(); + while(s) + { + ManagedSound *cur = s; + s = s->next; + cur->detach(); + } } }; From bf32845d07fa5fd5f2af9747cc2ad51ed1b72cd3 Mon Sep 17 00:00:00 2001 From: athile Date: Tue, 31 Aug 2010 01:47:18 +0100 Subject: [PATCH 116/269] Workaround for sound crash --- sound/sndmanager.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/sound/sndmanager.cpp b/sound/sndmanager.cpp index fe5ad80915..12708f10ce 100644 --- a/sound/sndmanager.cpp +++ b/sound/sndmanager.cpp @@ -1,6 +1,7 @@ #include "sndmanager.hpp" #include "../misc/list.hpp" +#include #include using namespace OEngine::Sound; @@ -126,8 +127,8 @@ public: struct SoundManager::SoundManagerList { private: - // A linked list of ManagedSound objects. - typedef Misc::List SoundList; + // A list of ManagedSound objects. + typedef std::set SoundList; SoundList list; public: @@ -140,24 +141,38 @@ public: // Remove a sound from the list void remove(ManagedSound *snd) { - list.remove(snd); + SoundList::iterator it = list.find(snd); + if (it != list.end()) + list.erase(it); } // Number of sounds in the list - int numSounds() { return list.getNum(); } + int numSounds() { return (int)list.size(); } // Update all sounds void updateAll() { - for(ManagedSound *s = list.getHead(); s != NULL; s=s->next) - s->update(); + // Iterate over a copy of the list since sounds may remove themselves + // from the master list during the iteration + SoundList tmp = list; + for (SoundList::iterator it = tmp.begin(); it != tmp.end(); ++it) + { + ManagedSound* pSound = *it; + pSound->update(); + } } // Detach and unlock all sounds void detachAll() { - for(ManagedSound *s = list.getHead(); s != NULL; s=s->next) - s->detach(); + // Iterate over a copy of the list since sounds may remove themselves + // from the master list during the iteration + SoundList tmp = list; + for (SoundList::iterator it = tmp.begin(); it != tmp.end(); ++it) + { + ManagedSound* pSound = *it; + pSound->detach(); + } } }; From 377e7d71ceea5a1ca166b8df9a03a5b68df83bc5 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 2 Sep 2010 12:52:33 +0200 Subject: [PATCH 117/269] Mangle update --- mangle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mangle b/mangle index 3324f6494c..2407f21c47 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit 3324f6494c021e3dc69cd76ace5ff25a52e4bcce +Subproject commit 2407f21c47abe218b9347df0fc9e8e9aebc02e3f From 053074babb1445993ec40f72f60755de8d8ee5b7 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Thu, 2 Sep 2010 22:19:44 +0200 Subject: [PATCH 118/269] Killed str_error, std::runtime_error does the job --- rend2d/servers/sdl_driver.cpp | 10 +++++----- sound/filters/source_splicer.hpp | 4 ++-- sound/outputs/openal_out.cpp | 4 ++-- sound/sources/audiere_source.cpp | 5 +++-- sound/sources/ffmpeg_source.cpp | 4 ++-- sound/sources/libsndfile.cpp | 4 ++-- sound/sources/mpg123_source.cpp | 4 ++-- sound/sources/wav_source.cpp | 5 +++-- stream/servers/file_stream.hpp | 3 ++- stream/servers/outfile_stream.hpp | 2 +- stream/servers/std_ostream.hpp | 4 ++-- stream/servers/std_stream.hpp | 4 ++-- tools/str_exception.hpp | 25 ------------------------- 13 files changed, 28 insertions(+), 50 deletions(-) delete mode 100644 tools/str_exception.hpp diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp index cc102e6615..89c1c79c21 100644 --- a/rend2d/servers/sdl_driver.cpp +++ b/rend2d/servers/sdl_driver.cpp @@ -2,7 +2,7 @@ #include #include -#include "../../tools/str_exception.hpp" +#include #include using namespace Mangle::Rend2D; @@ -70,7 +70,7 @@ int SDL_Sprite::height() { return surface->h; } SDLDriver::SDLDriver() : display(NULL), realDisp(NULL), softDouble(false) { if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) - throw str_exception("Error initializing SDL video"); + throw std::runtime_error("Error initializing SDL video"); } SDLDriver::~SDLDriver() { @@ -94,7 +94,7 @@ void SDLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) // Create the surface and check it realDisp = SDL_SetVideoMode(width, height, bpp, flags); if(realDisp == NULL) - throw str_exception("Failed setting SDL video mode"); + throw std::runtime_error("Failed setting SDL video mode"); // Code for software double buffering. I haven't found this to be // any speed advantage at all in windowed mode (it's slower, as one @@ -160,7 +160,7 @@ Sprite* SDLDriver::loadImage(const std::string &file) SDL_Surface *surf = IMG_Load(file.c_str()); surf = convertImage(surf); if(surf == NULL) - throw str_exception("SDL failed to load image file '" + file + "'"); + throw std::runtime_error("SDL failed to load image file '" + file + "'"); return spriteFromSDL(surf); } @@ -171,7 +171,7 @@ Sprite* SDLDriver::loadImage(SDL_RWops *src, bool autoFree) SDL_Surface *surf = IMG_Load_RW(src, autoFree); surf = convertImage(surf); if(surf == NULL) - throw str_exception("SDL failed to load image"); + throw std::runtime_error("SDL failed to load image"); return spriteFromSDL(surf); } diff --git a/sound/filters/source_splicer.hpp b/sound/filters/source_splicer.hpp index 9fd49d1260..9c76230865 100644 --- a/sound/filters/source_splicer.hpp +++ b/sound/filters/source_splicer.hpp @@ -2,7 +2,7 @@ #define MANGLE_SOUND_SOURCE_SPLICE_H #include "../source.hpp" -#include "../../tools/str_exception.hpp" +#include #include #include #include @@ -79,7 +79,7 @@ namespace Mangle if(catchAll) return catchAll->load(file); - throw str_exception("No handler for sound file " + file); + throw std::runtime_error("No handler for sound file " + file); } SampleSourcePtr load(Stream::StreamPtr input) { assert(0); } diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 666dbe2557..dd2da29b31 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -1,8 +1,8 @@ #include "openal_out.hpp" #include +#include #include "../../stream/filters/buffer_stream.hpp" -#include "../../tools/str_exception.hpp" #include "al.h" #include "alc.h" @@ -26,7 +26,7 @@ static char tmp_buffer[BSIZE]; const int STREAM_BUF_NUM = 4; static void fail(const std::string &msg) -{ throw str_exception("OpenAL exception: " + msg); } +{ throw std::runtime_error("OpenAL exception: " + msg); } /* Check for AL error. Since we're always calling this with string diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 1f11272b0c..9b97165be9 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -1,12 +1,13 @@ #include "audiere_source.hpp" #include "../../stream/clients/audiere_file.hpp" -#include "../../tools/str_exception.hpp" + +#include using namespace Mangle::Stream; static void fail(const std::string &msg) -{ throw str_exception("Audiere exception: " + msg); } +{ throw std::runtime_error("Audiere exception: " + msg); } using namespace audiere; using namespace Mangle::Sound; diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index 2633515d82..fdc2ff9541 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -1,6 +1,6 @@ #include "ffmpeg_source.hpp" -#include "../../tools/str_exception.hpp" +#include using namespace Mangle::Sound; @@ -9,7 +9,7 @@ using namespace Mangle::Sound; static uint8_t outBuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; static void fail(const std::string &msg) -{ throw str_exception("FFMpeg exception: " + msg); } +{ throw std::runtime_error("FFMpeg exception: " + msg); } // --- Loader --- diff --git a/sound/sources/libsndfile.cpp b/sound/sources/libsndfile.cpp index 9ac7ee4656..b69a2d4368 100644 --- a/sound/sources/libsndfile.cpp +++ b/sound/sources/libsndfile.cpp @@ -1,12 +1,12 @@ #include "libsndfile.hpp" -#include "../../tools/str_exception.hpp" +#include #include using namespace Mangle::Stream; static void fail(const std::string &msg) -{ throw str_exception("Mangle::libsndfile: " + msg); } +{ throw std::runtime_error("Mangle::libsndfile: " + msg); } using namespace Mangle::Sound; diff --git a/sound/sources/mpg123_source.cpp b/sound/sources/mpg123_source.cpp index b0eeb77bbd..24d6ecce1c 100644 --- a/sound/sources/mpg123_source.cpp +++ b/sound/sources/mpg123_source.cpp @@ -1,6 +1,6 @@ #include "mpg123_source.hpp" -#include "../../tools/str_exception.hpp" +#include #include @@ -28,7 +28,7 @@ using namespace Mangle::Stream; */ static void fail(const std::string &msg) -{ throw str_exception("Mangle::Mpg123 exception: " + msg); } +{ throw std::runtime_error("Mangle::Mpg123 exception: " + msg); } static void checkError(int err, void *mh = NULL) { diff --git a/sound/sources/wav_source.cpp b/sound/sources/wav_source.cpp index 8e3e8558cf..a46b3d27ec 100644 --- a/sound/sources/wav_source.cpp +++ b/sound/sources/wav_source.cpp @@ -1,13 +1,14 @@ #include "wav_source.hpp" -#include "../../tools/str_exception.hpp" #include "../../stream/servers/file_stream.hpp" +#include + using namespace Mangle::Stream; using namespace Mangle::Sound; static void fail(const std::string &msg) -{ throw str_exception("Mangle::Wav exception: " + msg); } +{ throw std::runtime_error("Mangle::Wav exception: " + msg); } void WavSource::getInfo(int32_t *pRate, int32_t *pChannels, int32_t *pBits) { diff --git a/stream/servers/file_stream.hpp b/stream/servers/file_stream.hpp index c789d20223..314a49642b 100644 --- a/stream/servers/file_stream.hpp +++ b/stream/servers/file_stream.hpp @@ -3,6 +3,7 @@ #include "std_stream.hpp" #include +#include namespace Mangle { namespace Stream { @@ -20,7 +21,7 @@ class FileStream : public StdStream file.open(name.c_str(), std::ios::binary); if(file.fail()) - throw str_exception("FileStream: failed to open file " + name); + throw std::runtime_error("FileStream: failed to open file " + name); } ~FileStream() { file.close(); } }; diff --git a/stream/servers/outfile_stream.hpp b/stream/servers/outfile_stream.hpp index 2946ff853a..8d953d9041 100644 --- a/stream/servers/outfile_stream.hpp +++ b/stream/servers/outfile_stream.hpp @@ -30,7 +30,7 @@ class OutFileStream : public StdOStream file.open(name.c_str(), mode); if(file.fail()) - throw str_exception("OutFileStream: failed to open file " + name); + throw std::runtime_error("OutFileStream: failed to open file " + name); } ~OutFileStream() { file.close(); } }; diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index f655477ff2..7976226310 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -3,7 +3,7 @@ #include "../stream.hpp" #include -#include "../../tools/str_exception.hpp" +#include namespace Mangle { namespace Stream { @@ -15,7 +15,7 @@ class StdOStream : public Stream std::ostream *inf; static void fail(const std::string &msg) - { throw str_exception("StdOStream: " + msg); } + { throw std::runtime_error("StdOStream: " + msg); } public: StdOStream(std::ostream *_inf) diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index 971c954158..68aca621ba 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -3,7 +3,7 @@ #include "../stream.hpp" #include -#include "../../tools/str_exception.hpp" +#include namespace Mangle { namespace Stream { @@ -15,7 +15,7 @@ class StdStream : public Stream std::istream *inf; static void fail(const std::string &msg) - { throw str_exception("StdStream: " + msg); } + { throw std::runtime_error("StdStream: " + msg); } public: StdStream(std::istream *_inf) diff --git a/tools/str_exception.hpp b/tools/str_exception.hpp deleted file mode 100644 index 2695010558..0000000000 --- a/tools/str_exception.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __STR_EXCEPTION_H -#define __STR_EXCEPTION_H - -#include -#include - -/** @brief A simple exception that takes and holds a string - - Usage: - - throw str_exception("message"); - - */ -class str_exception : public std::exception -{ - std::string msg; - - public: - - str_exception(const std::string &m) : msg(m) {} - ~str_exception() throw() {} - const char* what() const throw() { return msg.c_str(); } -}; - -#endif From 57c5b3b75ddf2743d226f555a2883fd3b6cf599b Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 7 Sep 2010 11:08:09 +0200 Subject: [PATCH 119/269] Created std::istream/ostream wrapper + test. --- stream/clients/io_stream.hpp | 177 ++++++++++++++++++++++++++ stream/servers/std_ostream.hpp | 6 +- stream/stream.hpp | 38 ++++-- stream/tests/Makefile | 7 +- stream/tests/iostream_test.cpp | 132 +++++++++++++++++++ stream/tests/output/iostream_test.out | 21 +++ 6 files changed, 361 insertions(+), 20 deletions(-) create mode 100644 stream/clients/io_stream.hpp create mode 100644 stream/tests/iostream_test.cpp create mode 100644 stream/tests/output/iostream_test.out diff --git a/stream/clients/io_stream.hpp b/stream/clients/io_stream.hpp new file mode 100644 index 0000000000..150f84cae2 --- /dev/null +++ b/stream/clients/io_stream.hpp @@ -0,0 +1,177 @@ +#ifndef MANGLE_STREAM_IOSTREAM_H +#define MANGLE_STREAM_IOSTREAM_H + +#include +#include "../stream.hpp" +#include +#include + +// This seems to work (TODO: test) +#ifndef EOF +#define EOF -1 +#endif + +namespace Mangle { +namespace Stream { + + /** This file contains classes for wrapping an std::istream or + std::ostream around a Mangle::Stream. Not to be confused with + servers/std_(o)stream.hpp, which do the opposite (wrap a + Mangle::Stream around an std::istream/ostream.) + + This allows you to use Mangle streams in places that require std + streams. The std::iostream interface is horrible and NOT + designed for easy subclassing. Defining your custom streams as + Mangle streams and then wrapping them here will usually be much + easier. + */ + class IOStreamBuffer : public std::streambuf + { + StreamPtr client; + std::vector ibuf, obuf; + + public: + IOStreamBuffer(StreamPtr strm) : client(strm) + { + // Set up input buffer + setg(NULL,NULL,NULL); + if(client->isReadable) + { + if(client->hasPtr) + { + assert(client->hasSize); + + // If the client supports direct pointer reading, then + // this is really easy. No internal buffer is needed. + char *ptr = (char*) client->getPtr(); + + // Set up the entire file as the input buffer + setg(ptr,ptr,ptr+client->size()); + } + else + { + // We need to do some manual slave labor. Set up an + // empty input buffer and let underflow() handle it. + ibuf.resize(1024); + } + } + + // Only create output buffer if the stream is writable + if(client->isWritable) + { + obuf.resize(1024); + /* Set beginning and end pointers, tells streambuf to write + to this area and call overflow() when it's full. + + Several examples use size-1, but the documentation for + streambuf clearly states that the end pointers is just + _past_ the last accessible position. + */ + char *beg = &obuf[0]; + setp(beg, beg+obuf.size()); + } + else + // Writing not permitted + setp(NULL, NULL); + } + + /* Underflow is called when there is no more info to read in the + input buffer. We need to refill ibuf with data (if any), and + set up the internal pointers with setg() to reflect the new + state. + */ + int underflow() + { + assert(client->isReadable); + + // If we've exhausted a pointer stream, then there's no more to + // be had. + if(client->hasPtr) + return EOF; + + // Read some more data + assert(ibuf.size()); + char *iptr = &ibuf[0]; + size_t read = client->read(iptr, ibuf.size()); + + // If we're out of data, then EOF + if(read == 0) + return EOF; + + // Otherwise, set up input buffer + setg(iptr, iptr, iptr+read); + + // Return the first char + return *((unsigned char*)iptr); + } + + /* Sync means to flush (write) all current data to the output + stream. It will also set up the entire output buffer to be + usable again. + */ + int sync() + { + assert(client->isWritable); + assert(obuf.size() > 0); + + // Get the number of bytes that streambuf wants us to write + int num = pptr() - pbase(); + assert(num >= 0); + + // Nothing to do + if(num == 0) return 0; + + if((int)client->write(pbase(), num) != num) + return -1; + + // Reset output buffer pointers + char *beg = &obuf[0]; + setp(beg, beg+obuf.size()); + + return 0; + } + + int overflow(int c=EOF) + { + // First, write all existing data + if(sync()) return EOF; + + // Put the requested character in the output + if(c != EOF) + { + *pptr() = c; + pbump(1); + } + + return 0; + } + }; + + class MangleIStream : public std::istream + { + IOStreamBuffer buf; + public: + MangleIStream(StreamPtr inp) + : std::istream(&buf) + , buf(inp) + { + assert(inp->isReadable); + } + }; + + class MangleOStream : public std::ostream + { + IOStreamBuffer buf; + public: + MangleOStream(StreamPtr inp) + : std::ostream(&buf) + , buf(inp) + { + assert(inp->isWritable); + } + + ~MangleOStream() { flush(); } + }; + +}} // namespaces +#endif diff --git a/stream/servers/std_ostream.hpp b/stream/servers/std_ostream.hpp index 7976226310..f406e1a934 100644 --- a/stream/servers/std_ostream.hpp +++ b/stream/servers/std_ostream.hpp @@ -25,11 +25,7 @@ class StdOStream : public Stream hasPosition = true; hasSize = true; isWritable = true; - } - - size_t read(void*,size_t) - { - assert(0&&"reading not supported by StdOStream"); + isReadable = false; } size_t write(const void* buf, size_t len) diff --git a/stream/stream.hpp b/stream/stream.hpp index aa9c14a9ed..2ee4fcbd8d 100644 --- a/stream/stream.hpp +++ b/stream/stream.hpp @@ -24,28 +24,38 @@ class Stream bool hasSize; /// If true, write() works. Writing through pointer operations is - /// not supported. + /// not (yet) supported. bool isWritable; + /// If true, read() and eof() works. + bool isReadable; + /// If true, the getPtr() functions work bool hasPtr; - /// Initialize all bools to false by default + /// Initialize all bools to false by default, except isReadable. Stream() : isSeekable(false), hasPosition(false), hasSize(false), - isWritable(false), hasPtr(false) {} + isWritable(false), isReadable(true), hasPtr(false) {} /// Virtual destructor virtual ~Stream() {} /** Read a given number of bytes from the stream. Returns the actual number read. If the return value is less than count, then the - stream is empty or an error occured. + stream is empty or an error occured. Only required for readable + streams. */ - virtual size_t read(void* buf, size_t count) = 0; + virtual size_t read(void* buf, size_t count) { assert(0); return 0; } /** Write a given number of bytes from the stream. Semantics is - similar to read(). Only valid if isWritable is true + similar to read(). Only valid if isWritable is true. + + The returned value is the number of bytes written. However in + most cases, unlike for read(), a write-count less than requested + usually indicates an error. The implementation should throw such + errors as exceptions rather than expect the caller to handle + them. Since most implementations do NOT support writing we default to an assert(0) here. @@ -57,18 +67,20 @@ class Stream /// Seek to an absolute position in this stream. Not all streams are /// seekable. - virtual void seek(size_t pos) = 0; + virtual void seek(size_t pos) { assert(0); } /// Get the current position in the stream. Non-seekable streams are /// not required to keep track of this. - virtual size_t tell() const = 0; + virtual size_t tell() const { assert(0); return 0; } - /// Return the total size of the stream. For streams where this is - /// not applicable, size() should return zero. - virtual size_t size() const = 0; + /// Return the total size of the stream. For streams hasSize is + /// false, size() should fail in some way, since it is an error to + /// call it in those cases. + virtual size_t size() const { assert(0); return 0; } - /// Returns true if the stream is empty - virtual bool eof() const = 0; + /// Returns true if the stream is empty. Required for readable + /// streams. + virtual bool eof() const { assert(0); return 0; } /// Return a pointer to the entire stream. This function (and the /// other getPtr() variants below) should only be implemented for diff --git a/stream/tests/Makefile b/stream/tests/Makefile index e64b900762..e4e32d1156 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -1,6 +1,6 @@ -GCC=g++ -I../ -Wall +GCC=g++ -I../ -Wall -Werror -all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test +all: ogre_client_test audiere_client_test memory_server_test buffer_filter_test file_server_test slice_filter_test file_write_test iostream_test I_OGRE=$(shell pkg-config --cflags OGRE) L_OGRE=$(shell pkg-config --libs OGRE) @@ -12,6 +12,9 @@ ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream. audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) +iostream_test: iostream_test.cpp ../clients/io_stream.hpp + $(GCC) $< -o $@ + file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ diff --git a/stream/tests/iostream_test.cpp b/stream/tests/iostream_test.cpp new file mode 100644 index 0000000000..7304e82fc7 --- /dev/null +++ b/stream/tests/iostream_test.cpp @@ -0,0 +1,132 @@ +#include +#include "../clients/io_stream.hpp" +#include "../servers/memory_stream.hpp" + +using namespace Mangle::Stream; +using namespace std; + +void test1() +{ + cout << "Testing ASCII reading from memory:\n"; + StreamPtr input(new MemoryStream("hello you world you", 19)); + MangleIStream inp(input); + + string str; + while(!inp.eof()) + { + inp >> str; + cout << "Got: " << str << endl; + } +} + +class Dummy : public Stream +{ + int count; + +public: + + Dummy() : count(0) + { + } + + size_t read(void *ptr, size_t num) + { + char *p = (char*)ptr; + char *start = p; + for(; (count < 2560) && (p-start < (int)num); count++) + { + *p = count / 10; + p++; + } + return p-start; + } + + bool eof() const { return count == 2560; } +}; + +void test2() +{ + cout << "Testing binary reading from non-memory:\n"; + + StreamPtr input(new Dummy); + MangleIStream inp(input); + + int x = 0; + while(!inp.eof()) + { + unsigned char buf[5]; + inp.read((char*)buf,5); + + // istream doesn't set eof() until we read _beyond_ the end of + // the stream, so we need an extra check. + if(inp.gcount() == 0) break; + + /* + for(int i=0;i<5;i++) + cout << (int)buf[i] << " "; + cout << endl; + */ + + assert(buf[4] == buf[0]); + assert(buf[0] == x/2); + x++; + } + cout << " Done\n"; +} + +struct Dummy2 : Stream +{ + Dummy2() + { + isWritable = true; + isReadable = false; + } + + size_t write(const void *ptr, size_t num) + { + const char *p = (const char*)ptr; + cout << " Got: "; + for(unsigned i=0;iwrite("testing", 7); + + cout << " Running through MangleOStream:\n"; + MangleOStream out(output); + out << "hello"; + out << " - are you ok?"; + cout << " Flushing:\n"; + out.flush(); + + cout << " Writing a hell of a lot of characters:\n"; + for(int i=0; i<127; i++) + out << "xxxxxxxx"; // 127 * 8 = 1016 + out << "fffffff"; // +7 = 1023 + cout << " Just one more:\n"; + out << "y"; + cout << " And oooone more:\n"; + out << "z"; + + cout << " Flushing again:\n"; + out.flush(); + cout << " Writing some more and exiting:\n"; + out << "blah bleh blob"; +} + +int main() +{ + test1(); + test2(); + test3(); + return 0; +} diff --git a/stream/tests/output/iostream_test.out b/stream/tests/output/iostream_test.out new file mode 100644 index 0000000000..9c28cade3f --- /dev/null +++ b/stream/tests/output/iostream_test.out @@ -0,0 +1,21 @@ +Testing ASCII reading from memory: +Got: hello +Got: you +Got: world +Got: you +Testing binary reading from non-memory: + Done +Writing to dummy stream: + Pure dummy test: + Got: t e s t i n g + Running through MangleOStream: + Flushing: + Got: h e l l o - a r e y o u o k ? + Writing a hell of a lot of characters: + Just one more: + And oooone more: + Got: x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x f f f f f f f y + Flushing again: + Got: z + Writing some more and exiting: + Got: b l a h b l e h b l o b From d41b3c017d6cb864b7299be7c129c654c064e074 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 7 Sep 2010 13:12:21 +0200 Subject: [PATCH 120/269] Completed and refactored io_stream, split into hpp/cpp. --- stream/clients/io_stream.cpp | 221 ++++++++++++++++++++++++++ stream/clients/io_stream.hpp | 166 ++----------------- stream/tests/Makefile | 4 +- stream/tests/iostream_test.cpp | 48 +++++- stream/tests/output/iostream_test.out | 11 ++ 5 files changed, 296 insertions(+), 154 deletions(-) create mode 100644 stream/clients/io_stream.cpp diff --git a/stream/clients/io_stream.cpp b/stream/clients/io_stream.cpp new file mode 100644 index 0000000000..5f1edc2217 --- /dev/null +++ b/stream/clients/io_stream.cpp @@ -0,0 +1,221 @@ +#include "io_stream.hpp" + +// This seems to work +#ifndef EOF +#define EOF -1 +#endif + +using namespace Mangle::Stream; + +#define BSIZE 1024 + +// Streambuf for normal stream reading +class _istreambuf : public std::streambuf +{ + StreamPtr client; + char buf[BSIZE]; + +public: + _istreambuf(StreamPtr strm) : client(strm) + { + // Make sure we picked the right class + assert(client->isReadable); + assert(!client->hasPtr); + + // Tell streambuf to delegate reading operations to underflow() + setg(NULL,NULL,NULL); + + // Disallow writing + setp(NULL,NULL); + } + + /* Underflow is called when there is no more info to read in the + input buffer. We need to refill buf with new data (if any), and + set up the internal pointers with setg() to reflect the new + state. + */ + int underflow() + { + // Read some more data + size_t read = client->read(buf, BSIZE); + assert(read <= BSIZE); + + // If we're out of data, then EOF + if(read == 0) + return EOF; + + // Otherwise, set up input buffer + setg(buf, buf, buf+read); + + // Return the first char + return *((unsigned char*)buf); + } + + // Seek stream, if the source supports it. Ignores the second + // parameter as Mangle doesn't separate input and output pointers. + std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in) + { + // Does this stream know how to seek? + if(!client->isSeekable || !client->hasPosition) + // If not, signal an error. + return -1; + + // Set stream position and reset the buffer. + client->seek(pos); + setg(NULL,NULL,NULL); + + return client->tell(); + } +}; + +// Streambuf optimized for pointer-based input streams +class _ptrstreambuf : public std::streambuf +{ + StreamPtr client; + +public: + _ptrstreambuf(StreamPtr strm) : client(strm) + { + // Make sure we picked the right class + assert(client->isReadable); + assert(client->hasPtr); + + // seekpos() does all the work + seekpos(0); + } + + // Underflow is only called when we're at the end of the file + int underflow() { return EOF; } + + // Seek to a new position within the memory stream. This bypasses + // client->seek() entirely so isSeekable doesn't have to be set. + std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in) + { + // All pointer streams need a size + assert(client->hasSize); + + // Figure out how much will be left of the stream after seeking + size_t size = client->size() - pos; + + // Get a pointer + char* ptr = (char*)client->getPtr(pos,size); + + // And use it + setg(ptr,ptr,ptr+size); + + return pos; + } +}; + +// Streambuf for stream writing +class _ostreambuf : public std::streambuf +{ + StreamPtr client; + char buf[BSIZE]; + +public: + _ostreambuf(StreamPtr strm) : client(strm) + { + // Make sure we picked the right class + assert(client->isWritable); + + // Inform streambuf about our nice buffer + setp(buf, buf+BSIZE); + + // Disallow reading + setg(NULL,NULL,NULL); + } + + /* Sync means to flush (write) all current data to the output + stream. It will also set up the entire output buffer to be usable + again. + */ + int sync() + { + // Get the number of bytes that streambuf wants us to write + int num = pptr() - pbase(); + assert(num >= 0); + + // Is there any work to do? + if(num == 0) return 0; + + if((int)client->write(pbase(), num) != num) + // Inform caller that writing failed + return -1; + + // Reset output buffer pointers + setp(buf, buf+BSIZE); + + // No error + return 0; + } + + /* Called whenever the output buffer is full. + */ + int overflow(int c) + { + // First, write all existing data + if(sync()) return EOF; + + // Put the requested character in the next round of output + if(c != EOF) + { + *pptr() = c; + pbump(1); + } + + // No error + return 0; + } + + // Seek stream, if the source supports it. + std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::out) + { + if(!client->isSeekable || !client->hasPosition) + return -1; + + // Flush data and reset buffers + sync(); + + // Set stream position + client->seek(pos); + + return client->tell(); + } +}; + +MangleIStream::MangleIStream(StreamPtr inp) + : std::istream(NULL) +{ + assert(inp->isReadable); + + // Pick the right streambuf implementation based on whether the + // input supports pointers or not. + if(inp->hasPtr) + buf = new _ptrstreambuf(inp); + else + buf = new _istreambuf(inp); + + rdbuf(buf); +} + +MangleIStream::~MangleIStream() +{ + delete buf; +} + +MangleOStream::MangleOStream(StreamPtr out) + : std::ostream(NULL) +{ + assert(out->isWritable); + buf = new _ostreambuf(out); + + rdbuf(buf); +} + +MangleOStream::~MangleOStream() +{ + // Make sure we don't have lingering data on exit + flush(); + delete buf; +} diff --git a/stream/clients/io_stream.hpp b/stream/clients/io_stream.hpp index 150f84cae2..98c6252ed2 100644 --- a/stream/clients/io_stream.hpp +++ b/stream/clients/io_stream.hpp @@ -4,173 +4,39 @@ #include #include "../stream.hpp" #include -#include - -// This seems to work (TODO: test) -#ifndef EOF -#define EOF -1 -#endif namespace Mangle { namespace Stream { /** This file contains classes for wrapping an std::istream or - std::ostream around a Mangle::Stream. Not to be confused with - servers/std_(o)stream.hpp, which do the opposite (wrap a - Mangle::Stream around an std::istream/ostream.) + std::ostream around a Mangle::Stream. This allows you to use Mangle streams in places that require std - streams. The std::iostream interface is horrible and NOT - designed for easy subclassing. Defining your custom streams as - Mangle streams and then wrapping them here will usually be much - easier. + streams. + + This is much easier than trying to make your own custom streams + into iostreams. The std::iostream interface is horrible and NOT + designed for easy subclassing. Create a Mangle::Stream instead, + and use this wrapper. */ - class IOStreamBuffer : public std::streambuf - { - StreamPtr client; - std::vector ibuf, obuf; - - public: - IOStreamBuffer(StreamPtr strm) : client(strm) - { - // Set up input buffer - setg(NULL,NULL,NULL); - if(client->isReadable) - { - if(client->hasPtr) - { - assert(client->hasSize); - - // If the client supports direct pointer reading, then - // this is really easy. No internal buffer is needed. - char *ptr = (char*) client->getPtr(); - - // Set up the entire file as the input buffer - setg(ptr,ptr,ptr+client->size()); - } - else - { - // We need to do some manual slave labor. Set up an - // empty input buffer and let underflow() handle it. - ibuf.resize(1024); - } - } - - // Only create output buffer if the stream is writable - if(client->isWritable) - { - obuf.resize(1024); - /* Set beginning and end pointers, tells streambuf to write - to this area and call overflow() when it's full. - - Several examples use size-1, but the documentation for - streambuf clearly states that the end pointers is just - _past_ the last accessible position. - */ - char *beg = &obuf[0]; - setp(beg, beg+obuf.size()); - } - else - // Writing not permitted - setp(NULL, NULL); - } - - /* Underflow is called when there is no more info to read in the - input buffer. We need to refill ibuf with data (if any), and - set up the internal pointers with setg() to reflect the new - state. - */ - int underflow() - { - assert(client->isReadable); - - // If we've exhausted a pointer stream, then there's no more to - // be had. - if(client->hasPtr) - return EOF; - - // Read some more data - assert(ibuf.size()); - char *iptr = &ibuf[0]; - size_t read = client->read(iptr, ibuf.size()); - - // If we're out of data, then EOF - if(read == 0) - return EOF; - - // Otherwise, set up input buffer - setg(iptr, iptr, iptr+read); - - // Return the first char - return *((unsigned char*)iptr); - } - - /* Sync means to flush (write) all current data to the output - stream. It will also set up the entire output buffer to be - usable again. - */ - int sync() - { - assert(client->isWritable); - assert(obuf.size() > 0); - - // Get the number of bytes that streambuf wants us to write - int num = pptr() - pbase(); - assert(num >= 0); - - // Nothing to do - if(num == 0) return 0; - - if((int)client->write(pbase(), num) != num) - return -1; - - // Reset output buffer pointers - char *beg = &obuf[0]; - setp(beg, beg+obuf.size()); - - return 0; - } - - int overflow(int c=EOF) - { - // First, write all existing data - if(sync()) return EOF; - - // Put the requested character in the output - if(c != EOF) - { - *pptr() = c; - pbump(1); - } - - return 0; - } - }; + // An istream wrapping a readable Mangle::Stream. Has extra + // optimizations for pointer-based streams. class MangleIStream : public std::istream { - IOStreamBuffer buf; + std::streambuf *buf; public: - MangleIStream(StreamPtr inp) - : std::istream(&buf) - , buf(inp) - { - assert(inp->isReadable); - } + MangleIStream(StreamPtr inp); + ~MangleIStream(); }; + // An ostream wrapping a writable Mangle::Stream. class MangleOStream : public std::ostream { - IOStreamBuffer buf; + std::streambuf *buf; public: - MangleOStream(StreamPtr inp) - : std::ostream(&buf) - , buf(inp) - { - assert(inp->isWritable); - } - - ~MangleOStream() { flush(); } + MangleOStream(StreamPtr inp); + ~MangleOStream(); }; }} // namespaces diff --git a/stream/tests/Makefile b/stream/tests/Makefile index e4e32d1156..5f43978190 100644 --- a/stream/tests/Makefile +++ b/stream/tests/Makefile @@ -12,8 +12,8 @@ ogre_client_test: ogre_client_test.cpp ../stream.hpp ../clients/ogre_datastream. audiere_client_test: audiere_client_test.cpp ../stream.hpp ../clients/audiere_file.hpp ../clients/audiere_file.cpp $(GCC) $< -o $@ ../clients/audiere_file.cpp $(L_AUDIERE) -iostream_test: iostream_test.cpp ../clients/io_stream.hpp - $(GCC) $< -o $@ +iostream_test: iostream_test.cpp ../clients/io_stream.cpp + $(GCC) $^ -o $@ file_server_test: file_server_test.cpp ../stream.hpp ../servers/file_stream.hpp ../servers/std_stream.hpp $(GCC) $< -o $@ diff --git a/stream/tests/iostream_test.cpp b/stream/tests/iostream_test.cpp index 7304e82fc7..6acebbbd61 100644 --- a/stream/tests/iostream_test.cpp +++ b/stream/tests/iostream_test.cpp @@ -46,7 +46,7 @@ public: void test2() { - cout << "Testing binary reading from non-memory:\n"; + cout << "\nTesting binary reading from non-memory:\n"; StreamPtr input(new Dummy); MangleIStream inp(input); @@ -95,7 +95,7 @@ struct Dummy2 : Stream void test3() { - cout << "Writing to dummy stream:\n"; + cout << "\nWriting to dummy stream:\n"; cout << " Pure dummy test:\n"; StreamPtr output(new Dummy2); @@ -123,10 +123,54 @@ void test3() out << "blah bleh blob"; } +struct Dummy3 : Stream +{ + int pos; + + Dummy3() : pos(0) + { + hasPosition = true; + isSeekable = true; + } + + size_t read(void*, size_t num) + { + cout << " Reading " << num << " bytes from " << pos << endl; + pos += num; + return num; + } + + void seek(size_t npos) { pos = npos; } + size_t tell() const { return pos; } +}; + +void test4() +{ + cout << "\nTesting seeking;\n"; + StreamPtr input(new Dummy3); + + cout << " Direct reading:\n"; + input->read(0,10); + input->read(0,5); + + MangleIStream inp(input); + + cout << " Reading from istream:\n"; + char buf[20]; + inp.read(buf, 20); + inp.read(buf, 20); + inp.read(buf, 20); + + cout << " Seeking to 30 and reading again:\n"; + inp.seekg(30); + inp.read(buf, 20); +} + int main() { test1(); test2(); test3(); + test4(); return 0; } diff --git a/stream/tests/output/iostream_test.out b/stream/tests/output/iostream_test.out index 9c28cade3f..b6da80c80d 100644 --- a/stream/tests/output/iostream_test.out +++ b/stream/tests/output/iostream_test.out @@ -3,8 +3,10 @@ Got: hello Got: you Got: world Got: you + Testing binary reading from non-memory: Done + Writing to dummy stream: Pure dummy test: Got: t e s t i n g @@ -19,3 +21,12 @@ Writing to dummy stream: Got: z Writing some more and exiting: Got: b l a h b l e h b l o b + +Testing seeking; + Direct reading: + Reading 10 bytes from 0 + Reading 5 bytes from 10 + Reading from istream: + Reading 1024 bytes from 15 + Seeking to 30 and reading again: + Reading 1024 bytes from 30 From fb95756a50ca0b77e0588c4c249c26b00bcf6011 Mon Sep 17 00:00:00 2001 From: athile Date: Tue, 7 Sep 2010 15:17:46 +0100 Subject: [PATCH 121/269] Fix crash at shutdown --- sound/clients/ogre_listener_mover.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/clients/ogre_listener_mover.hpp b/sound/clients/ogre_listener_mover.hpp index d1cfcb65ce..739c08a13f 100644 --- a/sound/clients/ogre_listener_mover.hpp +++ b/sound/clients/ogre_listener_mover.hpp @@ -9,7 +9,7 @@ namespace Mangle { namespace Sound { /** This class lets a sound listener (ie. the SoundFactory) track a - given camera in Ogre3D. The poisition and orientation of the + given camera in Ogre3D. The position and orientation of the listener will be updated to match the camera whenever the camera is moved. */ @@ -27,6 +27,16 @@ namespace Sound { camera->addListener(this); } + void unfollowCamera() + { + // If the camera is null, this object wasn't following a camera. + // It doesn't make sense to call unfollow + assert(camera != NULL); + + camera->removeListener(this); + camera = NULL; + } + private: Mangle::Sound::SoundFactoryPtr soundFact; Ogre::Camera *camera; From 4d2573038867df3857501ad6bad8ef0d75807112 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 8 Sep 2010 15:09:39 +0200 Subject: [PATCH 122/269] Added SDL+OpenGL 2D rendering backend (ony basic features) --- rend2d/servers/sdl_gl_driver.cpp | 321 ++++++++++++++++++++++++ rend2d/servers/sdl_gl_driver.hpp | 132 ++++++++++ rend2d/tests/Makefile | 10 +- rend2d/tests/output/sdl_move_test.out | 0 rend2d/tests/output/sdlgl_move_test.out | 0 rend2d/tests/sdl_move_test.cpp | 30 +++ rend2d/tests/sdlgl_move_test.cpp | 31 +++ 7 files changed, 522 insertions(+), 2 deletions(-) create mode 100644 rend2d/servers/sdl_gl_driver.cpp create mode 100644 rend2d/servers/sdl_gl_driver.hpp create mode 100644 rend2d/tests/output/sdl_move_test.out create mode 100644 rend2d/tests/output/sdlgl_move_test.out create mode 100644 rend2d/tests/sdl_move_test.cpp create mode 100644 rend2d/tests/sdlgl_move_test.cpp diff --git a/rend2d/servers/sdl_gl_driver.cpp b/rend2d/servers/sdl_gl_driver.cpp new file mode 100644 index 0000000000..aadb6abce3 --- /dev/null +++ b/rend2d/servers/sdl_gl_driver.cpp @@ -0,0 +1,321 @@ +#include "sdl_gl_driver.hpp" + +#include +#include +#include +#include +#include + +using namespace Mangle::Rend2D; + +void SDLGL_Sprite::draw(Sprite *s, // Must be SDLGL_Sprite + int x, int y, // Destination position + int sx, int sy, // Source position + int w, int h // Amount to draw. -1 means remainder. + ) +{ + // Get source surface + SDLGL_Sprite *other = dynamic_cast(s); + assert(other != NULL); + SDL_Surface *img = other->getSurface(); + + // Check coordinate validity + assert(sx <= img->w && sy <= img->h); + assert(x <= surface->w && y <= surface->h); + assert(sx >= 0 && sy >= 0); + + // Compute width and height if necessary + if(w == -1) w = img->w - sx; + if(h == -1) h = img->h - sy; + + // Check them if they're valid + assert(w >= 0 && w <= img->w); + assert(h >= 0 && h <= img->h); + + SDL_Rect dest; + dest.x = x; + dest.y = y; + dest.w = w; + dest.h = h; + + SDL_Rect src; + src.x = sx; + src.y = sy; + src.w = w; + src.h = h; + + // Do the Blitman + SDL_BlitSurface(img, &src, surface, &dest); +} + +SDLGL_Sprite::SDLGL_Sprite(SDL_Surface *s, bool autoDelete) + : surface(s), autoDel(autoDelete) +{ + assert(surface != NULL); +} + +SDLGL_Sprite::~SDLGL_Sprite() +{ + if(autoDel) + SDL_FreeSurface(surface); +} + +void SDLGL_Sprite::fill(int value) +{ + SDL_FillRect(surface, NULL, value); +} + +int SDLGL_Sprite::width() { return surface->w; } +int SDLGL_Sprite::height() { return surface->h; } + +SDLGLDriver::SDLGLDriver() : display(NULL), realDisp(NULL) +{ + if (SDL_InitSubSystem( SDL_INIT_VIDEO ) == -1) + throw std::runtime_error("Error initializing SDL video"); +} +SDLGLDriver::~SDLGLDriver() +{ + if(display) delete display; + SDL_Quit(); +} + +// Surface used for the screen. Since OpenGL surfaces must have sizes +// that are powers of 2, we have to "fake" the returned display size +// to match the screen, not the surface itself. If we don't use this, +// the client program will get confused about the actual size of our +// screen, thinking it is bigger than it is. +struct FakeSizeSprite : SDLGL_Sprite +{ + int fakeW, fakeH; + + FakeSizeSprite(SDL_Surface *s, int fw, int fh) + : SDLGL_Sprite(s), fakeW(fw), fakeH(fh) + {} + + int width() { return fakeW; } + int height() { return fakeH; } +}; + +static int makePow2(int num) +{ + assert(num); + if((num & (num-1)) != 0) + { + int cnt = 0; + while(num) + { + num >>= 1; + cnt++; + } + num = 1 << cnt; + } + return num; +} + +void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) +{ + unsigned int flags; + + if(display) delete display; + + flags = SDL_OPENGL; + + if (fullscreen) + flags |= SDL_FULLSCREEN; + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); + + // Create the surface and check it + screen = SDL_SetVideoMode(width, height, bpp, flags); + if(screen == NULL) + throw std::runtime_error("Failed setting SDL video mode"); + + // Expand width and height to be powers of 2 + int width2 = makePow2(width); + int height2 = makePow2(height); + + // Create a new SDL surface of this size + const SDL_PixelFormat& fmt = *(screen->format); + realDisp = SDL_CreateRGBSurface(SDL_SWSURFACE,width2,height2, + fmt.BitsPerPixel, + fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask); + + // Create a sprite directly representing the display surface. This + // allows the user to blit to it directly. + display = new FakeSizeSprite(realDisp, width, height); + + // Set up the OpenGL format + nOfColors = fmt.BytesPerPixel; + + if(nOfColors == 4) + { + if (fmt.Rmask == 0x000000ff) + texture_format = GL_RGBA; + else + texture_format = GL_BGRA; + } + else if(nOfColors == 3) + { + if (fmt.Rmask == 0x000000ff) + texture_format = GL_RGB; + else + texture_format = GL_BGR; + } + else + assert(0 && "unsupported screen format"); + + glEnable(GL_TEXTURE_2D); + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); +} + +void SDLGLDriver::updateNoSwap() +{ + if(!realDisp) return; + + // Fist, set up the screen texture: + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Edit the texture object's image data + glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, realDisp->w, realDisp->h, 0, + texture_format, GL_UNSIGNED_BYTE, realDisp->pixels ); + + glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + // OpenGL barf. Set up the projection to match our screen + int vPort[4]; + glGetIntegerv(GL_VIEWPORT, vPort); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, vPort[2], 0, vPort[3], -1, 1); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + glBegin( GL_QUADS ); + + // Bottom-left vertex (corner) + glTexCoord2i( 0, 0 ); + glVertex3f(0,0,0); + + // Bottom-right vertex (corner) + glTexCoord2i( 1, 0 ); + glVertex3f( realDisp->w, 0, 0.f ); + + // Top-right vertex (corner) + glTexCoord2i( 1, 1 ); + glVertex3f( realDisp->w, realDisp->h, 0.f ); + + // Top-left vertex (corner) + glTexCoord2i( 0, 1 ); + glVertex3f( 0, realDisp->h, 0.f ); + glEnd(); + + /* + glBegin(GL_TRIANGLES); + glColor3ub(255, 0, 0); + glVertex2d(0, 0); + + glColor3ub(0, 255, 0); + glVertex2d(100,0); + + glColor3ub(0, 0, 255); + glVertex2d(50, 50); + glEnd(); + */ + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +void SDLGLDriver::swap() +{ + SDL_GL_SwapBuffers(); +} + +void SDLGLDriver::update() +{ + updateNoSwap(); + swap(); +} + +/// Set the window title, as well as the title of the window when +/// "iconified" +void SDLGLDriver::setWindowTitle(const std::string &title, + const std::string &icon) +{ + SDL_WM_SetCaption( title.c_str(), icon.c_str() ); +} + +// Convert the given surface to display format. +static SDL_Surface* convertImage(SDL_Surface* surf) +{ + if(surf != NULL) + { + // Convert the image to the display buffer format, for faster + // blitting + SDL_Surface *surf2 = SDL_DisplayFormat(surf); + SDL_FreeSurface(surf); + surf = surf2; + } + return surf; +} + +/// Load sprite from an image file, using SDL_image. +Sprite* SDLGLDriver::loadImage(const std::string &file) +{ + SDL_Surface *surf = IMG_Load(file.c_str()); + surf = convertImage(surf); + if(surf == NULL) + throw std::runtime_error("SDL failed to load image file '" + file + "'"); + return spriteFromSDL(surf); +} + +/// Load sprite from an SDL_RWops structure. autoFree determines +/// whether the RWops struct should be closed/freed after use. +Sprite* SDLGLDriver::loadImage(SDL_RWops *src, bool autoFree) +{ + SDL_Surface *surf = IMG_Load_RW(src, autoFree); + surf = convertImage(surf); + if(surf == NULL) + throw std::runtime_error("SDL failed to load image"); + return spriteFromSDL(surf); +} + +/// Load a sprite from an image file stored in memory. Uses +/// SDL_image. +Sprite* SDLGLDriver::loadImage(const void* data, size_t size) +{ + SDL_RWops *rw = SDL_RWFromConstMem(data, size); + return loadImage(rw, true); +} + +void SDLGLDriver::setGamma(float red, float green, float blue) +{ + SDL_SetGamma(red,green,blue); +} + +/// Convert an existing SDL surface into a sprite +Sprite* SDLGLDriver::spriteFromSDL(SDL_Surface *surf, bool autoFree) +{ + assert(surf); + return new SDLGL_Sprite(surf, autoFree); +} + +void SDLGLDriver::sleep(int ms) { SDL_Delay(ms); } +unsigned int SDLGLDriver::ticks() { return SDL_GetTicks(); } diff --git a/rend2d/servers/sdl_gl_driver.hpp b/rend2d/servers/sdl_gl_driver.hpp new file mode 100644 index 0000000000..d116e3659b --- /dev/null +++ b/rend2d/servers/sdl_gl_driver.hpp @@ -0,0 +1,132 @@ +#ifndef MANGLE_DRAW2D_SDLGL_H +#define MANGLE_DRAW2D_SDLGL_H + +/** This driver is similar to SDLDriver, except that it uses SDL on + top of OpenGL. + + I've decided to make it a separate file instead of just adding + optional OpenGL support to the original, so that pure SDL users + don't have to add OpenGL as a dependency. + */ + +#include "../driver.hpp" + +// Predeclarations keep the streets safe at night +struct SDL_Surface; +struct SDL_RWops; + +namespace Mangle +{ + namespace Rend2D + { + /// SDL-implementation of Sprite + struct SDLGL_Sprite : Sprite + { + /** Draw a sprite in the given position. Can only draw other SDL + sprites. + */ + void draw(Sprite *s, // Must be SDLGL_Sprite + int x, int y, // Destination position + int sx=0, int sy=0, // Source position + int w=-1, int h=-1 // Amount to draw. -1 means remainder. + ); + + SDLGL_Sprite(SDL_Surface *s, bool autoDelete=true); + ~SDLGL_Sprite(); + + // Information retrieval + virtual int width(); + virtual int height(); + SDL_Surface *getSurface() { return surface; } + + // Fill with a given pixel value + void fill(int value); + + private: + // The SDL surface + SDL_Surface* surface; + + // If true, delete this surface when the canvas is destructed + bool autoDel; + }; + + class SDLGLDriver : public Driver + { + // The main display surface + SDLGL_Sprite *display; + + // The screen surface. This is completely unused. + SDL_Surface *screen; + + // The display surface and main GL texture. These are used when + // drawing the entire screen as one surface, as a drop-in + // replacement for SDLDriver. + SDL_Surface *realDisp; + unsigned int texture; + int nOfColors, texture_format; + + public: + SDLGLDriver(); + ~SDLGLDriver(); + + /// Sets the video mode. Will create the window if it is not + /// already set up. Note that for SDL, bpp=0 means use current + /// bpp. + void setVideoMode(int width, int height, int bpp=0, bool fullscreen=false); + + /// Update the screen + void update(); + + /// Calls SDL_GL_SwapBuffers + void swap(); + + /// Draw surface to screen but do not call SDL_GL_SwapBuffers() + void updateNoSwap(); + + /// Set the window title, as well as the title of the window + /// when "iconified" + void setWindowTitle(const std::string &title, + const std::string &icon); + + // Include overloads from our Glorious parent + using Driver::setWindowTitle; + + /// Load sprite from an image file, using SDL_image. + Sprite* loadImage(const std::string &file); + + /// Load sprite from an SDL_RWops structure. autoFree determines + /// whether the RWops struct should be closed/freed after use. + Sprite* loadImage(SDL_RWops *src, bool autoFree=false); + + /// Load a sprite from an image file stored in memory. Uses + /// SDL_image. + Sprite* loadImage(const void* data, size_t size); + + /// Set gamma value + void setGamma(float gamma) { setGamma(gamma,gamma,gamma); } + + /// Set gamma individually for red, green, blue + void setGamma(float red, float green, float blue); + + /// Convert an existing SDL surface into a sprite + Sprite* spriteFromSDL(SDL_Surface *surf, bool autoFree = true); + + // Get width and height + int width() { return display ? display->width() : 0; } + int height() { return display ? display->height() : 0; } + + /// Get the screen sprite + Sprite *getScreen() { return display; } + + /// Not really a graphic-related function, but very + /// handly. Sleeps the given number of milliseconds using + /// SDL_Delay(). + void sleep(int ms); + + /// Get the number of ticks since SDL initialization, using + /// SDL_GetTicks(). + unsigned int ticks(); + }; + } +} +#endif diff --git a/rend2d/tests/Makefile b/rend2d/tests/Makefile index efbbda5d67..d430f60a93 100644 --- a/rend2d/tests/Makefile +++ b/rend2d/tests/Makefile @@ -1,9 +1,15 @@ -GCC=g++ -Wall +GCC=g++ -Wall -Werror -all: sdl_test +all: sdl_test sdl_move_test sdlgl_move_test sdl_test: sdl_test.cpp $(GCC) $< ../servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image +sdl_move_test: sdl_move_test.cpp ../servers/sdl_driver.cpp + $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image + +sdlgl_move_test: sdlgl_move_test.cpp ../servers/sdl_gl_driver.cpp + $(GCC) $^ -o $@ -I/usr/include/SDL/ -lSDL -lSDL_image -lGL + clean: rm *_test diff --git a/rend2d/tests/output/sdl_move_test.out b/rend2d/tests/output/sdl_move_test.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rend2d/tests/output/sdlgl_move_test.out b/rend2d/tests/output/sdlgl_move_test.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rend2d/tests/sdl_move_test.cpp b/rend2d/tests/sdl_move_test.cpp new file mode 100644 index 0000000000..bfbca98fa7 --- /dev/null +++ b/rend2d/tests/sdl_move_test.cpp @@ -0,0 +1,30 @@ +#include +#include + +using namespace std; + +#include "../servers/sdl_driver.hpp" + +using namespace Mangle::Rend2D; + +int main() +{ + SDLDriver sdl; + + sdl.setVideoMode(640,480,0,false); + sdl.setWindowTitle("Testing 123"); + Sprite *screen = sdl.getScreen(); + const char* imgName = "tile1-blue.png"; + Sprite *image = sdl.loadImage(imgName); + + for(int frames=0; frames<170; frames++) + { + screen->fill(0); + for(int j=0; j<10; j++) + for(int i=0; i<25; i++) + screen->draw(image, 2*frames+30*j, 20*i); + sdl.update(); + sdl.sleep(10); + } + return 0; +} diff --git a/rend2d/tests/sdlgl_move_test.cpp b/rend2d/tests/sdlgl_move_test.cpp new file mode 100644 index 0000000000..b769ee837d --- /dev/null +++ b/rend2d/tests/sdlgl_move_test.cpp @@ -0,0 +1,31 @@ +#include +#include + +using namespace std; + +#include "../servers/sdl_gl_driver.hpp" + +using namespace Mangle::Rend2D; + +int main() +{ + SDLGLDriver sdl; + + sdl.setVideoMode(640,480,0,false); + sdl.setWindowTitle("Testing 123"); + Sprite *screen = sdl.getScreen(); + const char* imgName = "tile1-blue.png"; + Sprite *image = sdl.loadImage(imgName); + + for(int frames=0; frames<170; frames++) + { + screen->fill(0); + for(int j=0; j<10; j++) + for(int i=0; i<25; i++) + screen->draw(image, 2*frames+30*j, 20*i); + sdl.update(); + sdl.sleep(5); + } + + return 0; +} From 32966b80cbdc0fc132f38391b2c365344174b63a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Wed, 8 Sep 2010 15:19:04 +0200 Subject: [PATCH 123/269] Fixed screen transformation --- rend2d/servers/sdl_gl_driver.cpp | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/rend2d/servers/sdl_gl_driver.cpp b/rend2d/servers/sdl_gl_driver.cpp index aadb6abce3..e2e829a7ad 100644 --- a/rend2d/servers/sdl_gl_driver.cpp +++ b/rend2d/servers/sdl_gl_driver.cpp @@ -207,36 +207,26 @@ void SDLGLDriver::updateNoSwap() glBegin( GL_QUADS ); + // Needed to move the screen into the right place + int diff = screen->h - realDisp->h; + // Bottom-left vertex (corner) - glTexCoord2i( 0, 0 ); - glVertex3f(0,0,0); + glTexCoord2i( 0, 1 ); + glVertex3f(0,diff,0); // Bottom-right vertex (corner) - glTexCoord2i( 1, 0 ); - glVertex3f( realDisp->w, 0, 0.f ); + glTexCoord2i( 1, 1 ); + glVertex3f( realDisp->w, diff, 0.f ); // Top-right vertex (corner) - glTexCoord2i( 1, 1 ); - glVertex3f( realDisp->w, realDisp->h, 0.f ); + glTexCoord2i( 1, 0 ); + glVertex3f( realDisp->w, screen->h, 0.f ); // Top-left vertex (corner) - glTexCoord2i( 0, 1 ); - glVertex3f( 0, realDisp->h, 0.f ); + glTexCoord2i( 0, 0 ); + glVertex3f( 0, screen->h, 0.f ); glEnd(); - /* - glBegin(GL_TRIANGLES); - glColor3ub(255, 0, 0); - glVertex2d(0, 0); - - glColor3ub(0, 255, 0); - glVertex2d(100,0); - - glColor3ub(0, 0, 255); - glVertex2d(50, 50); - glEnd(); - */ - glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); From 32a2d1650ccaf3fb478862922908bdf8d2123c6a Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 10 Sep 2010 10:57:30 +0200 Subject: [PATCH 124/269] Fixed OpenAL cloning bug --- sound/outputs/openal_out.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index dd2da29b31..4a3c875d7b 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -364,7 +364,7 @@ OpenAL_Sound::OpenAL_Sound(ALuint buf, int *ref, OpenAL_Factory *fact) { // Increase the reference count assert(ref != NULL); - *refCnt++; + (*refCnt)++; // Set up buffer bufferID[0] = buf; @@ -469,7 +469,7 @@ OpenAL_Sound::~OpenAL_Sound() owner->notifyDelete(this); // Decrease the reference counter - if((-- *refCnt) == 0) + if((-- (*refCnt)) == 0) { // We're the last owner. Delete the buffer(s) and the counter // itself. From 7f26ba2ba3aa27a2d6d740ab508b6687ea012827 Mon Sep 17 00:00:00 2001 From: Jan Borsodi Date: Thu, 9 Sep 2010 23:25:11 +0200 Subject: [PATCH 125/269] Fix for Visual Studio 2008. stdint.h is not bundled with VS 2008, instead we use cstdint.hpp from BOOST. --- sound/outputs/openal_out.cpp | 5 +++-- sound/source.hpp | 4 +++- sound/sources/audiere_source.cpp | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index 4a3c875d7b..c0cae640e0 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -55,8 +55,9 @@ static void checkALError(const char *where) static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) { - int ch, bits; - inp->getInfo(&rate, &ch, &bits); + int32_t rate_, ch, bits; + inp->getInfo(&rate_, &ch, &bits); + rate = rate_; fmt = 0; diff --git a/sound/source.hpp b/sound/source.hpp index 8d2a801bcb..fbe7cf958b 100644 --- a/sound/source.hpp +++ b/sound/source.hpp @@ -2,7 +2,7 @@ #define MANGLE_SOUND_SOURCE_H #include -#include +#include #include #include "../stream/stream.hpp" @@ -10,6 +10,8 @@ namespace Mangle { namespace Sound { +typedef boost::int32_t int32_t; + /// A stream containing raw sound data and information about the format class SampleSource : public Stream::Stream { diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 9b97165be9..4e5b6adce2 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -17,7 +17,10 @@ using namespace Mangle::Sound; void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) { SampleFormat fmt; - sample->getFormat(*channels, *rate, fmt); + int channels_, rate_; + sample->getFormat(channels_, rate_, fmt); + *channels = channels_; + *rate = rate_; if(bits) { if(fmt == SF_U8) From 7a22068da611235190fa7343ca3f8c1d1944a68c Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 10 Sep 2010 13:57:35 +0200 Subject: [PATCH 126/269] Fixed conflict in last commit --- sound/outputs/openal_out.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index c0cae640e0..d0ce47718c 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -55,7 +55,7 @@ static void checkALError(const char *where) static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate) { - int32_t rate_, ch, bits; + boost::int32_t rate_, ch, bits; inp->getInfo(&rate_, &ch, &bits); rate = rate_; From 7345f2307f3ce6682a4044b98a811fac2cb7c4f0 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 13 Sep 2010 10:52:26 +0200 Subject: [PATCH 127/269] Improved OpenAL includes. Hacked std_stream to make seek() able to recover from eof(). --- sound/outputs/openal_out.cpp | 12 ++++++++++-- stream/servers/std_stream.hpp | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index d0ce47718c..bce08d02f9 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -4,8 +4,16 @@ #include "../../stream/filters/buffer_stream.hpp" -#include "al.h" -#include "alc.h" +#ifdef _WIN32 +#include +#include +#elif defined(__APPLE__) +#include +#include +#else +#include +#include +#endif using namespace Mangle::Sound; diff --git a/stream/servers/std_stream.hpp b/stream/servers/std_stream.hpp index 68aca621ba..163f023f60 100644 --- a/stream/servers/std_stream.hpp +++ b/stream/servers/std_stream.hpp @@ -36,6 +36,7 @@ class StdStream : public Stream void seek(size_t pos) { + inf->clear(); inf->seekg(pos); if(inf->fail()) fail("seek error"); From 593ac7b240a517d8412fc806cce9680f924ab5f0 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sun, 19 Sep 2010 11:57:09 +0200 Subject: [PATCH 128/269] Added pixel() to Rend2D::Sprite --- rend2d/servers/sdl_driver.cpp | 32 ++++++++++++++++++++++++++++++++ rend2d/servers/sdl_driver.hpp | 3 +++ rend2d/sprite.hpp | 5 +++++ 3 files changed, 40 insertions(+) diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp index 89c1c79c21..d86cf53e4d 100644 --- a/rend2d/servers/sdl_driver.cpp +++ b/rend2d/servers/sdl_driver.cpp @@ -7,6 +7,38 @@ using namespace Mangle::Rend2D; +// This is a really crappy and slow implementation +void SDL_Sprite::pixel(int x, int y, int color) +{ + SDL_LockSurface(surface); + + int bpp = surface->format->BytesPerPixel; + char *p = (char*)surface->pixels + y*surface->pitch + x*bpp; + + switch(bpp) + { + case 1: *p = color; break; + case 3: + if(SDL_BYTEORDER == SDL_BIG_ENDIAN) + { + p[0] = (color >> 16) & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = color & 0xff; + } + else + { + p[0] = color & 0xff; + p[1] = (color >> 8) & 0xff; + p[2] = (color >> 16) & 0xff; + } + break; + case 4: + *(int*)p = color; + break; + } + SDL_UnlockSurface(surface); +} + void SDL_Sprite::draw(Sprite *s, // Must be SDL_Sprite int x, int y, // Destination position int sx, int sy, // Source position diff --git a/rend2d/servers/sdl_driver.hpp b/rend2d/servers/sdl_driver.hpp index 9aeed2f926..4cab45c2e8 100644 --- a/rend2d/servers/sdl_driver.hpp +++ b/rend2d/servers/sdl_driver.hpp @@ -34,6 +34,9 @@ namespace Mangle // Fill with a given pixel value void fill(int value); + // Set one pixel + void pixel(int x, int y, int value); + private: // The SDL surface SDL_Surface* surface; diff --git a/rend2d/sprite.hpp b/rend2d/sprite.hpp index 150d97e4e7..7f6b9a2206 100644 --- a/rend2d/sprite.hpp +++ b/rend2d/sprite.hpp @@ -28,6 +28,11 @@ namespace Mangle /// Fill the sprite with the given pixel value. The pixel format /// depends on the format of the sprite. virtual void fill(int value) = 0; + + /// Set one pixel value. The pixel format depends on the sprite + /// format. This is not expected to be fast, and in some + /// implementations may not work at all. + virtual void pixel(int x, int y, int value) {} }; } } From a05046026ec9edb1e528fac2c70f887239302237 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Sun, 19 Sep 2010 12:36:19 +0200 Subject: [PATCH 129/269] More direct pixel functions. --- rend2d/servers/sdl_driver.cpp | 30 +++++++++++++++++++++++++++++- rend2d/servers/sdl_driver.hpp | 6 ++++++ rend2d/sprite.hpp | 18 ++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/rend2d/servers/sdl_driver.cpp b/rend2d/servers/sdl_driver.cpp index d86cf53e4d..aa1ff6c6dc 100644 --- a/rend2d/servers/sdl_driver.cpp +++ b/rend2d/servers/sdl_driver.cpp @@ -7,7 +7,34 @@ using namespace Mangle::Rend2D; -// This is a really crappy and slow implementation +const SpriteData *SDL_Sprite::lock() +{ + // Make sure we aren't already locked + assert(!data.pixels); + + // Lock the surface and set up the data structure + SDL_LockSurface(surface); + + data.pixels = surface->pixels; + data.w = surface->w; + data.h = surface->h; + data.pitch = surface->pitch; + data.bypp = surface->format->BytesPerPixel; + + return &data; +} + +void SDL_Sprite::unlock() +{ + if(data.pixels) + { + SDL_UnlockSurface(surface); + data.pixels = NULL; + } +} + +// This is a really crappy and slow implementation, only intended for +// testing purposes. Use lock/unlock for faster pixel drawing. void SDL_Sprite::pixel(int x, int y, int color) { SDL_LockSurface(surface); @@ -83,6 +110,7 @@ SDL_Sprite::SDL_Sprite(SDL_Surface *s, bool autoDelete) : surface(s), autoDel(autoDelete) { assert(surface != NULL); + data.pixels = NULL; } SDL_Sprite::~SDL_Sprite() diff --git a/rend2d/servers/sdl_driver.hpp b/rend2d/servers/sdl_driver.hpp index 4cab45c2e8..0f205ba34c 100644 --- a/rend2d/servers/sdl_driver.hpp +++ b/rend2d/servers/sdl_driver.hpp @@ -37,10 +37,16 @@ namespace Mangle // Set one pixel void pixel(int x, int y, int value); + const SpriteData *lock(); + void unlock(); + private: // The SDL surface SDL_Surface* surface; + // Used for locking + SpriteData data; + // If true, delete this surface when the canvas is destructed bool autoDel; }; diff --git a/rend2d/sprite.hpp b/rend2d/sprite.hpp index 7f6b9a2206..f49da6cb6d 100644 --- a/rend2d/sprite.hpp +++ b/rend2d/sprite.hpp @@ -5,6 +5,17 @@ namespace Mangle { namespace Rend2D { + /** + A pointer to sprite data for direct drawing. Only to be used + while the corresponding sprite is locked. + */ + struct SpriteData + { + void *pixels; // Pixel data + int w, h; // Width and height + int pitch, bypp; // Pitch (bytes) and bytes per pixel + }; + /** A Sprite is either a bitmap to be drawn or an output of area for blitting other bitmaps, or both. They are created by the @@ -33,6 +44,13 @@ namespace Mangle /// format. This is not expected to be fast, and in some /// implementations may not work at all. virtual void pixel(int x, int y, int value) {} + + /// Lock sprite for direct drawing, and return a struct + /// containing the necessary pointer. When finished, unlock the + /// sprite with unlock(). May return NULL, if so then direct + /// drawing is not possible. + virtual const SpriteData *lock() { return NULL; } + virtual void unlock() {} }; } } From 3a17aaa6976cd4c3d57dfd236bbdf689bc60d770 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Mon, 27 Sep 2010 18:03:10 +0200 Subject: [PATCH 130/269] Another mangle update --- mangle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mangle b/mangle index 2407f21c47..a05046026e 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit 2407f21c47abe218b9347df0fc9e8e9aebc02e3f +Subproject commit a05046026ec9edb1e528fac2c70f887239302237 From 12e5c8d704b959c78a62931b2360d18092b82c3d Mon Sep 17 00:00:00 2001 From: Yuri Krupenin Date: Sat, 8 Jan 2011 23:42:51 +0300 Subject: [PATCH 131/269] Fix for audiere 1.9.4 (last non-SVN version). audiere.h uses cstring routines without including itself, this workaround should fix possible build errors. --- sound/sources/audiere_source.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/sources/audiere_source.hpp b/sound/sources/audiere_source.hpp index d0189b710e..d797c55c86 100644 --- a/sound/sources/audiere_source.hpp +++ b/sound/sources/audiere_source.hpp @@ -3,6 +3,10 @@ #include "sample_reader.hpp" +// audiere.h from 1.9.4 (latest) release uses +// cstring routines like strchr() and strlen() without +// including cstring itself. +#include #include namespace Mangle { From 7185eab18c29a0a5e03e44690d6bd8ece7e3792b Mon Sep 17 00:00:00 2001 From: Jan-Peter Nilsson Date: Tue, 4 Jan 2011 01:03:05 +0100 Subject: [PATCH 132/269] Make configuration and log paths configurable --- gui/manager.cpp | 10 +++++++--- gui/manager.hpp | 6 +++--- ogre/renderer.cpp | 27 +++++++++++++++++++++++++-- ogre/renderer.hpp | 13 +++++++++++++ 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/gui/manager.cpp b/gui/manager.cpp index 564a33cfed..b606d7d16f 100644 --- a/gui/manager.cpp +++ b/gui/manager.cpp @@ -6,7 +6,7 @@ using namespace OEngine::GUI; -void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging) +void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) { assert(wnd); assert(mgr); @@ -21,13 +21,17 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool LogManager::initialise(); LogManager::setSTDOutputEnabled(logging); + std::string theLogFile = std::string(MYGUI_PLATFORM_LOG_FILENAME); + if(!logDir.empty()) + theLogFile.insert(0, logDir); + // Set up OGRE platform. We might make this more generic later. mPlatform = new OgrePlatform(); - mPlatform->initialise(wnd, mgr); + mPlatform->initialise(wnd, mgr, "General", theLogFile); // Create GUI mGui = new Gui(); - mGui->initialise(); + mGui->initialise("core.xml", theLogFile); } void MyGUIManager::shutdown() diff --git a/gui/manager.hpp b/gui/manager.hpp index e59b4b54bd..781685acfe 100644 --- a/gui/manager.hpp +++ b/gui/manager.hpp @@ -23,11 +23,11 @@ namespace GUI public: MyGUIManager() : mPlatform(NULL), mGui(NULL) {} - MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false) - { setup(wnd,mgr,logging); } + MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) + { setup(wnd,mgr,logging, logDir); } ~MyGUIManager() { shutdown(); } - void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false); + void setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")); void shutdown(); MyGUI::Gui *getGui() { return mGui; } diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index c60a29c77b..1773832d79 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -28,12 +28,20 @@ void OgreRenderer::screenshot(const std::string &file) } bool OgreRenderer::configure(bool showConfig, + const std::string &cfgPath, + const std::string &logPath, const std::string &pluginCfg, bool _logging) { + std::string theLogFile("Ogre.log"); + std::string theCfgFile("ogre.cfg"); + + theLogFile.insert(0, logPath); + theCfgFile.insert(0, cfgPath); + // Set up logging first new LogManager; - Log *log = LogManager::getSingleton().createLog("Ogre.log"); + Log *log = LogManager::getSingleton().createLog(theLogFile); logging = _logging; if(logging) @@ -43,7 +51,7 @@ bool OgreRenderer::configure(bool showConfig, // Disable logging log->setDebugOutputEnabled(false); - mRoot = new Root(pluginCfg, "ogre.cfg", ""); + mRoot = new Root(pluginCfg, theCfgFile, ""); // Show the configuration dialog and initialise the system, if the // showConfig parameter is specified. The settings are stored in @@ -58,6 +66,21 @@ bool OgreRenderer::configure(bool showConfig, return !result; } +bool OgreRenderer::configure(bool showConfig, + const std::string &cfgPath, + const std::string &pluginCfg, + bool _logging) +{ + return configure(showConfig, cfgPath, cfgPath, pluginCfg, _logging); +} + +bool OgreRenderer::configure(bool showConfig, + const std::string &pluginCfg, + bool _logging) +{ + return configure(showConfig, "", pluginCfg, _logging); +} + void OgreRenderer::createWindow(const std::string &title) { assert(mRoot); diff --git a/ogre/renderer.hpp b/ogre/renderer.hpp index f706b16f7e..59b69f6896 100644 --- a/ogre/renderer.hpp +++ b/ogre/renderer.hpp @@ -33,6 +33,19 @@ namespace Render : mRoot(NULL), mWindow(NULL), mScene(NULL) {} ~OgreRenderer() { cleanup(); } + /** Configure the renderer. This will load configuration files and + set up the Root and logging classes. */ + bool configure(bool showConfig, // Show config dialog box? + const std::string &cfgPath, // Path to directory where to store config files + const std::string &logPath, // Path to directory where to store log files + const std::string &pluginCfg, // plugin.cfg file + bool _logging); // Enable or disable logging + + bool configure(bool showConfig, // Show config dialog box? + const std::string &cfgPath, // Path to directory where to store config files + const std::string &pluginCfg, // plugin.cfg file + bool _logging); // Enable or disable logging + /** Configure the renderer. This will load configuration files and set up the Root and logging classes. */ bool configure(bool showConfig, // Show config dialog box? From c60a48b397b2077e9a81fdbf3fe31c83276f6ffa Mon Sep 17 00:00:00 2001 From: sergei Date: Sun, 13 Feb 2011 16:45:52 +0300 Subject: [PATCH 133/269] added getFPS() method to OgreRenderer Committer: sergoz --- ogre/renderer.cpp | 5 +++++ ogre/renderer.hpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index 1773832d79..9ce7a053f4 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -27,6 +27,11 @@ void OgreRenderer::screenshot(const std::string &file) mWindow->writeContentsToFile(file); } +float OgreRenderer::getFPS() +{ + return mWindow->getLastFPS(); +} + bool OgreRenderer::configure(bool showConfig, const std::string &cfgPath, const std::string &logPath, diff --git a/ogre/renderer.hpp b/ogre/renderer.hpp index 59b69f6896..982534e6b7 100644 --- a/ogre/renderer.hpp +++ b/ogre/renderer.hpp @@ -70,6 +70,8 @@ namespace Render /// Write a screenshot to file void screenshot(const std::string &file); + float getFPS(); + /// Get the Root Ogre::Root *getRoot() { return mRoot; } From adc1fa8e2ce1303a3fd612da77045cdcecf53587 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 22 Feb 2011 14:02:50 +0100 Subject: [PATCH 134/269] add physic support --- bullet/CMotionState.cpp | 45 ++ bullet/CMotionState.h | 52 ++ bullet/btKinematicCharacterController.cpp | 629 ++++++++++++++++++++++ bullet/btKinematicCharacterController.h | 164 ++++++ bullet/physic.cpp | 275 ++++++++++ bullet/physic.hpp | 190 +++++++ 6 files changed, 1355 insertions(+) create mode 100644 bullet/CMotionState.cpp create mode 100644 bullet/CMotionState.h create mode 100644 bullet/btKinematicCharacterController.cpp create mode 100644 bullet/btKinematicCharacterController.h create mode 100644 bullet/physic.cpp create mode 100644 bullet/physic.hpp diff --git a/bullet/CMotionState.cpp b/bullet/CMotionState.cpp new file mode 100644 index 0000000000..9cb662caa4 --- /dev/null +++ b/bullet/CMotionState.cpp @@ -0,0 +1,45 @@ +#include "CMotionState.h" +#include "physic.hpp" + +#include +#include +#include +//#include + +namespace OEngine { +namespace Physic +{ + + CMotionState::CMotionState(PhysicEngine* eng,std::string name) + { + pEng = eng; + tr.setIdentity(); + pName = name; + }; + + void CMotionState::getWorldTransform(btTransform &worldTrans) const + { + worldTrans = tr; + } + + void CMotionState::setWorldTransform(const btTransform &worldTrans) + { + tr = worldTrans; + + PhysicEvent evt; + evt.isNPC = isNPC; + evt.isPC = isPC; + evt.newTransform = tr; + evt.RigidBodyName = pName; + + if(isPC) + { + pEng->PEventList.push_back(evt); + } + else + { + pEng->NPEventList.push_back(evt); + } + } + +}} \ No newline at end of file diff --git a/bullet/CMotionState.h b/bullet/CMotionState.h new file mode 100644 index 0000000000..9ccc35adbb --- /dev/null +++ b/bullet/CMotionState.h @@ -0,0 +1,52 @@ +#ifndef OENGINE_CMOTIONSTATE_H +#define OENGINE_CMOTIONSTATE_H + +#include +#include + +namespace OEngine { +namespace Physic +{ + class PhysicEngine; + + /** + *A CMotionState is associated with a single RigidBody. + *When the RigidBody is moved by bullet, bullet will call the function setWorldTransform. + *for more info, see the bullet Wiki at btMotionState. + */ + class CMotionState:public btMotionState + { + public: + + CMotionState(PhysicEngine* eng,std::string name); + + /** + *Return the position of the RigidBody. + */ + virtual void getWorldTransform(btTransform &worldTrans) const; + + /** + *Function called by bullet when the RigidBody is moved. + *It add an event to the EventList of the PhysicEngine class. + */ + virtual void setWorldTransform(const btTransform &worldTrans); + + protected: + PhysicEngine* pEng; + btTransform tr; + bool isNPC; + bool isPC; + + std::string pName; + }; + + struct PhysicEvent + { + bool isNPC; + bool isPC; + btTransform newTransform; + std::string RigidBodyName; + }; + +}} +#endif \ No newline at end of file diff --git a/bullet/btKinematicCharacterController.cpp b/bullet/btKinematicCharacterController.cpp new file mode 100644 index 0000000000..8c81d5a42c --- /dev/null +++ b/bullet/btKinematicCharacterController.cpp @@ -0,0 +1,629 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "LinearMath/btIDebugDraw.h" +#include "BulletCollision/CollisionDispatch/btGhostObject.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +#include "LinearMath/btDefaultMotionState.h" +#include "btKinematicCharacterController.h" + +#include +///@todo Interact with dynamic objects, +///Ride kinematicly animated platforms properly +///Support ducking +class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback +{ +public: + btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + { + m_me[0] = me; + count = 1; + } + + btKinematicClosestNotMeRayResultCallback (btCollisionObject* me[], int count_) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + { + count = count_; + + for(int i = 0; i < count; i++) + m_me[i] = me[i]; + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) + { + for(int i = 0; i < count; i++) + if (rayResult.m_collisionObject == m_me[i]) + return 1.0; + + return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); + } +protected: + btCollisionObject* m_me[10]; + int count; +}; + +class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback +{ +public: + btKinematicClosestNotMeConvexResultCallback( btCollisionObject* me, const btVector3& up, btScalar minSlopeDot ) + : btCollisionWorld::ClosestConvexResultCallback( btVector3( 0.0, 0.0, 0.0 ), btVector3( 0.0, 0.0, 0.0 ) ), + m_me( me ), m_up( up ), m_minSlopeDot( minSlopeDot ) + { + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if( convexResult.m_hitCollisionObject == m_me ) + return btScalar( 1 ); + + btVector3 hitNormalWorld; + if( normalInWorldSpace ) + { + hitNormalWorld = convexResult.m_hitNormalLocal; + } + else + { + ///need to transform normal into worldspace + hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; + } + + // NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box... + + btScalar dotUp = m_up.dot(hitNormalWorld); + if( dotUp < m_minSlopeDot ) + return btScalar( 1 ); + + return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace); + } + +protected: + btCollisionObject* m_me; + const btVector3 m_up; + btScalar m_minSlopeDot; +}; + + + +btKinematicCharacterController::btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject_, + btPairCachingGhostObject* internalGhostObject_, + btScalar stepHeight, + btScalar constantScale, + btScalar gravity, + btScalar fallVelocity, + btScalar jumpVelocity, + btScalar recoveringFactor ) +{ + m_upAxis = btKinematicCharacterController::Y_AXIS; + + m_walkDirection.setValue( btScalar( 0 ), btScalar( 0 ), btScalar( 0 ) ); + + m_useGhostObjectSweepTest = true; + + externalGhostObject = externalGhostObject_; + internalGhostObject = internalGhostObject_; + + m_recoveringFactor = recoveringFactor; + + m_stepHeight = stepHeight; + + m_useWalkDirection = true; // use walk direction by default, legacy behavior + m_velocityTimeInterval = btScalar( 0 ); + m_verticalVelocity = btScalar( 0 ); + m_verticalOffset = btScalar( 0 ); + + m_gravity = constantScale * gravity; + m_fallSpeed = constantScale * fallVelocity; // Terminal velocity of a sky diver in m/s. + + m_jumpSpeed = constantScale * jumpVelocity; // ? + m_wasJumping = false; + + setMaxSlope( btRadians( 45.0 ) ); +} + + +btKinematicCharacterController::~btKinematicCharacterController () +{ +} + + +bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld ) +{ + //std::cout << "recover!!!!"; + bool penetration = false; + + collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(), + collisionWorld->getDispatchInfo(), + collisionWorld->getDispatcher() ); + + btVector3 currentPosition = internalGhostObject->getWorldTransform().getOrigin(); + + btScalar maxPen = btScalar( 0 ); + + for( int i = 0; i < internalGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ ) + { + m_manifoldArray.resize(0); + + btBroadphasePair* collisionPair = &internalGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; + + if( collisionPair->m_algorithm ) + collisionPair->m_algorithm->getAllContactManifolds( m_manifoldArray ); + + + for( int j = 0; j < m_manifoldArray.size(); j++ ) + { + btPersistentManifold* manifold = m_manifoldArray[j]; + + btScalar directionSign = manifold->getBody0() == internalGhostObject ? btScalar( -1.0 ) : btScalar( 1.0 ); + + for( int p = 0; p < manifold->getNumContacts(); p++ ) + { + const btManifoldPoint&pt = manifold->getContactPoint( p ); + if(manifold->getBody1() == externalGhostObject) std::cout << "external!!"; + if(manifold->getBody0() == externalGhostObject) std::cout << "external!!"; + if(manifold->getBody1() == internalGhostObject) std::cout << "internal!!"; + if(manifold->getBody0() == internalGhostObject) std::cout << "internal!!"; + if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject) + ||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) ) + { + } + else + { + btScalar dist = pt.getDistance(); + + if( dist < 0.0 ) + { + if( dist < maxPen ) + maxPen = dist; + + // NOTE : btScalar affects the stairs but the parkinson... + // 0.0 , the capsule can break the walls... + currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor; + + penetration = true; + std::cout << "recover!!!!"; + } + } + } + + // ??? + //manifold->clearManifold(); + } + } + + btTransform transform = internalGhostObject->getWorldTransform(); + + transform.setOrigin( currentPosition ); + + internalGhostObject->setWorldTransform( transform ); + externalGhostObject->setWorldTransform( transform ); + + return penetration; +} + + +btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const btVector3& currentPosition, btScalar& currentStepOffset ) +{ + btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) ); + + // Retrieve the collision shape + // + btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); + btAssert( collisionShape->isConvex() ); + + btConvexShape* convexShape = ( btConvexShape* )collisionShape; + + // FIXME: Handle penetration properly + // + btTransform start; + start.setIdentity(); + start.setOrigin( currentPosition + getUpAxisDirections()[ m_upAxis ] * ( convexShape->getMargin() ) ); + + btTransform end; + end.setIdentity(); + end.setOrigin( targetPosition ); + + btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, -getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); + callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + + // Sweep test + // + if( m_useGhostObjectSweepTest ) + externalGhostObject->convexSweepTest( convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration ); + + else + world->convexSweepTest( convexShape, start, end, callback ); + + if( callback.hasHit() ) + { + // Only modify the position if the hit was a slope and not a wall or ceiling. + // + if( callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > btScalar( 0.0 ) ) + { + // We moved up only a fraction of the step height + // + currentStepOffset = m_stepHeight * callback.m_closestHitFraction; + + return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); + } + + m_verticalVelocity = btScalar( 0.0 ); + m_verticalOffset = btScalar( 0.0 ); + + return currentPosition; + } + else + { + currentStepOffset = m_stepHeight; + return targetPosition; + } +} + + +///Reflect the vector d around the vector r +inline btVector3 reflect( const btVector3& d, const btVector3& r ) +{ + return d - ( btScalar( 2.0 ) * d.dot( r ) ) * r; +} + + +///Project a vector u on another vector v +inline btVector3 project( const btVector3& u, const btVector3& v ) +{ + return v * u.dot( v ); +} + + +///Helper for computing the character sliding +inline btVector3 slide( const btVector3& direction, const btVector3& planeNormal ) +{ + return direction - project( direction, planeNormal ); +} + + + +btVector3 slideOnCollision( const btVector3& fromPosition, const btVector3& toPosition, const btVector3& hitNormal ) +{ + btVector3 moveDirection = toPosition - fromPosition; + btScalar moveLength = moveDirection.length(); + + if( moveLength <= btScalar( SIMD_EPSILON ) ) + return toPosition; + + moveDirection.normalize(); + + btVector3 reflectDir = reflect( moveDirection, hitNormal ); + reflectDir.normalize(); + + return fromPosition + slide( reflectDir, hitNormal ) * moveLength; +} + + +btVector3 btKinematicCharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove ) +{ + // We go to ! + // + btVector3 targetPosition = currentPosition + walkMove; + + // Retrieve the collision shape + // + btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); + btAssert( collisionShape->isConvex() ); + + btConvexShape* convexShape = ( btConvexShape* )collisionShape; + + btTransform start; + start.setIdentity(); + + btTransform end; + end.setIdentity(); + + btScalar fraction = btScalar( 1.0 ); + + // This optimization scheme suffers in the corners. + // It basically jumps from a wall to another, then fails to find a new + // position (after 4 iterations here) and finally don't move at all. + // + // The stepping algorithm adds some problems with stairs. It seems + // the treads create some fake corner using capsules for collisions. + // + for( int i = 0; i < 4 && fraction > btScalar( 0.01 ); i++ ) + { + start.setOrigin( currentPosition ); + end.setOrigin( targetPosition ); + + btVector3 sweepDirNegative = currentPosition - targetPosition; + + btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, sweepDirNegative, btScalar( 0.0 ) ); + callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + + if( m_useGhostObjectSweepTest ) + externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + + else + collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + + if( callback.hasHit() ) + { + // Try another target position + // + targetPosition = slideOnCollision( currentPosition, targetPosition, callback.m_hitNormalWorld ); + fraction = callback.m_closestHitFraction; + } + else + + // Move to the valid target position + // + return targetPosition; + } + + // Don't move if you can't find a valid target position... + // It prevents some flickering. + // + return currentPosition; +} + + +///Handle the gravity +btScalar btKinematicCharacterController::addFallOffset( bool wasOnGround, btScalar currentStepOffset, btScalar dt ) +{ + btScalar downVelocity = ( m_verticalVelocity < 0.0 ? -m_verticalVelocity : btScalar( 0.0 ) ) * dt; + + if( downVelocity > btScalar( 0.0 ) && downVelocity < m_stepHeight && ( wasOnGround || !m_wasJumping ) ) + downVelocity = m_stepHeight; + + return currentStepOffset + downVelocity; +} + + +btVector3 btKinematicCharacterController::stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset ) +{ + btVector3 stepDrop = getUpAxisDirections()[ m_upAxis ] * currentStepOffset; + + // Be sure we are falling from the last m_currentPosition + // It prevents some flickering + // + btVector3 targetPosition = currentPosition - stepDrop; + + btTransform start; + start.setIdentity(); + start.setOrigin( currentPosition ); + + btTransform end; + end.setIdentity(); + end.setOrigin( targetPosition ); + + btKinematicClosestNotMeConvexResultCallback callback( internalGhostObject, getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); + callback.m_collisionFilterGroup = internalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = internalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + + // Retrieve the collision shape + // + btCollisionShape* collisionShape = internalGhostObject->getCollisionShape(); + btAssert( collisionShape->isConvex() ); + btConvexShape* convexShape = ( btConvexShape* )collisionShape; + + if( m_useGhostObjectSweepTest ) + externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + + else + collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + + if( callback.hasHit() ) + { + m_verticalVelocity = btScalar( 0.0 ); + m_verticalOffset = btScalar( 0.0 ); + m_wasJumping = false; + + // We dropped a fraction of the height -> hit floor + // + return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); + } + else + + // We dropped the full height + // + return targetPosition; +} + + + +void btKinematicCharacterController::setWalkDirection( const btVector3& walkDirection ) +{ + m_useWalkDirection = true; + m_walkDirection = walkDirection; +} + + +void btKinematicCharacterController::setVelocityForTimeInterval( const btVector3& velocity, btScalar timeInterval ) +{ + m_useWalkDirection = false; + m_walkDirection = velocity; + m_velocityTimeInterval = timeInterval; +} + + +void btKinematicCharacterController::reset() +{ +} + + +void btKinematicCharacterController::warp( const btVector3& origin ) +{ + btTransform transform; + transform.setIdentity(); + transform.setOrigin( -origin ); + + externalGhostObject->setWorldTransform( transform ); + internalGhostObject->setWorldTransform( transform ); +} + + +void btKinematicCharacterController::preStep( btCollisionWorld* collisionWorld ) +{ + BT_PROFILE( "preStep" ); + + for( int i = 0; i < 4 && recoverFromPenetration ( collisionWorld ); i++ ); +} + + +void btKinematicCharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt ) +{ + BT_PROFILE( "playerStep" ); + + if( !m_useWalkDirection && m_velocityTimeInterval <= btScalar( 0.0 ) ) + return; + + bool wasOnGround = onGround(); + + // Handle the gravity + // + m_verticalVelocity -= m_gravity * dt; + + if( m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed ) + m_verticalVelocity = m_jumpSpeed; + + if( m_verticalVelocity < 0.0 && btFabs( m_verticalVelocity ) > btFabs( m_fallSpeed ) ) + m_verticalVelocity = -btFabs( m_fallSpeed ); + + m_verticalOffset = m_verticalVelocity * dt; + + // This forced stepping up can cause problems when the character + // walks (jump in fact...) under too low ceilings. + // + btVector3 currentPosition = externalGhostObject->getWorldTransform().getOrigin(); + btScalar currentStepOffset; + + currentPosition = stepUp( collisionWorld, currentPosition, currentStepOffset ); + + // Move in the air and slide against the walls ignoring the stair steps. + // + if( m_useWalkDirection ) + currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, m_walkDirection ); + + else + { + btScalar dtMoving = ( dt < m_velocityTimeInterval ) ? dt : m_velocityTimeInterval; + m_velocityTimeInterval -= dt; + + // How far will we move while we are moving ? + // + btVector3 moveDirection = m_walkDirection * dtMoving; + + currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, moveDirection ); + } + + // Finally find the ground. + // + currentStepOffset = addFallOffset( wasOnGround, currentStepOffset, dt ); + + currentPosition = stepDown( collisionWorld, currentPosition, currentStepOffset ); + + // Apply the new position to the collision objects. + // + btTransform tranform; + tranform = externalGhostObject->getWorldTransform(); + tranform.setOrigin( currentPosition ); + + externalGhostObject->setWorldTransform( tranform ); + internalGhostObject->setWorldTransform( tranform ); +} + + +void btKinematicCharacterController::setFallSpeed( btScalar fallSpeed ) +{ + m_fallSpeed = fallSpeed; +} + + +void btKinematicCharacterController::setJumpSpeed( btScalar jumpSpeed ) +{ + m_jumpSpeed = jumpSpeed; +} + + +void btKinematicCharacterController::setMaxJumpHeight( btScalar maxJumpHeight ) +{ + m_maxJumpHeight = maxJumpHeight; +} + + +bool btKinematicCharacterController::canJump() const +{ + return onGround(); +} + + +void btKinematicCharacterController::jump() +{ + if( !canJump() ) + return; + + m_verticalVelocity = m_jumpSpeed; + m_wasJumping = true; +} + + +void btKinematicCharacterController::setGravity( btScalar gravity ) +{ + m_gravity = gravity; +} + + +btScalar btKinematicCharacterController::getGravity() const +{ + return m_gravity; +} + + +void btKinematicCharacterController::setMaxSlope( btScalar slopeRadians ) +{ + m_maxSlopeRadians = slopeRadians; + m_maxSlopeCosine = btCos( slopeRadians ); +} + + +btScalar btKinematicCharacterController::getMaxSlope() const +{ + return m_maxSlopeRadians; +} + + +bool btKinematicCharacterController::onGround() const +{ + return btFabs( m_verticalVelocity ) < btScalar( SIMD_EPSILON ) && + btFabs( m_verticalOffset ) < btScalar( SIMD_EPSILON ); +} + + +btVector3* btKinematicCharacterController::getUpAxisDirections() +{ + static btVector3 sUpAxisDirection[] = + { + btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 0.0 ) ), + btVector3( btScalar( 0.0 ), btScalar( 1.0 ), btScalar( 0.0 ) ), + btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 1.0 ) ) + }; + + return sUpAxisDirection; +} + + +void btKinematicCharacterController::debugDraw( btIDebugDraw* debugDrawer ) +{ +} diff --git a/bullet/btKinematicCharacterController.h b/bullet/btKinematicCharacterController.h new file mode 100644 index 0000000000..8f32b32e98 --- /dev/null +++ b/bullet/btKinematicCharacterController.h @@ -0,0 +1,164 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef KINEMATIC_CHARACTER_CONTROLLER_H +#define KINEMATIC_CHARACTER_CONTROLLER_H + +#include "LinearMath/btVector3.h" +#include "LinearMath\btQuickprof.h" + +#include "BulletDynamics\Character\btCharacterControllerInterface.h" + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + + +class btCollisionShape; +class btRigidBody; +class btCollisionWorld; +class btCollisionDispatcher; +class btPairCachingGhostObject; + +///btKinematicCharacterController is an object that supports a sliding motion in a world. +///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations. +///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user. +class btKinematicCharacterController : public btCharacterControllerInterface +{ +public: + enum UpAxis + { + X_AXIS = 0, + Y_AXIS = 1, + Z_AXIS = 2 + }; + +private: + btPairCachingGhostObject* externalGhostObject; // use this for querying collisions for sliding and move + btPairCachingGhostObject* internalGhostObject; // and this for recoreving from penetrations + + btScalar m_verticalVelocity; + btScalar m_verticalOffset; + btScalar m_fallSpeed; + btScalar m_jumpSpeed; + btScalar m_maxJumpHeight; + btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) + btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) + btScalar m_gravity; + btScalar m_recoveringFactor; + + btScalar m_stepHeight; + + ///this is the desired walk direction, set by the user + btVector3 m_walkDirection; + + ///keep track of the contact manifolds + btManifoldArray m_manifoldArray; + + ///Gravity attributes + bool m_wasJumping; + + bool m_useGhostObjectSweepTest; + bool m_useWalkDirection; + btScalar m_velocityTimeInterval; + + UpAxis m_upAxis; + + static btVector3* getUpAxisDirections(); + + bool recoverFromPenetration ( btCollisionWorld* collisionWorld ); + + btVector3 stepUp( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar& currentStepOffset ); + btVector3 stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove ); + btScalar addFallOffset( bool wasJumping, btScalar currentStepOffset, btScalar dt ); + btVector3 stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset ); + +public: + /// externalGhostObject is used for querying the collisions for sliding along the wall, + /// and internalGhostObject is used for querying the collisions for recovering from large penetrations. + /// These parameters can point on the same object. + /// Using a smaller internalGhostObject can help for removing some flickering but create some + /// stopping artefacts when sliding along stairs or small walls. + /// Don't forget to scale gravity and fallSpeed if you scale the world. + btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject, + btPairCachingGhostObject* internalGhostObject, + btScalar stepHeight, + btScalar constantScale = btScalar( 1.0 ), + btScalar gravity = btScalar( 9.8 ), + btScalar fallVelocity = btScalar( 55.0 ), + btScalar jumpVelocity = btScalar( 9.8 ), + btScalar recoveringFactor = btScalar( 0.2 ) ); + + ~btKinematicCharacterController (); + + + ///btActionInterface interface + virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime ) + { + preStep( collisionWorld ); + playerStep( collisionWorld, deltaTime ); + } + + ///btActionInterface interface + void debugDraw( btIDebugDraw* debugDrawer ); + + void setUpAxis( UpAxis axis ) + { + m_upAxis = axis; + } + + /// This should probably be called setPositionIncrementPerSimulatorStep. + /// This is neither a direction nor a velocity, but the amount to + /// increment the position each simulation iteration, regardless + /// of dt. + /// This call will reset any velocity set by setVelocityForTimeInterval(). + virtual void setWalkDirection(const btVector3& walkDirection); + + /// Caller provides a velocity with which the character should move for + /// the given time period. After the time period, velocity is reset + /// to zero. + /// This call will reset any walk direction set by setWalkDirection(). + /// Negative time intervals will result in no motion. + virtual void setVelocityForTimeInterval(const btVector3& velocity, + btScalar timeInterval); + + void reset(); + void warp( const btVector3& origin ); + + void preStep( btCollisionWorld* collisionWorld ); + void playerStep( btCollisionWorld* collisionWorld, btScalar dt ); + + void setFallSpeed( btScalar fallSpeed ); + void setJumpSpeed( btScalar jumpSpeed ); + void setMaxJumpHeight( btScalar maxJumpHeight ); + bool canJump() const; + + void jump(); + + void setGravity( btScalar gravity ); + btScalar getGravity() const; + + /// The max slope determines the maximum angle that the controller can walk up. + /// The slope angle is measured in radians. + void setMaxSlope( btScalar slopeRadians ); + btScalar getMaxSlope() const; + + void setUseGhostSweepTest( bool useGhostObjectSweepTest ) + { + m_useGhostObjectSweepTest = useGhostObjectSweepTest; + } + + bool onGround() const; +}; + +#endif // KINEMATIC_CHARACTER_CONTROLLER_H diff --git a/bullet/physic.cpp b/bullet/physic.cpp new file mode 100644 index 0000000000..d5c504cfdd --- /dev/null +++ b/bullet/physic.cpp @@ -0,0 +1,275 @@ +#include "physic.hpp" +#include +#include +#include +//#include +#include "CMotionState.h" +#include "OgreRoot.h" +#include "btKinematicCharacterController.h" +#include "BtOgrePG.h" +#include "BtOgreGP.h" +#include "BtOgreExtras.h" + +#define BIT(x) (1<<(x)) + +namespace OEngine { +namespace Physic +{ + enum collisiontypes { + COL_NOTHING = 0, //setWorldTransform( transform ); + + btScalar externalCapsuleHeight = 50; + btScalar externalCapsuleWidth = 20; + + externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); + externalCollisionShape->setMargin( 1 ); + + externalGhostObject->setCollisionShape( externalCollisionShape ); + externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); + + // Internal capsule + internalGhostObject = new btPairCachingGhostObject(); + internalGhostObject->setWorldTransform( transform ); + //internalGhostObject->getBroadphaseHandle()->s + btScalar internalCapsuleHeight = 20; + btScalar internalCapsuleWidth = 5; + + internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); + internalCollisionShape->setMargin( 1 ); + + internalGhostObject->setCollisionShape( internalCollisionShape ); + internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); + + mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 0.4 ),1,0 ); + mCharacter->setUpAxis(btKinematicCharacterController::UpAxis::Z_AXIS); + } + + PhysicActor::~PhysicActor() + { + delete mCharacter; + delete internalGhostObject; + delete internalCollisionShape; + delete externalGhostObject; + delete externalCollisionShape; + } + + void PhysicActor::setWalkDirection(btVector3& mvt) + { + mCharacter->setWalkDirection( mvt ); + } + + void PhysicActor::Rotate(btQuaternion& quat) + { + externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat ); + internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat ); + } + + void PhysicActor::setRotation(btQuaternion& quat) + { + externalGhostObject->getWorldTransform().setRotation( quat ); + internalGhostObject->getWorldTransform().setRotation( quat ); + } + + btVector3 PhysicActor::getPosition(void) + { + return internalGhostObject->getWorldTransform().getOrigin(); + } + + btQuaternion PhysicActor::getRotation(void) + { + return internalGhostObject->getWorldTransform().getRotation(); + } + + void PhysicActor::setPosition(btVector3& pos) + { + internalGhostObject->getWorldTransform().setOrigin(pos); + externalGhostObject->getWorldTransform().setOrigin(pos); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name) + :btRigidBody(CI),mName(name) + { + + }; + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// + + + + PhysicEngine::PhysicEngine() + { + // Set up the collision configuration and dispatcher + collisionConfiguration = new btDefaultCollisionConfiguration(); + dispatcher = new btCollisionDispatcher(collisionConfiguration); + + // The actual physics solver + solver = new btSequentialImpulseConstraintSolver; + + //TODO: memory leak? + btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); + pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); + + broadphase = new btDbvtBroadphase(pairCache); + + // The world. + dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); + dynamicsWorld->setGravity(btVector3(0,0,-10)); + + if(BulletShapeManager::getSingletonPtr() == NULL) + { + new BulletShapeManager(); + } + //TODO:singleton? + ShapeLoader = new ManualBulletShapeLoader(); + + isDebugCreated = false; + } + + void PhysicEngine::createDebugRendering() + { + if(!isDebugCreated) + { + Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator(); + iter.begin(); + Ogre::SceneManager* scn = iter.getNext(); + Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode(); + node->pitch(Ogre::Degree(-90)); + mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); + dynamicsWorld->setDebugDrawer(mDebugDrawer); + isDebugCreated = true; + dynamicsWorld->debugDrawWorld(); + } + } + + void PhysicEngine::setDebugRenderingMode(int mode) + { + if(!isDebugCreated) + { + createDebugRendering(); + } + mDebugDrawer->setDebugMode(mode); + } + + PhysicEngine::~PhysicEngine() + { + delete dynamicsWorld; + delete solver; + delete collisionConfiguration; + delete dispatcher; + delete broadphase; + delete ShapeLoader; + } + + RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name) + { + //get the shape from the .nif + ShapeLoader->load(mesh,"General"); + BulletShapeManager::getSingletonPtr()->load(mesh,"General"); + BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); + + //create the motionState + CMotionState* newMotionState = new CMotionState(this,name); + + //create the real body + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape); + RigidBody* body = new RigidBody(CI,name); + + return body; + } + + void PhysicEngine::addRigidBody(RigidBody* body) + { + dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL); + body->setActivationState(DISABLE_DEACTIVATION); + RigidBodyMap[body->mName] = body; + } + + void PhysicEngine::removeRigidBody(std::string name) + { + RigidBody* body = RigidBodyMap[name]; + if(body != NULL) + { + dynamicsWorld->removeRigidBody(RigidBodyMap[name]); + } + } + + void PhysicEngine::deleteRigidBody(std::string name) + { + RigidBody* body = RigidBodyMap[name]; + if(body != NULL) + { + delete body; + RigidBodyMap[name] = NULL; + } + } + + RigidBody* PhysicEngine::getRigidBody(std::string name) + { + RigidBody* body = RigidBodyMap[name]; + return body; + } + + void PhysicEngine::stepSimulation(double deltaT) + { + dynamicsWorld->stepSimulation(deltaT,1,1/30.); + if(isDebugCreated) + { + mDebugDrawer->step(); + } + } + + void PhysicEngine::addCharacter(std::string name) + { + PhysicActor* newActor = new PhysicActor(name); + dynamicsWorld->addCollisionObject( newActor->externalGhostObject, COL_ACTOR_EXTERNAL, COL_WORLD |COL_ACTOR_EXTERNAL ); + dynamicsWorld->addCollisionObject( newActor->internalGhostObject, COL_ACTOR_INTERNAL, COL_WORLD |COL_ACTOR_INTERNAL ); + dynamicsWorld->addAction( newActor->mCharacter ); + PhysicActorMap[name] = newActor; + } + + void PhysicEngine::removeCharacter(std::string name) + { + PhysicActor* act = PhysicActorMap[name]; + if(act != NULL) + { + dynamicsWorld->removeCollisionObject(act->externalGhostObject); + dynamicsWorld->removeCollisionObject(act->internalGhostObject); + dynamicsWorld->removeAction(act->mCharacter); + delete act; + PhysicActorMap[name] = NULL; + } + } + + PhysicActor* PhysicEngine::getCharacter(std::string name) + { + PhysicActor* act = PhysicActorMap[name]; + return act; + } + + void PhysicEngine::emptyEventLists(void) + { + } +}}; \ No newline at end of file diff --git a/bullet/physic.hpp b/bullet/physic.hpp new file mode 100644 index 0000000000..5fdc03e7d3 --- /dev/null +++ b/bullet/physic.hpp @@ -0,0 +1,190 @@ +#ifndef OENGINE_BULLET_PHYSIC_H +#define OENGINE_BULLET_PHYSIC_H + +#include +#include "BulletCollision/CollisionDispatch/btGhostObject.h" +#include +#include +#include + +class btRigidBody; +class btBroadphaseInterface; +class btDefaultCollisionConfiguration; +class btSequentialImpulseConstraintSolver; +class btCollisionDispatcher; +class btDiscreteDynamicsWorld; +class btKinematicCharacterController; + +namespace BtOgre +{ + class DebugDrawer; +} + +class BulletShapeManager; +class ManualBulletShapeLoader; + +namespace MWWorld +{ + class World; +} + +namespace OEngine { +namespace Physic +{ + class CMotionState; + struct PhysicEvent; + + /** + *A physic Actor use a modifed KinematicCharacterController taken in the bullet forum. + */ + class PhysicActor + { + public: + PhysicActor(std::string name); + + ~PhysicActor(); + + /** + *This function set the walkDirection. This is not relative to the actor orientation. + *I think it's also needed to take time into account. A typical call should look like this: + *setWalkDirection( mvt * orientation * dt) + */ + void setWalkDirection(btVector3& mvt); + + void Rotate(btQuaternion& quat); + + void setRotation(btQuaternion& quat); + + btVector3 getPosition(void); + + btQuaternion getRotation(void); + + void setPosition(btVector3& pos); + + btKinematicCharacterController* mCharacter; + + btPairCachingGhostObject* internalGhostObject; + btCollisionShape* internalCollisionShape; + + btPairCachingGhostObject* externalGhostObject; + btCollisionShape* externalCollisionShape; + + std::string mName; + }; + + /** + *This class is just an extension of normal btRigidBody in order to add extra info. + *When bullet give back a btRigidBody, you can just do a static_cast to RigidBody, + *so one never should use btRigidBody directly! + */ + class RigidBody: public btRigidBody + { + public: + RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); + std::string mName; + }; + + /** + *The PhysicEngine class contain everything which is needed for Physic. + *It's needed that Ogre Resources are set up before the PhysicEngine is created. + *Note:deleting it WILL NOT delete the RigidBody! + *TODO:unload unused resources? + */ + class PhysicEngine + { + public: + PhysicEngine(); + ~PhysicEngine(); + + /** + *create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map, + *so you can get it with the getRigidBody function. + */ + RigidBody* createRigidBody(std::string mesh,std::string name); + + /** + *Add a RigidBody to the simulation + */ + void addRigidBody(RigidBody* body); + + /** + *Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. + */ + void removeRigidBody(std::string name); + + /** + *delete a RigidBody, and remove it from RigidBodyMap. + */ + void deleteRigidBody(std::string name); + + /** + *Return a pointer to a given rigid body. + *TODO:check if exist + */ + RigidBody* getRigidBody(std::string name); + + /** + *Create and add a character to the scene, and add it to the ActorMap. + */ + void addCharacter(std::string name); + + /** + *Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done? + */ + void removeCharacter(std::string name); + + /** + *return a pointer to a character + *TODO:check if the actor exist... + */ + PhysicActor* getCharacter(std::string name); + + /** + *This step the simulation of a given time. + */ + void stepSimulation(double deltaT); + + /** + *Empty events lists + */ + void emptyEventLists(void); + + /** + *Create a debug rendering. It is called by setDebgRenderingMode if it's not created yet. + *Important Note: this will crash if the Render is not yet initialise! + */ + void createDebugRendering(); + + /** + *Set the debug rendering mode. 0 to turn it off. + *Important Note: this will crash if the Render is not yet initialise! + */ + void setDebugRenderingMode(int mode); + + //event list of non player object + std::list NPEventList; + + //event list affecting the player + std::list PEventList; + + //Bullet Stuff + btBroadphaseInterface* broadphase; + btDefaultCollisionConfiguration* collisionConfiguration; + btSequentialImpulseConstraintSolver* solver; + btCollisionDispatcher* dispatcher; + btDiscreteDynamicsWorld* dynamicsWorld; + + //the NIF file loader. + ManualBulletShapeLoader* ShapeLoader; + + std::map RigidBodyMap; + std::map PhysicActorMap; + + //debug rendering + BtOgre::DebugDrawer* mDebugDrawer; + bool isDebugCreated; + }; + +}} + +#endif \ No newline at end of file From f071328d5535bfa061359d6e233c2a8c2fdb6d3b Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 22 Feb 2011 14:04:12 +0100 Subject: [PATCH 135/269] add debug rendering --- bullet/BtOgre.cpp | 1044 +++++++++++++++++++++++++++++++++++++++++ bullet/BtOgreExtras.h | 280 +++++++++++ bullet/BtOgrePG.h | 81 ++++ 3 files changed, 1405 insertions(+) create mode 100644 bullet/BtOgre.cpp create mode 100644 bullet/BtOgreExtras.h create mode 100644 bullet/BtOgrePG.h diff --git a/bullet/BtOgre.cpp b/bullet/BtOgre.cpp new file mode 100644 index 0000000000..82ed2cbc27 --- /dev/null +++ b/bullet/BtOgre.cpp @@ -0,0 +1,1044 @@ +/* + * ============================================================================================= + * + * Filename: BtOgre.cpp + * + * Description: BtOgre implementation. + * + * Version: 1.0 + * Created: 27/12/2008 01:47:56 PM + * + * Author: Nikhilesh (nikki) + * + * ============================================================================================= + */ + +#include "BtOgrePG.h" +#include "BtOgreGP.h" +#include "BtOgreExtras.h" + +using namespace Ogre; + +namespace BtOgre { + +/* + * ============================================================================================= + * BtOgre::VertexIndexToShape + * ============================================================================================= + */ + + void VertexIndexToShape::addStaticVertexData(const VertexData *vertex_data) + { + if (!vertex_data) + return; + + const VertexData *data = vertex_data; + + const unsigned int prev_size = mVertexCount; + mVertexCount += (unsigned int)data->vertexCount; + + Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount]; + if (mVertexBuffer) + { + memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); + delete[] mVertexBuffer; + } + mVertexBuffer = tmp_vert; + + // Get the positional buffer element + { + const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); + Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + + unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); + float* pReal; + Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; + const unsigned int vertexCount = (unsigned int)data->vertexCount; + for(unsigned int j = 0; j < vertexCount; ++j) + { + posElem->baseVertexPointerToElement(vertex, &pReal); + vertex += vSize; + + curVertices->x = (*pReal++); + curVertices->y = (*pReal++); + curVertices->z = (*pReal++); + + *curVertices = mTransform * (*curVertices); + + curVertices++; + } + vbuf->unlock(); + } + } + //------------------------------------------------------------------------------------------------ + void VertexIndexToShape::addAnimatedVertexData(const Ogre::VertexData *vertex_data, + const Ogre::VertexData *blend_data, + const Ogre::Mesh::IndexMap *indexMap) + { + // Get the bone index element + assert(vertex_data); + + const VertexData *data = blend_data; + const unsigned int prev_size = mVertexCount; + mVertexCount += (unsigned int)data->vertexCount; + Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount]; + if (mVertexBuffer) + { + memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); + delete[] mVertexBuffer; + } + mVertexBuffer = tmp_vert; + + // Get the positional buffer element + { + const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); + assert (posElem); + Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + + unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); + float* pReal; + Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; + const unsigned int vertexCount = (unsigned int)data->vertexCount; + for(unsigned int j = 0; j < vertexCount; ++j) + { + posElem->baseVertexPointerToElement(vertex, &pReal); + vertex += vSize; + + curVertices->x = (*pReal++); + curVertices->y = (*pReal++); + curVertices->z = (*pReal++); + + *curVertices = mTransform * (*curVertices); + + curVertices++; + } + vbuf->unlock(); + } + { + const Ogre::VertexElement* bneElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_BLEND_INDICES); + assert (bneElem); + + Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(bneElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); + + unsigned char* pBone; + + if (!mBoneIndex) + mBoneIndex = new BoneIndex(); + BoneIndex::iterator i; + + Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; + + const unsigned int vertexCount = (unsigned int)vertex_data->vertexCount; + for(unsigned int j = 0; j < vertexCount; ++j) + { + bneElem->baseVertexPointerToElement(vertex, &pBone); + vertex += vSize; + + const unsigned char currBone = (indexMap) ? (*indexMap)[*pBone] : *pBone; + i = mBoneIndex->find (currBone); + Vector3Array* l = 0; + if (i == mBoneIndex->end()) + { + l = new Vector3Array; + mBoneIndex->insert(BoneKeyIndex(currBone, l)); + } + else + { + l = i->second; + } + + l->push_back(*curVertices); + + curVertices++; + } + vbuf->unlock(); + } + } + //------------------------------------------------------------------------------------------------ + void VertexIndexToShape::addIndexData(IndexData *data, const unsigned int offset) + { + const unsigned int prev_size = mIndexCount; + mIndexCount += (unsigned int)data->indexCount; + + unsigned int* tmp_ind = new unsigned int[mIndexCount]; + if (mIndexBuffer) + { + memcpy (tmp_ind, mIndexBuffer, sizeof(unsigned int) * prev_size); + delete[] mIndexBuffer; + } + mIndexBuffer = tmp_ind; + + const unsigned int numTris = (unsigned int) data->indexCount / 3; + HardwareIndexBufferSharedPtr ibuf = data->indexBuffer; + const bool use32bitindexes = (ibuf->getType() == HardwareIndexBuffer::IT_32BIT); + unsigned int index_offset = prev_size; + + if (use32bitindexes) + { + const unsigned int* pInt = static_cast(ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); + for(unsigned int k = 0; k < numTris; ++k) + { + mIndexBuffer[index_offset ++] = offset + *pInt++; + mIndexBuffer[index_offset ++] = offset + *pInt++; + mIndexBuffer[index_offset ++] = offset + *pInt++; + } + ibuf->unlock(); + } + else + { + const unsigned short* pShort = static_cast(ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); + for(unsigned int k = 0; k < numTris; ++k) + { + mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); + mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); + mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); + } + ibuf->unlock(); + } + + } + //------------------------------------------------------------------------------------------------ + Real VertexIndexToShape::getRadius() + { + if (mBoundRadius == (-1)) + { + getSize(); + mBoundRadius = (std::max(mBounds.x,std::max(mBounds.y,mBounds.z)) * 0.5); + } + return mBoundRadius; + } + //------------------------------------------------------------------------------------------------ + Vector3 VertexIndexToShape::getSize() + { + const unsigned int vCount = getVertexCount(); + if (mBounds == Ogre::Vector3(-1,-1,-1) && vCount > 0) + { + + const Ogre::Vector3 * const v = getVertices(); + + Ogre::Vector3 vmin(v[0]); + Ogre::Vector3 vmax(v[0]); + + for(unsigned int j = 1; j < vCount; j++) + { + vmin.x = std::min(vmin.x, v[j].x); + vmin.y = std::min(vmin.y, v[j].y); + vmin.z = std::min(vmin.z, v[j].z); + + vmax.x = std::max(vmax.x, v[j].x); + vmax.y = std::max(vmax.y, v[j].y); + vmax.z = std::max(vmax.z, v[j].z); + } + + mBounds.x = vmax.x - vmin.x; + mBounds.y = vmax.y - vmin.y; + mBounds.z = vmax.z - vmin.z; + } + + return mBounds; + } + //------------------------------------------------------------------------------------------------ + const Ogre::Vector3* VertexIndexToShape::getVertices() + { + return mVertexBuffer; + } + //------------------------------------------------------------------------------------------------ + unsigned int VertexIndexToShape::getVertexCount() + { + return mVertexCount; + } + //------------------------------------------------------------------------------------------------ + const unsigned int* VertexIndexToShape::getIndices() + { + return mIndexBuffer; + } + //------------------------------------------------------------------------------------------------ + unsigned int VertexIndexToShape::getIndexCount() + { + return mIndexCount; + } + + //------------------------------------------------------------------------------------------------ + btSphereShape* VertexIndexToShape::createSphere() + { + const Ogre::Real rad = getRadius(); + assert((rad > 0.0) && + ("Sphere radius must be greater than zero")); + btSphereShape* shape = new btSphereShape(rad); + + shape->setLocalScaling(Convert::toBullet(mScale)); + + return shape; + } + //------------------------------------------------------------------------------------------------ + btBoxShape* VertexIndexToShape::createBox() + { + const Ogre::Vector3 sz = getSize(); + + assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && + ("Size of box must be greater than zero on all axes")); + + btBoxShape* shape = new btBoxShape(Convert::toBullet(sz * 0.5)); + + shape->setLocalScaling(Convert::toBullet(mScale)); + + return shape; + } + //------------------------------------------------------------------------------------------------ + btCylinderShape* VertexIndexToShape::createCylinder() + { + const Ogre::Vector3 sz = getSize(); + + assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && + ("Size of Cylinder must be greater than zero on all axes")); + + btCylinderShape* shape = new btCylinderShapeX(Convert::toBullet(sz * 0.5)); + + shape->setLocalScaling(Convert::toBullet(mScale)); + + return shape; + } + //------------------------------------------------------------------------------------------------ + btConvexHullShape* VertexIndexToShape::createConvex() + { + assert(mVertexCount && (mIndexCount >= 6) && + ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + + return new btConvexHullShape((btScalar*) &mVertexBuffer[0].x, mVertexCount, sizeof(Vector3)); + } + //------------------------------------------------------------------------------------------------ + btBvhTriangleMeshShape* VertexIndexToShape::createTrimesh() + { + assert(mVertexCount && (mIndexCount >= 6) && + ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + + unsigned int numFaces = mIndexCount / 3; + + btTriangleMesh *trimesh = new btTriangleMesh(); + unsigned int *indices = mIndexBuffer; + Vector3 *vertices = mVertexBuffer; + + btVector3 vertexPos[3]; + for (unsigned int n = 0; n < numFaces; ++n) + { + { + const Vector3 &vec = vertices[*indices]; + vertexPos[0][0] = vec.x; + vertexPos[0][1] = vec.y; + vertexPos[0][2] = vec.z; + } + { + const Vector3 &vec = vertices[*(indices + 1)]; + vertexPos[1][0] = vec.x; + vertexPos[1][1] = vec.y; + vertexPos[1][2] = vec.z; + } + { + const Vector3 &vec = vertices[*(indices + 2)]; + vertexPos[2][0] = vec.x; + vertexPos[2][1] = vec.y; + vertexPos[2][2] = vec.z; + } + + indices += 3; + + trimesh->addTriangle(vertexPos[0], vertexPos[1], vertexPos[2]); + } + + const bool useQuantizedAABB = true; + btBvhTriangleMeshShape *shape = new btBvhTriangleMeshShape(trimesh, useQuantizedAABB); + + shape->setLocalScaling(Convert::toBullet(mScale)); + + return shape; + } + //------------------------------------------------------------------------------------------------ + VertexIndexToShape::~VertexIndexToShape() + { + delete[] mVertexBuffer; + delete[] mIndexBuffer; + + if (mBoneIndex) + { + for(BoneIndex::iterator i = mBoneIndex->begin(); + i != mBoneIndex->end(); + ++i) + { + delete i->second; + } + delete mBoneIndex; + } + } + //------------------------------------------------------------------------------------------------ + VertexIndexToShape::VertexIndexToShape(const Matrix4 &transform) : + mVertexBuffer (0), + mIndexBuffer (0), + mVertexCount (0), + mIndexCount (0), + mBounds (Vector3(-1,-1,-1)), + mBoundRadius (-1), + mBoneIndex (0), + mTransform (transform), + mScale(1) + { + } + +/* + * ============================================================================================= + * BtOgre::StaticMeshToShapeConverter + * ============================================================================================= + */ + + StaticMeshToShapeConverter::StaticMeshToShapeConverter() : + VertexIndexToShape(), + mEntity (0), + mNode (0) + { + } + //------------------------------------------------------------------------------------------------ + StaticMeshToShapeConverter::~StaticMeshToShapeConverter() + { + } + //------------------------------------------------------------------------------------------------ + StaticMeshToShapeConverter::StaticMeshToShapeConverter(Entity *entity, const Matrix4 &transform) : + VertexIndexToShape(transform), + mEntity (0), + mNode (0) + { + addEntity(entity, transform); + } + //------------------------------------------------------------------------------------------------ + StaticMeshToShapeConverter::StaticMeshToShapeConverter(Renderable *rend, const Matrix4 &transform) : + VertexIndexToShape(transform), + mEntity (0), + mNode (0) + { + RenderOperation op; + rend->getRenderOperation(op); + VertexIndexToShape::addStaticVertexData(op.vertexData); + if(op.useIndexes) + VertexIndexToShape::addIndexData(op.indexData); + + } + //------------------------------------------------------------------------------------------------ + void StaticMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; + + mEntity = entity; + mNode = (SceneNode*)(mEntity->getParentNode()); + mTransform = transform; + mScale = mNode->getScale(); + + if (mEntity->getMesh()->sharedVertexData) + { + VertexIndexToShape::addStaticVertexData (mEntity->getMesh()->sharedVertexData); + } + + for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i) + { + SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh(); + + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } + + } + } + //------------------------------------------------------------------------------------------------ + void StaticMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; + + //_entity = entity; + //_node = (SceneNode*)(_entity->getParentNode()); + mTransform = transform; + + if (mesh->hasSkeleton ()) + Ogre::LogManager::getSingleton().logMessage("MeshToShapeConverter::addMesh : Mesh " + mesh->getName () + " as skeleton but added to trimesh non animated"); + + if (mesh->sharedVertexData) + { + VertexIndexToShape::addStaticVertexData (mesh->sharedVertexData); + } + + for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i) + { + SubMesh *sub_mesh = mesh->getSubMesh(i); + + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } + + } + } + +/* + * ============================================================================================= + * BtOgre::AnimatedMeshToShapeConverter + * ============================================================================================= + */ + + AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter(Entity *entity,const Matrix4 &transform) : + VertexIndexToShape(transform), + mEntity (0), + mNode (0), + mTransformedVerticesTemp(0), + mTransformedVerticesTempSize(0) + { + addEntity(entity, transform); + } + //------------------------------------------------------------------------------------------------ + AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter() : + VertexIndexToShape(), + mEntity (0), + mNode (0), + mTransformedVerticesTemp(0), + mTransformedVerticesTempSize(0) + { + } + //------------------------------------------------------------------------------------------------ + AnimatedMeshToShapeConverter::~AnimatedMeshToShapeConverter() + { + delete[] mTransformedVerticesTemp; + } + //------------------------------------------------------------------------------------------------ + void AnimatedMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; + + mEntity = entity; + mNode = (SceneNode*)(mEntity->getParentNode()); + mTransform = transform; + + assert (entity->getMesh()->hasSkeleton ()); + + mEntity->addSoftwareAnimationRequest(false); + mEntity->_updateAnimation(); + + if (mEntity->getMesh()->sharedVertexData) + { + VertexIndexToShape::addAnimatedVertexData (mEntity->getMesh()->sharedVertexData, + mEntity->_getSkelAnimVertexData(), + &mEntity->getMesh()->sharedBlendIndexToBoneIndexMap); + } + + for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i) + { + SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh(); + + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + + VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData, + mEntity->getSubEntity(i)->_getSkelAnimVertexData(), + &sub_mesh->blendIndexToBoneIndexMap); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } + + } + + mEntity->removeSoftwareAnimationRequest(false); + } + //------------------------------------------------------------------------------------------------ + void AnimatedMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; + + //_entity = entity; + //_node = (SceneNode*)(_entity->getParentNode()); + mTransform = transform; + + assert (mesh->hasSkeleton ()); + + if (mesh->sharedVertexData) + { + VertexIndexToShape::addAnimatedVertexData (mesh->sharedVertexData, + 0, + &mesh->sharedBlendIndexToBoneIndexMap); + } + + for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i) + { + SubMesh *sub_mesh = mesh->getSubMesh(i); + + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + + VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData, + 0, + &sub_mesh->blendIndexToBoneIndexMap); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } + + } + } + //------------------------------------------------------------------------------------------------ + bool AnimatedMeshToShapeConverter::getBoneVertices(unsigned char bone, + unsigned int &vertex_count, + Ogre::Vector3* &vertices, + const Vector3 &bonePosition) + { + BoneIndex::iterator i = mBoneIndex->find(bone); + + if (i == mBoneIndex->end()) + return false; + + if (i->second->empty()) + return false; + + vertex_count = (unsigned int) i->second->size() + 1; + if (vertex_count > mTransformedVerticesTempSize) + { + if (mTransformedVerticesTemp) + delete[] mTransformedVerticesTemp; + + mTransformedVerticesTemp = new Ogre::Vector3[vertex_count]; + + } + + vertices = mTransformedVerticesTemp; + vertices[0] = bonePosition; + //mEntity->_getParentNodeFullTransform() * + // mEntity->getSkeleton()->getBone(bone)->_getDerivedPosition(); + + //mEntity->getSkeleton()->getBone(bone)->_getDerivedOrientation() + unsigned int currBoneVertex = 1; + Vector3Array::iterator j = i->second->begin(); + while(j != i->second->end()) + { + vertices[currBoneVertex] = (*j); + ++j; + ++currBoneVertex; + } + return true; + } + //------------------------------------------------------------------------------------------------ + btBoxShape* AnimatedMeshToShapeConverter::createAlignedBox(unsigned char bone, + const Vector3 &bonePosition, + const Quaternion &boneOrientation) + { + unsigned int vertex_count; + Vector3* vertices; + + if (!getBoneVertices(bone, vertex_count, vertices, bonePosition)) + return 0; + + Vector3 min_vec(vertices[0]); + Vector3 max_vec(vertices[0]); + + for(unsigned int j = 1; j < vertex_count ;j++) + { + min_vec.x = std::min(min_vec.x,vertices[j].x); + min_vec.y = std::min(min_vec.y,vertices[j].y); + min_vec.z = std::min(min_vec.z,vertices[j].z); + + max_vec.x = std::max(max_vec.x,vertices[j].x); + max_vec.y = std::max(max_vec.y,vertices[j].y); + max_vec.z = std::max(max_vec.z,vertices[j].z); + } + const Ogre::Vector3 maxMinusMin(max_vec - min_vec); + btBoxShape* box = new btBoxShape(Convert::toBullet(maxMinusMin)); + + /*const Ogre::Vector3 pos + (min_vec.x + (maxMinusMin.x * 0.5), + min_vec.y + (maxMinusMin.y * 0.5), + min_vec.z + (maxMinusMin.z * 0.5));*/ + + //box->setPosition(pos); + + return box; + } + //------------------------------------------------------------------------------------------------ + bool AnimatedMeshToShapeConverter::getOrientedBox(unsigned char bone, + const Vector3 &bonePosition, + const Quaternion &boneOrientation, + Vector3 &box_afExtent, + Vector3 *box_akAxis, + Vector3 &box_kCenter) + { + unsigned int vertex_count; + Vector3* vertices; + + if (!getBoneVertices(bone, vertex_count, vertices, bonePosition)) + return false; + + box_kCenter = Vector3::ZERO; + + { + for(unsigned int c = 0 ;c < vertex_count;c++) + { + box_kCenter += vertices[c]; + } + const Ogre::Real invVertexCount = 1.0 / vertex_count; + box_kCenter *= invVertexCount; + } + Quaternion orient = boneOrientation; + orient.ToAxes(box_akAxis); + + // Let C be the box center and let U0, U1, and U2 be the box axes. Each + // input point is of the form X = C + y0*U0 + y1*U1 + y2*U2. The + // following code computes min(y0), max(y0), min(y1), max(y1), min(y2), + // and max(y2). The box center is then adjusted to be + // C' = C + 0.5*(min(y0)+max(y0))*U0 + 0.5*(min(y1)+max(y1))*U1 + + // 0.5*(min(y2)+max(y2))*U2 + + Ogre::Vector3 kDiff (vertices[1] - box_kCenter); + Ogre::Real fY0Min = kDiff.dotProduct(box_akAxis[0]), fY0Max = fY0Min; + Ogre::Real fY1Min = kDiff.dotProduct(box_akAxis[1]), fY1Max = fY1Min; + Ogre::Real fY2Min = kDiff.dotProduct(box_akAxis[2]), fY2Max = fY2Min; + + for (unsigned int i = 2; i < vertex_count; i++) + { + kDiff = vertices[i] - box_kCenter; + + const Ogre::Real fY0 = kDiff.dotProduct(box_akAxis[0]); + if ( fY0 < fY0Min ) + fY0Min = fY0; + else if ( fY0 > fY0Max ) + fY0Max = fY0; + + const Ogre::Real fY1 = kDiff.dotProduct(box_akAxis[1]); + if ( fY1 < fY1Min ) + fY1Min = fY1; + else if ( fY1 > fY1Max ) + fY1Max = fY1; + + const Ogre::Real fY2 = kDiff.dotProduct(box_akAxis[2]); + if ( fY2 < fY2Min ) + fY2Min = fY2; + else if ( fY2 > fY2Max ) + fY2Max = fY2; + } + + box_afExtent.x = ((Real)0.5)*(fY0Max - fY0Min); + box_afExtent.y = ((Real)0.5)*(fY1Max - fY1Min); + box_afExtent.z = ((Real)0.5)*(fY2Max - fY2Min); + + box_kCenter += (0.5*(fY0Max+fY0Min))*box_akAxis[0] + + (0.5*(fY1Max+fY1Min))*box_akAxis[1] + + (0.5*(fY2Max+fY2Min))*box_akAxis[2]; + + box_afExtent *= 2.0; + + return true; + } + //------------------------------------------------------------------------------------------------ + btBoxShape *AnimatedMeshToShapeConverter::createOrientedBox(unsigned char bone, + const Vector3 &bonePosition, + const Quaternion &boneOrientation) + { + Ogre::Vector3 box_akAxis[3]; + Ogre::Vector3 box_afExtent; + Ogre::Vector3 box_afCenter; + + if (!getOrientedBox(bone, bonePosition, boneOrientation, + box_afExtent, + box_akAxis, + box_afCenter)) + return 0; + + btBoxShape *geom = new btBoxShape(Convert::toBullet(box_afExtent)); + //geom->setOrientation(Quaternion(box_akAxis[0],box_akAxis[1],box_akAxis[2])); + //geom->setPosition(box_afCenter); + return geom; + } + +/* + * ============================================================================================= + * BtOgre::DynamicRenderable + * ============================================================================================= + */ + + DynamicRenderable::DynamicRenderable() + { + } + //------------------------------------------------------------------------------------------------ + DynamicRenderable::~DynamicRenderable() + { + delete mRenderOp.vertexData; + delete mRenderOp.indexData; + } + //------------------------------------------------------------------------------------------------ + void DynamicRenderable::initialize(RenderOperation::OperationType operationType, + bool useIndices) + { + // Initialize render operation + mRenderOp.operationType = operationType; + mRenderOp.useIndexes = useIndices; + mRenderOp.vertexData = new VertexData; + if (mRenderOp.useIndexes) + mRenderOp.indexData = new IndexData; + + // Reset buffer capacities + mVertexBufferCapacity = 0; + mIndexBufferCapacity = 0; + + // Create vertex declaration + createVertexDeclaration(); + } + //------------------------------------------------------------------------------------------------ + void DynamicRenderable::prepareHardwareBuffers(size_t vertexCount, + size_t indexCount) + { + // Prepare vertex buffer + size_t newVertCapacity = mVertexBufferCapacity; + if ((vertexCount > mVertexBufferCapacity) || + (!mVertexBufferCapacity)) + { + // vertexCount exceeds current capacity! + // It is necessary to reallocate the buffer. + + // Check if this is the first call + if (!newVertCapacity) + newVertCapacity = 1; + + // Make capacity the next power of two + while (newVertCapacity < vertexCount) + newVertCapacity <<= 1; + } + else if (vertexCount < mVertexBufferCapacity>>1) { + // Make capacity the previous power of two + while (vertexCount < newVertCapacity>>1) + newVertCapacity >>= 1; + } + if (newVertCapacity != mVertexBufferCapacity) + { + mVertexBufferCapacity = newVertCapacity; + // Create new vertex buffer + HardwareVertexBufferSharedPtr vbuf = + HardwareBufferManager::getSingleton().createVertexBuffer( + mRenderOp.vertexData->vertexDeclaration->getVertexSize(0), + mVertexBufferCapacity, + HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_? + + // Bind buffer + mRenderOp.vertexData->vertexBufferBinding->setBinding(0, vbuf); + } + // Update vertex count in the render operation + mRenderOp.vertexData->vertexCount = vertexCount; + + if (mRenderOp.useIndexes) + { + OgreAssert(indexCount <= std::numeric_limits::max(), "indexCount exceeds 16 bit"); + + size_t newIndexCapacity = mIndexBufferCapacity; + // Prepare index buffer + if ((indexCount > newIndexCapacity) || + (!newIndexCapacity)) + { + // indexCount exceeds current capacity! + // It is necessary to reallocate the buffer. + + // Check if this is the first call + if (!newIndexCapacity) + newIndexCapacity = 1; + + // Make capacity the next power of two + while (newIndexCapacity < indexCount) + newIndexCapacity <<= 1; + + } + else if (indexCount < newIndexCapacity>>1) + { + // Make capacity the previous power of two + while (indexCount < newIndexCapacity>>1) + newIndexCapacity >>= 1; + } + + if (newIndexCapacity != mIndexBufferCapacity) + { + mIndexBufferCapacity = newIndexCapacity; + // Create new index buffer + mRenderOp.indexData->indexBuffer = + HardwareBufferManager::getSingleton().createIndexBuffer( + HardwareIndexBuffer::IT_16BIT, + mIndexBufferCapacity, + HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_? + } + + // Update index count in the render operation + mRenderOp.indexData->indexCount = indexCount; + } + } + //------------------------------------------------------------------------------------------------ + Real DynamicRenderable::getBoundingRadius(void) const + { + return Math::Sqrt(std::max(mBox.getMaximum().squaredLength(), mBox.getMinimum().squaredLength())); + } + //------------------------------------------------------------------------------------------------ + Real DynamicRenderable::getSquaredViewDepth(const Camera* cam) const + { + Vector3 vMin, vMax, vMid, vDist; + vMin = mBox.getMinimum(); + vMax = mBox.getMaximum(); + vMid = ((vMax - vMin) * 0.5) + vMin; + vDist = cam->getDerivedPosition() - vMid; + + return vDist.squaredLength(); + } + +/* + * ============================================================================================= + * BtOgre::DynamicLines + * ============================================================================================= + */ + + enum { + POSITION_BINDING, + TEXCOORD_BINDING + }; + //------------------------------------------------------------------------------------------------ + DynamicLines::DynamicLines(OperationType opType) + { + initialize(opType,false); + setMaterial("BaseWhiteNoLighting"); + mDirty = true; + } + //------------------------------------------------------------------------------------------------ + DynamicLines::~DynamicLines() + { + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::setOperationType(OperationType opType) + { + mRenderOp.operationType = opType; + } + //------------------------------------------------------------------------------------------------ + RenderOperation::OperationType DynamicLines::getOperationType() const + { + return mRenderOp.operationType; + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::addPoint(const Vector3 &p) + { + mPoints.push_back(p); + mDirty = true; + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::addPoint(Real x, Real y, Real z) + { + mPoints.push_back(Vector3(x,y,z)); + mDirty = true; + } + //------------------------------------------------------------------------------------------------ + const Vector3& DynamicLines::getPoint(unsigned short index) const + { + assert(index < mPoints.size() && "Point index is out of bounds!!"); + return mPoints[index]; + } + //------------------------------------------------------------------------------------------------ + unsigned short DynamicLines::getNumPoints(void) const + { + return (unsigned short)mPoints.size(); + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::setPoint(unsigned short index, const Vector3 &value) + { + assert(index < mPoints.size() && "Point index is out of bounds!!"); + + mPoints[index] = value; + mDirty = true; + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::clear() + { + mPoints.clear(); + mDirty = true; + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::update() + { + if (mDirty) fillHardwareBuffers(); + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::createVertexDeclaration() + { + VertexDeclaration *decl = mRenderOp.vertexData->vertexDeclaration; + decl->addElement(POSITION_BINDING, 0, VET_FLOAT3, VES_POSITION); + } + //------------------------------------------------------------------------------------------------ + void DynamicLines::fillHardwareBuffers() + { + int size = mPoints.size(); + + prepareHardwareBuffers(size,0); + + if (!size) { + mBox.setExtents(Vector3::ZERO,Vector3::ZERO); + mDirty=false; + return; + } + + Vector3 vaabMin = mPoints[0]; + Vector3 vaabMax = mPoints[0]; + + HardwareVertexBufferSharedPtr vbuf = + mRenderOp.vertexData->vertexBufferBinding->getBuffer(0); + + Real *prPos = static_cast(vbuf->lock(HardwareBuffer::HBL_DISCARD)); + { + for(int i = 0; i < size; i++) + { + *prPos++ = mPoints[i].x; + *prPos++ = mPoints[i].y; + *prPos++ = mPoints[i].z; + + if(mPoints[i].x < vaabMin.x) + vaabMin.x = mPoints[i].x; + if(mPoints[i].y < vaabMin.y) + vaabMin.y = mPoints[i].y; + if(mPoints[i].z < vaabMin.z) + vaabMin.z = mPoints[i].z; + + if(mPoints[i].x > vaabMax.x) + vaabMax.x = mPoints[i].x; + if(mPoints[i].y > vaabMax.y) + vaabMax.y = mPoints[i].y; + if(mPoints[i].z > vaabMax.z) + vaabMax.z = mPoints[i].z; + } + } + vbuf->unlock(); + + mBox.setExtents(vaabMin, vaabMax); + + mDirty = false; + } +} diff --git a/bullet/BtOgreExtras.h b/bullet/BtOgreExtras.h new file mode 100644 index 0000000000..f959433774 --- /dev/null +++ b/bullet/BtOgreExtras.h @@ -0,0 +1,280 @@ +/* + * ===================================================================================== + * + * Filename: BtOgreExtras.h + * + * Description: Contains the Ogre Mesh to Bullet Shape converters. + * + * Version: 1.0 + * Created: 27/12/2008 01:45:56 PM + * + * Author: Nikhilesh (nikki) + * + * ===================================================================================== + */ + +#ifndef _BtOgreShapes_H_ +#define _BtOgreShapes_H_ + +#include "btBulletDynamicsCommon.h" +#include "OgreSimpleRenderable.h" +#include "OgreCamera.h" +#include "OgreHardwareBufferManager.h" +#include "OgreMaterialManager.h" +#include "OgreTechnique.h" +#include "OgrePass.h" + +#include "OgreLogManager.h" + +namespace BtOgre +{ + +typedef std::vector Vector3Array; + +//Converts from and to Bullet and Ogre stuff. Pretty self-explanatory. +class Convert +{ +public: + Convert() {}; + ~Convert() {}; + + static btQuaternion toBullet(const Ogre::Quaternion &q) + { + return btQuaternion(q.x, q.y, q.z, q.w); + } + static btVector3 toBullet(const Ogre::Vector3 &v) + { + return btVector3(v.x, v.y, v.z); + } + + static Ogre::Quaternion toOgre(const btQuaternion &q) + { + return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z()); + } + static Ogre::Vector3 toOgre(const btVector3 &v) + { + return Ogre::Vector3(v.x(), v.y(), v.z()); + } +}; + +//From here on its debug-drawing stuff. ------------------------------------------------------------------ + +class DynamicRenderable : public Ogre::SimpleRenderable +{ +public: + /// Constructor + DynamicRenderable(); + /// Virtual destructor + virtual ~DynamicRenderable(); + + /** Initializes the dynamic renderable. + @remarks + This function should only be called once. It initializes the + render operation, and calls the abstract function + createVertexDeclaration(). + @param operationType The type of render operation to perform. + @param useIndices Specifies whether to use indices to determine the + vertices to use as input. */ + void initialize(Ogre::RenderOperation::OperationType operationType, + bool useIndices); + + /// Implementation of Ogre::SimpleRenderable + virtual Ogre::Real getBoundingRadius(void) const; + /// Implementation of Ogre::SimpleRenderable + virtual Ogre::Real getSquaredViewDepth(const Ogre::Camera* cam) const; + +protected: + /// Maximum capacity of the currently allocated vertex buffer. + size_t mVertexBufferCapacity; + /// Maximum capacity of the currently allocated index buffer. + size_t mIndexBufferCapacity; + + /** Creates the vertex declaration. + @remarks + Override and set mRenderOp.vertexData->vertexDeclaration here. + mRenderOp.vertexData will be created for you before this method + is called. */ + virtual void createVertexDeclaration() = 0; + + /** Prepares the hardware buffers for the requested vertex and index counts. + @remarks + This function must be called before locking the buffers in + fillHardwareBuffers(). It guarantees that the hardware buffers + are large enough to hold at least the requested number of + vertices and indices (if using indices). The buffers are + possibly reallocated to achieve this. + @par + The vertex and index count in the render operation are set to + the values of vertexCount and indexCount respectively. + @param vertexCount The number of vertices the buffer must hold. + + @param indexCount The number of indices the buffer must hold. This + parameter is ignored if not using indices. */ + void prepareHardwareBuffers(size_t vertexCount, size_t indexCount); + + /** Fills the hardware vertex and index buffers with data. + @remarks + This function must call prepareHardwareBuffers() before locking + the buffers to ensure the they are large enough for the data to + be written. Afterwards the vertex and index buffers (if using + indices) can be locked, and data can be written to them. */ + virtual void fillHardwareBuffers() = 0; +}; + +class DynamicLines : public DynamicRenderable +{ + typedef Ogre::Vector3 Vector3; + typedef Ogre::Quaternion Quaternion; + typedef Ogre::Camera Camera; + typedef Ogre::Real Real; + typedef Ogre::RenderOperation::OperationType OperationType; + +public: + /// Constructor - see setOperationType() for description of argument. + DynamicLines(OperationType opType=Ogre::RenderOperation::OT_LINE_STRIP); + virtual ~DynamicLines(); + + /// Add a point to the point list + void addPoint(const Ogre::Vector3 &p); + /// Add a point to the point list + void addPoint(Real x, Real y, Real z); + + /// Change the location of an existing point in the point list + void setPoint(unsigned short index, const Vector3 &value); + + /// Return the location of an existing point in the point list + const Vector3& getPoint(unsigned short index) const; + + /// Return the total number of points in the point list + unsigned short getNumPoints(void) const; + + /// Remove all points from the point list + void clear(); + + /// Call this to update the hardware buffer after making changes. + void update(); + + /** Set the type of operation to draw with. + * @param opType Can be one of + * - RenderOperation::OT_LINE_STRIP + * - RenderOperation::OT_LINE_LIST + * - RenderOperation::OT_POINT_LIST + * - RenderOperation::OT_TRIANGLE_LIST + * - RenderOperation::OT_TRIANGLE_STRIP + * - RenderOperation::OT_TRIANGLE_FAN + * The default is OT_LINE_STRIP. + */ + void setOperationType(OperationType opType); + OperationType getOperationType() const; + +protected: + /// Implementation DynamicRenderable, creates a simple vertex-only decl + virtual void createVertexDeclaration(); + /// Implementation DynamicRenderable, pushes point list out to hardware memory + virtual void fillHardwareBuffers(); + +private: + std::vector mPoints; + bool mDirty; +}; + +class DebugDrawer : public btIDebugDraw +{ +protected: + Ogre::SceneNode *mNode; + btDynamicsWorld *mWorld; + DynamicLines *mLineDrawer; + bool mDebugOn; + +public: + + DebugDrawer(Ogre::SceneNode *node, btDynamicsWorld *world) + : mNode(node), + mWorld(world), + mDebugOn(true) + { + mLineDrawer = new DynamicLines(Ogre::RenderOperation::OT_LINE_LIST); + mNode->attachObject(mLineDrawer); + + if (!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists("BtOgre")) + Ogre::ResourceGroupManager::getSingleton().createResourceGroup("BtOgre"); + if (!Ogre::MaterialManager::getSingleton().resourceExists("BtOgre/DebugLines")) + { + Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create("BtOgre/DebugLines", "BtOgre"); + mat->setReceiveShadows(false); + mat->setSelfIllumination(1,1,1); + } + + mLineDrawer->setMaterial("BtOgre/DebugLines"); + } + + ~DebugDrawer() + { + Ogre::MaterialManager::getSingleton().remove("BtOgre/DebugLines"); + Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup("BtOgre"); + delete mLineDrawer; + } + + void step() + { + if (mDebugOn) + { + mWorld->debugDrawWorld(); + mLineDrawer->update(); + mNode->needUpdate(); + mLineDrawer->clear(); + } + else + { + mLineDrawer->clear(); + mLineDrawer->update(); + mNode->needUpdate(); + } + } + + void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) + { + mLineDrawer->addPoint(Convert::toOgre(from)); + mLineDrawer->addPoint(Convert::toOgre(to)); + } + + void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color) + { + mLineDrawer->addPoint(Convert::toOgre(PointOnB)); + mLineDrawer->addPoint(Convert::toOgre(PointOnB) + (Convert::toOgre(normalOnB) * distance * 20)); + } + + void reportErrorWarning(const char* warningString) + { + Ogre::LogManager::getSingleton().logMessage(warningString); + } + + void draw3dText(const btVector3& location,const char* textString) + { + } + + //0 for off, anything else for on. + void setDebugMode(int isOn) + { + mDebugOn = (isOn == 0) ? false : true; + + if (!mDebugOn) + mLineDrawer->clear(); + } + + //0 for off, anything else for on. + int getDebugMode() const + { + return mDebugOn; + } + +}; + +} + +#endif + + + + + diff --git a/bullet/BtOgrePG.h b/bullet/BtOgrePG.h new file mode 100644 index 0000000000..b1d2715409 --- /dev/null +++ b/bullet/BtOgrePG.h @@ -0,0 +1,81 @@ +/* + * ===================================================================================== + * + * Filename: BtOgrePG.h + * + * Description: The part of BtOgre that handles information transfer from Bullet to + * Ogre (like updating graphics object positions). + * + * Version: 1.0 + * Created: 27/12/2008 03:40:56 AM + * + * Author: Nikhilesh (nikki) + * + * ===================================================================================== + */ + +#ifndef _BtOgreGP_H_ +#define _BtOgreGP_H_ + +#include "btBulletDynamicsCommon.h" +#include "OgreSceneNode.h" +#include "BtOgreExtras.h" + +namespace BtOgre { + +//A MotionState is Bullet's way of informing you about updates to an object. +//Pass this MotionState to a btRigidBody to have your SceneNode updated automaticaly. +class RigidBodyState : public btMotionState +{ + protected: + btTransform mTransform; + btTransform mCenterOfMassOffset; + + Ogre::SceneNode *mNode; + + public: + RigidBodyState(Ogre::SceneNode *node, const btTransform &transform, const btTransform &offset = btTransform::getIdentity()) + : mTransform(transform), + mCenterOfMassOffset(offset), + mNode(node) + { + } + + RigidBodyState(Ogre::SceneNode *node) + : mTransform(((node != NULL) ? BtOgre::Convert::toBullet(node->getOrientation()) : btQuaternion(0,0,0,1)), + ((node != NULL) ? BtOgre::Convert::toBullet(node->getPosition()) : btVector3(0,0,0))), + mCenterOfMassOffset(btTransform::getIdentity()), + mNode(node) + { + } + + virtual void getWorldTransform(btTransform &ret) const + { + ret = mCenterOfMassOffset.inverse() * mTransform; + } + + virtual void setWorldTransform(const btTransform &in) + { + if (mNode == NULL) + return; + + mTransform = in; + btTransform transform = in * mCenterOfMassOffset; + + btQuaternion rot = transform.getRotation(); + btVector3 pos = transform.getOrigin(); + mNode->setOrientation(rot.w(), rot.x(), rot.y(), rot.z()); + mNode->setPosition(pos.x(), pos.y(), pos.z()); + } + + void setNode(Ogre::SceneNode *node) + { + mNode = node; + } +}; + +//Softbody-Ogre connection goes here! + +} + +#endif From e84a02546ed38e0070dd688fc54695b43a359fa1 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 22 Feb 2011 14:04:30 +0100 Subject: [PATCH 136/269] add debug rendering --- bullet/BtOgreGP.h | 143 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 bullet/BtOgreGP.h diff --git a/bullet/BtOgreGP.h b/bullet/BtOgreGP.h new file mode 100644 index 0000000000..1894a79b3b --- /dev/null +++ b/bullet/BtOgreGP.h @@ -0,0 +1,143 @@ +/* + * ===================================================================================== + * + * Filename: BtOgreGP.h + * + * Description: The part of BtOgre that handles information transfer from Ogre to + * Bullet (like mesh data for making trimeshes). + * + * Version: 1.0 + * Created: 27/12/2008 03:29:56 AM + * + * Author: Nikhilesh (nikki) + * + * ===================================================================================== + */ + +#ifndef _BtOgrePG_H_ +#define _BtOgrePG_H_ + +#include "btBulletDynamicsCommon.h" +#include "BtOgreExtras.h" +#include "Ogre.h" + +namespace BtOgre { + +typedef std::map BoneIndex; +typedef std::pair BoneKeyIndex; + +class VertexIndexToShape +{ +public: + VertexIndexToShape(const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + ~VertexIndexToShape(); + + Ogre::Real getRadius(); + Ogre::Vector3 getSize(); + + + btSphereShape* createSphere(); + btBoxShape* createBox(); + btBvhTriangleMeshShape* createTrimesh(); + btCylinderShape* createCylinder(); + btConvexHullShape* createConvex(); + + const Ogre::Vector3* getVertices(); + unsigned int getVertexCount(); + const unsigned int* getIndices(); + unsigned int getIndexCount(); + +protected: + + void addStaticVertexData(const Ogre::VertexData *vertex_data); + + void addAnimatedVertexData(const Ogre::VertexData *vertex_data, + const Ogre::VertexData *blended_data, + const Ogre::Mesh::IndexMap *indexMap); + + void addIndexData(Ogre::IndexData *data, const unsigned int offset = 0); + + +protected: + Ogre::Vector3* mVertexBuffer; + unsigned int* mIndexBuffer; + unsigned int mVertexCount; + unsigned int mIndexCount; + + Ogre::Matrix4 mTransform; + + Ogre::Real mBoundRadius; + Ogre::Vector3 mBounds; + + BoneIndex *mBoneIndex; + + Ogre::Vector3 mScale; +}; + +//For static (non-animated) meshes. +class StaticMeshToShapeConverter : public VertexIndexToShape +{ +public: + + StaticMeshToShapeConverter(Ogre::Renderable *rend, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + StaticMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + StaticMeshToShapeConverter(); + + ~StaticMeshToShapeConverter(); + + void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + + void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + + +protected: + + Ogre::Entity* mEntity; + Ogre::SceneNode* mNode; +}; + +//For animated meshes. +class AnimatedMeshToShapeConverter : public VertexIndexToShape +{ +public: + + AnimatedMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + AnimatedMeshToShapeConverter(); + ~AnimatedMeshToShapeConverter(); + + void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform); + + btBoxShape* createAlignedBox(unsigned char bone, + const Ogre::Vector3 &bonePosition, + const Ogre::Quaternion &boneOrientation); + + btBoxShape* createOrientedBox(unsigned char bone, + const Ogre::Vector3 &bonePosition, + const Ogre::Quaternion &boneOrientation); + +protected: + + bool getBoneVertices(unsigned char bone, + unsigned int &vertex_count, + Ogre::Vector3* &vertices, + const Ogre::Vector3 &bonePosition); + + bool getOrientedBox(unsigned char bone, + const Ogre::Vector3 &bonePosition, + const Ogre::Quaternion &boneOrientation, + Ogre::Vector3 &extents, + Ogre::Vector3 *axis, + Ogre::Vector3 ¢er); + + + Ogre::Entity* mEntity; + Ogre::SceneNode* mNode; + + Ogre::Vector3 *mTransformedVerticesTemp; + size_t mTransformedVerticesTempSize; +}; + +} + +#endif \ No newline at end of file From ada4616fd3932ee6e8883d420108a203014573f3 Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 22 Feb 2011 20:53:02 +0100 Subject: [PATCH 137/269] add raycasting --- bullet/physic.cpp | 37 +++++++++++++++++++++++++++++++++---- bullet/physic.hpp | 25 +++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index d5c504cfdd..76dabce5d9 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -31,7 +31,7 @@ namespace Physic transform.setIdentity(); // External capsule - externalGhostObject = new btPairCachingGhostObject(); + externalGhostObject = new PairCachingGhostObject(name); externalGhostObject->setWorldTransform( transform ); btScalar externalCapsuleHeight = 50; @@ -44,7 +44,7 @@ namespace Physic externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); // Internal capsule - internalGhostObject = new btPairCachingGhostObject(); + internalGhostObject = new PairCachingGhostObject(name); internalGhostObject->setWorldTransform( transform ); //internalGhostObject->getBroadphaseHandle()->s btScalar internalCapsuleHeight = 20; @@ -196,13 +196,20 @@ namespace Physic //create the real body btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape); RigidBody* body = new RigidBody(CI,name); - + body->collide = shape->collide; return body; } void PhysicEngine::addRigidBody(RigidBody* body) { - dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL); + if(body->collide) + { + dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL); + } + else + { + dynamicsWorld->addRigidBody(body,COL_WORLD,COL_NOTHING); + } body->setActivationState(DISABLE_DEACTIVATION); RigidBodyMap[body->mName] = body; } @@ -272,4 +279,26 @@ namespace Physic void PhysicEngine::emptyEventLists(void) { } + + std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to) + { + std::string name = ""; + float d = -1.; + btCollisionWorld::ClosestRayResultCallback resultCallback(from, to); + dynamicsWorld->rayTest(from, to, resultCallback); + + if (resultCallback.hasHit()) + { + if(resultCallback.m_collisionFilterGroup == COL_WORLD) + { + name = static_cast(resultCallback.m_collisionObject)->mName; + } + if(resultCallback.m_collisionFilterGroup == COL_ACTOR_EXTERNAL || resultCallback.m_collisionFilterGroup == COL_ACTOR_INTERNAL) + { + name = static_cast(resultCallback.m_collisionObject)->mName; + } + d = resultCallback.m_closestHitFraction; + } + return std::pair(name,d); + } }}; \ No newline at end of file diff --git a/bullet/physic.hpp b/bullet/physic.hpp index 5fdc03e7d3..062e69e025 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -34,6 +34,19 @@ namespace Physic class CMotionState; struct PhysicEvent; + /** + *This is just used to be able to name objects. + */ + class PairCachingGhostObject : public btPairCachingGhostObject + { + public: + PairCachingGhostObject(std::string name) + :btPairCachingGhostObject(),mName(name) + { + } + std::string mName; + }; + /** *A physic Actor use a modifed KinematicCharacterController taken in the bullet forum. */ @@ -63,10 +76,10 @@ namespace Physic btKinematicCharacterController* mCharacter; - btPairCachingGhostObject* internalGhostObject; + PairCachingGhostObject* internalGhostObject; btCollisionShape* internalCollisionShape; - btPairCachingGhostObject* externalGhostObject; + PairCachingGhostObject* externalGhostObject; btCollisionShape* externalCollisionShape; std::string mName; @@ -82,6 +95,9 @@ namespace Physic public: RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); std::string mName; + + //is this body used for raycasting only? + bool collide; }; /** @@ -161,6 +177,11 @@ namespace Physic */ void setDebugRenderingMode(int mode); + /** + *Return the closest object hit by a ray. If there are no objects, it will return ("",-1). + */ + std::pair rayTest(btVector3& from,btVector3& to); + //event list of non player object std::list NPEventList; From ab1fa41c240b6e8626ffa4bff1ac3479eda593fb Mon Sep 17 00:00:00 2001 From: gugus Date: Wed, 23 Feb 2011 12:28:46 +0100 Subject: [PATCH 138/269] fix camera bug --- ogre/mouselook.cpp | 58 +++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/ogre/mouselook.cpp b/ogre/mouselook.cpp index 84e6e03975..c74f5365c4 100644 --- a/ogre/mouselook.cpp +++ b/ogre/mouselook.cpp @@ -2,6 +2,7 @@ #include #include +#include using namespace OIS; using namespace Ogre; @@ -9,29 +10,48 @@ using namespace OEngine::Render; void MouseLookEvent::event(Type type, int index, const void *p) { - if(type != EV_MouseMove || camera == NULL) return; + if(type != EV_MouseMove || camera == NULL) return; - MouseEvent *arg = (MouseEvent*)(p); + MouseEvent *arg = (MouseEvent*)(p); - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; + float x = arg->state.X.rel * sensX; + float y = arg->state.Y.rel * sensY; - camera->yaw(Degree(-x)); + camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x)); + camera->getParentSceneNode()->pitch(Degree(-y)); + if(flipProt) + { + // The camera before pitching + /*Quaternion nopitch = camera->getParentSceneNode()->getOrientation(); - if(flipProt) - { - // The camera before pitching - Quaternion nopitch = camera->getOrientation(); + camera->getParentSceneNode()->pitch(Degree(-y)); - camera->pitch(Degree(-y)); + // Apply some failsafe measures against the camera flipping + // upside down. Is the camera close to pointing straight up or + // down? + if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1) + // If so, undo the last pitch + camera->getParentSceneNode()->setOrientation(nopitch);*/ + //camera->getU - // Apply some failsafe measures against the camera flipping - // upside down. Is the camera close to pointing straight up or - // down? - if(camera->getUp()[1] <= 0.1) - // If so, undo the last pitch - camera->setOrientation(nopitch); - } - else - camera->pitch(Degree(-y)); + // Angle of rotation around the X-axis. + float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees()); + + // Just to determine the sign of the angle we pick up above, the + // value itself does not interest us. + float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x; + + // Limit the pitch between -90 degress and +90 degrees, Quake3-style. + if (pitchAngle > 90.0f) + { + if (pitchAngleSign > 0) + // Set orientation to 90 degrees on X-axis. + camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), + Ogre::Math::Sqrt(0.5f), 0, 0)); + else if (pitchAngleSign < 0) + // Sets orientation to -90 degrees on X-axis. + camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), + -Ogre::Math::Sqrt(0.5f), 0, 0)); + } + } } From 6c1338821e6bf640a0d54f36effc943aa4d86c6a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 26 Feb 2011 16:34:43 +0100 Subject: [PATCH 139/269] de-Windonizing --- bullet/CMotionState.cpp | 4 ++-- bullet/CMotionState.h | 4 ++-- bullet/btKinematicCharacterController.h | 16 ++++++++-------- bullet/physic.cpp | 18 +++++++++--------- bullet/physic.hpp | 14 +++++++------- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/bullet/CMotionState.cpp b/bullet/CMotionState.cpp index 9cb662caa4..3725fd77a8 100644 --- a/bullet/CMotionState.cpp +++ b/bullet/CMotionState.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include //#include namespace OEngine { @@ -42,4 +42,4 @@ namespace Physic } } -}} \ No newline at end of file +}} diff --git a/bullet/CMotionState.h b/bullet/CMotionState.h index 9ccc35adbb..3dfb3a05cf 100644 --- a/bullet/CMotionState.h +++ b/bullet/CMotionState.h @@ -1,7 +1,7 @@ #ifndef OENGINE_CMOTIONSTATE_H #define OENGINE_CMOTIONSTATE_H -#include +#include #include namespace OEngine { @@ -49,4 +49,4 @@ namespace Physic }; }} -#endif \ No newline at end of file +#endif diff --git a/bullet/btKinematicCharacterController.h b/bullet/btKinematicCharacterController.h index 8f32b32e98..96720dd7d6 100644 --- a/bullet/btKinematicCharacterController.h +++ b/bullet/btKinematicCharacterController.h @@ -4,8 +4,8 @@ Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -17,9 +17,9 @@ subject to the following restrictions: #define KINEMATIC_CHARACTER_CONTROLLER_H #include "LinearMath/btVector3.h" -#include "LinearMath\btQuickprof.h" +#include "LinearMath/btQuickprof.h" -#include "BulletDynamics\Character\btCharacterControllerInterface.h" +#include "BulletDynamics/Character/btCharacterControllerInterface.h" #include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" @@ -46,7 +46,7 @@ public: private: btPairCachingGhostObject* externalGhostObject; // use this for querying collisions for sliding and move btPairCachingGhostObject* internalGhostObject; // and this for recoreving from penetrations - + btScalar m_verticalVelocity; btScalar m_verticalOffset; btScalar m_fallSpeed; @@ -100,15 +100,15 @@ public: btScalar recoveringFactor = btScalar( 0.2 ) ); ~btKinematicCharacterController (); - + ///btActionInterface interface virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime ) { preStep( collisionWorld ); - playerStep( collisionWorld, deltaTime ); + playerStep( collisionWorld, deltaTime ); } - + ///btActionInterface interface void debugDraw( btIDebugDraw* debugDrawer ); diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 76dabce5d9..2375d19e78 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -1,7 +1,7 @@ #include "physic.hpp" #include #include -#include +#include //#include #include "CMotionState.h" #include "OgreRoot.h" @@ -18,7 +18,7 @@ namespace Physic enum collisiontypes { COL_NOTHING = 0, //setWalkDirection( mvt ); } - void PhysicActor::Rotate(btQuaternion& quat) + void PhysicActor::Rotate(const btQuaternion& quat) { externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat ); internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat ); } - void PhysicActor::setRotation(btQuaternion& quat) + void PhysicActor::setRotation(const btQuaternion& quat) { externalGhostObject->getWorldTransform().setRotation( quat ); internalGhostObject->getWorldTransform().setRotation( quat ); @@ -96,7 +96,7 @@ namespace Physic return internalGhostObject->getWorldTransform().getRotation(); } - void PhysicActor::setPosition(btVector3& pos) + void PhysicActor::setPosition(const btVector3& pos) { internalGhostObject->getWorldTransform().setOrigin(pos); externalGhostObject->getWorldTransform().setOrigin(pos); @@ -179,7 +179,7 @@ namespace Physic delete solver; delete collisionConfiguration; delete dispatcher; - delete broadphase; + delete broadphase; delete ShapeLoader; } @@ -232,7 +232,7 @@ namespace Physic RigidBodyMap[name] = NULL; } } - + RigidBody* PhysicEngine::getRigidBody(std::string name) { RigidBody* body = RigidBodyMap[name]; @@ -301,4 +301,4 @@ namespace Physic } return std::pair(name,d); } -}}; \ No newline at end of file +}}; diff --git a/bullet/physic.hpp b/bullet/physic.hpp index 062e69e025..bc33143745 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -1,7 +1,7 @@ #ifndef OENGINE_BULLET_PHYSIC_H #define OENGINE_BULLET_PHYSIC_H -#include +#include #include "BulletCollision/CollisionDispatch/btGhostObject.h" #include #include @@ -35,7 +35,7 @@ namespace Physic struct PhysicEvent; /** - *This is just used to be able to name objects. + *This is just used to be able to name objects. */ class PairCachingGhostObject : public btPairCachingGhostObject { @@ -62,17 +62,17 @@ namespace Physic *I think it's also needed to take time into account. A typical call should look like this: *setWalkDirection( mvt * orientation * dt) */ - void setWalkDirection(btVector3& mvt); + void setWalkDirection(const btVector3& mvt); - void Rotate(btQuaternion& quat); + void Rotate(const btQuaternion& quat); - void setRotation(btQuaternion& quat); + void setRotation(const btQuaternion& quat); btVector3 getPosition(void); btQuaternion getRotation(void); - void setPosition(btVector3& pos); + void setPosition(const btVector3& pos); btKinematicCharacterController* mCharacter; @@ -208,4 +208,4 @@ namespace Physic }} -#endif \ No newline at end of file +#endif From aa0100bc08b59b27a681e872f6318c404e21edd4 Mon Sep 17 00:00:00 2001 From: Jan-Peter Nilsson Date: Sun, 27 Feb 2011 00:44:52 +0100 Subject: [PATCH 140/269] Don't mix the enum type name in there --- bullet/physic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 2375d19e78..d09d299268 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -57,7 +57,7 @@ namespace Physic internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 0.4 ),1,0 ); - mCharacter->setUpAxis(btKinematicCharacterController::UpAxis::Z_AXIS); + mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS); } PhysicActor::~PhysicActor() From 21295664507cca03068f9c2e9878603e2245f5af Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 28 Feb 2011 19:43:11 +0100 Subject: [PATCH 141/269] fix coc bug --- bullet/physic.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 76dabce5d9..733216cc17 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -219,6 +219,13 @@ namespace Physic RigidBody* body = RigidBodyMap[name]; if(body != NULL) { + broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + std::map::iterator it = PhysicActorMap.begin(); + for(;it!=PhysicActorMap.end();it++) + { + it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + } dynamicsWorld->removeRigidBody(RigidBodyMap[name]); } } From 3f0b610f2c5331fbc01617f993b77ead7e5261e8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Mar 2011 14:55:18 +0100 Subject: [PATCH 142/269] throw an exception instead of crashing (ray test bug) --- bullet/physic.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index d09d299268..5974417713 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -219,6 +219,13 @@ namespace Physic RigidBody* body = RigidBodyMap[name]; if(body != NULL) { + broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + std::map::iterator it = PhysicActorMap.begin(); + for(;it!=PhysicActorMap.end();it++) + { + it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + } dynamicsWorld->removeRigidBody(RigidBodyMap[name]); } } @@ -291,11 +298,11 @@ namespace Physic { if(resultCallback.m_collisionFilterGroup == COL_WORLD) { - name = static_cast(resultCallback.m_collisionObject)->mName; + name = dynamic_cast(*resultCallback.m_collisionObject).mName; } if(resultCallback.m_collisionFilterGroup == COL_ACTOR_EXTERNAL || resultCallback.m_collisionFilterGroup == COL_ACTOR_INTERNAL) { - name = static_cast(resultCallback.m_collisionObject)->mName; + name = dynamic_cast(*resultCallback.m_collisionObject).mName; } d = resultCallback.m_closestHitFraction; } From 79fba7e77ed81f0e814d603315b1b4e5a1f4f309 Mon Sep 17 00:00:00 2001 From: gugus Date: Wed, 9 Mar 2011 18:23:13 +0100 Subject: [PATCH 143/269] add gravity --- bullet/physic.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 5974417713..a8425857fd 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -34,11 +34,11 @@ namespace Physic externalGhostObject = new PairCachingGhostObject(name); externalGhostObject->setWorldTransform( transform ); - btScalar externalCapsuleHeight = 50; - btScalar externalCapsuleWidth = 20; + btScalar externalCapsuleHeight = 90; + btScalar externalCapsuleWidth = 16; externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); - externalCollisionShape->setMargin( 1 ); + externalCollisionShape->setMargin( 0.05 ); externalGhostObject->setCollisionShape( externalCollisionShape ); externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); @@ -47,16 +47,16 @@ namespace Physic internalGhostObject = new PairCachingGhostObject(name); internalGhostObject->setWorldTransform( transform ); //internalGhostObject->getBroadphaseHandle()->s - btScalar internalCapsuleHeight = 20; - btScalar internalCapsuleWidth = 5; + btScalar internalCapsuleHeight = 88; + btScalar internalCapsuleWidth = 15; internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); - internalCollisionShape->setMargin( 1 ); + internalCollisionShape->setMargin( 0.05 ); internalGhostObject->setCollisionShape( internalCollisionShape ); internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); - mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 0.4 ),1,0 ); + mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 10 ),1,20,20,9.8,0.2 ); mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS); } From d872d09081a61345dbfcfc51f349c457a1b89a22 Mon Sep 17 00:00:00 2001 From: gugus Date: Thu, 17 Mar 2011 11:00:46 +0100 Subject: [PATCH 144/269] add no gravity, no collision and remove debug messages. --- bullet/btKinematicCharacterController.cpp | 24 ++++++++++++++++------- bullet/btKinematicCharacterController.h | 3 +++ bullet/physic.cpp | 20 ++++++++++++++++--- bullet/physic.hpp | 4 ++++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/bullet/btKinematicCharacterController.cpp b/bullet/btKinematicCharacterController.cpp index 8c81d5a42c..e2bd63ebf3 100644 --- a/bullet/btKinematicCharacterController.cpp +++ b/bullet/btKinematicCharacterController.cpp @@ -22,7 +22,6 @@ subject to the following restrictions: #include "LinearMath/btDefaultMotionState.h" #include "btKinematicCharacterController.h" -#include ///@todo Interact with dynamic objects, ///Ride kinematicly animated platforms properly ///Support ducking @@ -132,6 +131,8 @@ btKinematicCharacterController::btKinematicCharacterController( btPairCachingGho m_wasJumping = false; setMaxSlope( btRadians( 45.0 ) ); + + mCollision = true; } @@ -142,9 +143,10 @@ btKinematicCharacterController::~btKinematicCharacterController () bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld ) { - //std::cout << "recover!!!!"; bool penetration = false; + if(!mCollision) return penetration; + collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher() ); @@ -172,10 +174,6 @@ bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* c for( int p = 0; p < manifold->getNumContacts(); p++ ) { const btManifoldPoint&pt = manifold->getContactPoint( p ); - if(manifold->getBody1() == externalGhostObject) std::cout << "external!!"; - if(manifold->getBody0() == externalGhostObject) std::cout << "external!!"; - if(manifold->getBody1() == internalGhostObject) std::cout << "internal!!"; - if(manifold->getBody0() == internalGhostObject) std::cout << "internal!!"; if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject) ||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) ) { @@ -194,7 +192,6 @@ bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* c currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor; penetration = true; - std::cout << "recover!!!!"; } } } @@ -219,6 +216,13 @@ btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const { btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) ); + //if the no collisions mode is on, no need to go any further + if(!mCollision) + { + currentStepOffset = m_stepHeight; + return targetPosition; + } + // Retrieve the collision shape // btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); @@ -319,6 +323,9 @@ btVector3 btKinematicCharacterController::stepForwardAndStrafe( btCollisionWorld // btVector3 targetPosition = currentPosition + walkMove; + //if the no collisions mode is on, no need to go any further + if(!mCollision) return targetPosition; + // Retrieve the collision shape // btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); @@ -400,6 +407,9 @@ btVector3 btKinematicCharacterController::stepDown( btCollisionWorld* collisionW // btVector3 targetPosition = currentPosition - stepDrop; + //if the no collisions mode is on, no need to go any further + if(!mCollision) return targetPosition; + btTransform start; start.setIdentity(); start.setOrigin( currentPosition ); diff --git a/bullet/btKinematicCharacterController.h b/bullet/btKinematicCharacterController.h index 96720dd7d6..e851e1cb82 100644 --- a/bullet/btKinematicCharacterController.h +++ b/bullet/btKinematicCharacterController.h @@ -159,6 +159,9 @@ public: } bool onGround() const; + + //if set to false, there will be no collision. + bool mCollision; }; #endif // KINEMATIC_CHARACTER_CONTROLLER_H diff --git a/bullet/physic.cpp b/bullet/physic.cpp index a8425857fd..fe491f6c6e 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -34,7 +34,7 @@ namespace Physic externalGhostObject = new PairCachingGhostObject(name); externalGhostObject->setWorldTransform( transform ); - btScalar externalCapsuleHeight = 90; + btScalar externalCapsuleHeight = 130; btScalar externalCapsuleWidth = 16; externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); @@ -47,7 +47,7 @@ namespace Physic internalGhostObject = new PairCachingGhostObject(name); internalGhostObject->setWorldTransform( transform ); //internalGhostObject->getBroadphaseHandle()->s - btScalar internalCapsuleHeight = 88; + btScalar internalCapsuleHeight = 120; btScalar internalCapsuleWidth = 15; internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); @@ -56,8 +56,12 @@ namespace Physic internalGhostObject->setCollisionShape( internalCollisionShape ); internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); - mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 10 ),1,20,20,9.8,0.2 ); + mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 10 ),1,9.8,20,9.8,0.2 ); mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS); + mCharacter->setUseGhostSweepTest(false); + + mCharacter->mCollision = false; + setGravity(0); } PhysicActor::~PhysicActor() @@ -69,6 +73,16 @@ namespace Physic delete externalCollisionShape; } + void PhysicActor::setGravity(float gravity) + { + mCharacter->setGravity(gravity); + } + + void PhysicActor::enableCollisions(bool collision) + { + mCharacter->mCollision = collision; + } + void PhysicActor::setWalkDirection(const btVector3& mvt) { mCharacter->setWalkDirection( mvt ); diff --git a/bullet/physic.hpp b/bullet/physic.hpp index bc33143745..6115628fa3 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -68,6 +68,10 @@ namespace Physic void setRotation(const btQuaternion& quat); + void setGravity(float gravity); + + void enableCollisions(bool collision); + btVector3 getPosition(void); btQuaternion getRotation(void); From 25b3cf935967beb5011419b6d3911fca05863743 Mon Sep 17 00:00:00 2001 From: gugus Date: Fri, 18 Mar 2011 13:24:47 +0100 Subject: [PATCH 145/269] OEngine doesn't rely anymore on OpenMW+ some change for the toggleCollisionMode function. --- bullet/BulletShapeLoader.cpp | 124 +++++++++++++++++++++++++++++++ bullet/BulletShapeLoader.h | 138 +++++++++++++++++++++++++++++++++++ bullet/physic.cpp | 29 +++++--- bullet/physic.hpp | 17 +++-- 4 files changed, 291 insertions(+), 17 deletions(-) create mode 100644 bullet/BulletShapeLoader.cpp create mode 100644 bullet/BulletShapeLoader.h diff --git a/bullet/BulletShapeLoader.cpp b/bullet/BulletShapeLoader.cpp new file mode 100644 index 0000000000..927ec6a27a --- /dev/null +++ b/bullet/BulletShapeLoader.cpp @@ -0,0 +1,124 @@ +#include "BulletShapeLoader.h" + + + +BulletShape::BulletShape(Ogre::ResourceManager* creator, const Ogre::String &name, + Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual, + Ogre::ManualResourceLoader *loader) : +Ogre::Resource(creator, name, handle, group, isManual, loader) +{ + /* If you were storing a pointer to an object, then you would set that pointer to NULL here. + */ + + /* For consistency with StringInterface, but we don't add any parameters here + That's because the Resource implementation of StringInterface is to + list all the options that need to be set before loading, of which + we have none as such. Full details can be set through scripts. + */ + Shape = NULL; + collide = true; + createParamDictionary("BulletShape"); +} + +BulletShape::~BulletShape() +{ +} + +// farm out to BulletShapeLoader +void BulletShape::loadImpl() +{ + mLoader->loadResource(this); +} + +void BulletShape::deleteShape(btCollisionShape* mShape) +{ + if(mShape!=NULL) + { + if(mShape->isCompound()) + { + btCompoundShape* ms = static_cast(Shape); + int a = ms->getNumChildShapes(); + for(int i=0; i getChildShape(i)); + } + } + delete mShape; + } + mShape = NULL; +} + +void BulletShape::unloadImpl() +{ + deleteShape(Shape); +} + +//TODO:change this? +size_t BulletShape::calculateSize() const +{ + return 1; +} + + + +//============================================================================================================= +template<> BulletShapeManager *Ogre::Singleton::ms_Singleton = 0; + +BulletShapeManager *BulletShapeManager::getSingletonPtr() +{ + return ms_Singleton; +} + +BulletShapeManager &BulletShapeManager::getSingleton() +{ + assert(ms_Singleton); + return(*ms_Singleton); +} + +BulletShapeManager::BulletShapeManager() +{ + mResourceType = "BulletShape"; + + // low, because it will likely reference other resources + mLoadOrder = 30.0f; + + // this is how we register the ResourceManager with OGRE + Ogre::ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this); +} + +BulletShapeManager::~BulletShapeManager() +{ + // and this is how we unregister it + Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType); +} + +BulletShapePtr BulletShapeManager::load(const Ogre::String &name, const Ogre::String &group) +{ + BulletShapePtr textf = getByName(name); + + if (textf.isNull()) + textf = create(name, group); + + textf->load(); + return textf; +} + +Ogre::Resource *BulletShapeManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle, + const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, + const Ogre::NameValuePairList *createParams) +{ + BulletShape* res = new BulletShape(this, name, handle, group, isManual, loader); + //if(isManual) + //{ + //loader->loadResource(res); + //} + return res; +} + + +//==================================================================== +void BulletShapeLoader::loadResource(Ogre::Resource *resource) +{} + +void BulletShapeLoader::load(const std::string &name,const std::string &group) +{} \ No newline at end of file diff --git a/bullet/BulletShapeLoader.h b/bullet/BulletShapeLoader.h new file mode 100644 index 0000000000..e28a7175e3 --- /dev/null +++ b/bullet/BulletShapeLoader.h @@ -0,0 +1,138 @@ +#ifndef _BULLET_SHAPE_LOADER_H_ +#define _BULLET_SHAPE_LOADER_H_ + +#include +#include +#include + +//For some reason, Ogre Singleton cannot be used in another namespace, that's why there is no namespace here. +//But the risk of name collision seems pretty low here. + +/** +*Define a new resource which describe a Shape usable by bullet.See BulletShapeManager for how to get/use them. +*/ +class BulletShape : public Ogre::Resource +{ + Ogre::String mString; + +protected: + void loadImpl(); + void unloadImpl(); + size_t calculateSize() const; + + void deleteShape(btCollisionShape* mShape); + +public: + + BulletShape(Ogre::ResourceManager *creator, const Ogre::String &name, + Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual = false, + Ogre::ManualResourceLoader *loader = 0); + + virtual ~BulletShape(); + + btCollisionShape* Shape; + //this flag indicate if the shape is used for collision or if it's for raycasting only. + bool collide; +}; + +/** +* +*/ +class BulletShapePtr : public Ogre::SharedPtr +{ +public: + BulletShapePtr() : Ogre::SharedPtr() {} + explicit BulletShapePtr(BulletShape *rep) : Ogre::SharedPtr(rep) {} + BulletShapePtr(const BulletShapePtr &r) : Ogre::SharedPtr(r) {} + BulletShapePtr(const Ogre::ResourcePtr &r) : Ogre::SharedPtr() + { + if( r.isNull() ) + return; + // lock & copy other mutex pointer + OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME) + OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME) + pRep = static_cast(r.getPointer()); + pUseCount = r.useCountPointer(); + useFreeMethod = r.freeMethod(); + if (pUseCount) + { + ++(*pUseCount); + } + } + + /// Operator used to convert a ResourcePtr to a BulletShapePtr + BulletShapePtr& operator=(const Ogre::ResourcePtr& r) + { + if(pRep == static_cast(r.getPointer())) + return *this; + release(); + if( r.isNull() ) + return *this; // resource ptr is null, so the call to release above has done all we need to do. + // lock & copy other mutex pointer + OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME) + OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME) + pRep = static_cast(r.getPointer()); + pUseCount = r.useCountPointer(); + useFreeMethod = r.freeMethod(); + if (pUseCount) + { + ++(*pUseCount); + } + return *this; + } +}; + + + + +/** +*Hold any BulletShape that was created by the ManualBulletShapeLoader. +* +*To get a bulletShape, you must load it first. +*First, create a manualBulletShapeLoader. Then call ManualBulletShapeManager->load(). This create an "empty" resource. +*Then use BulletShapeManager->load(). This will fill the resource with the required info. +*To get the resource,use BulletShapeManager::getByName. +*When you use the resource no more, just use BulletShapeManager->unload(). It won't completly delete the resource, but it will +*"empty" it.This allow a better management of memory: when you are leaving a cell, just unload every useless shape. +* +*Alternatively, you can call BulletShape->load() in order to actually load the resource. +*When you are finished with it, just call BulletShape->unload(). +* +*IMO: prefere the first methode, i am not completly sure about the 2nd. +* +*Important Note: i have no idea of what happen if you try to load two time the same resource without unloading. +*It won't crash, but it might lead to memory leaks(I don't know how Ogre handle this). So don't do it! +*/ +class BulletShapeManager : public Ogre::ResourceManager, public Ogre::Singleton +{ +protected: + + // must implement this from ResourceManager's interface + Ogre::Resource *createImpl(const Ogre::String &name, Ogre::ResourceHandle handle, + const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, + const Ogre::NameValuePairList *createParams); + +public: + + BulletShapeManager(); + virtual ~BulletShapeManager(); + + virtual BulletShapePtr load(const Ogre::String &name, const Ogre::String &group); + + static BulletShapeManager &getSingleton(); + static BulletShapeManager *getSingletonPtr(); +}; + +class BulletShapeLoader : public Ogre::ManualResourceLoader +{ +public: + + BulletShapeLoader(){}; + virtual ~BulletShapeLoader() {} + + virtual void loadResource(Ogre::Resource *resource); + + virtual void load(const std::string &name,const std::string &group); +}; + +#endif \ No newline at end of file diff --git a/bullet/physic.cpp b/bullet/physic.cpp index fe491f6c6e..d0be072f33 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -83,6 +83,11 @@ namespace Physic mCharacter->mCollision = collision; } + bool PhysicActor::getCollisionMode() + { + return mCharacter->mCollision; + } + void PhysicActor::setWalkDirection(const btVector3& mvt) { mCharacter->setWalkDirection( mvt ); @@ -133,7 +138,7 @@ namespace Physic - PhysicEngine::PhysicEngine() + PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) { // Set up the collision configuration and dispatcher collisionConfiguration = new btDefaultCollisionConfiguration(); @@ -157,7 +162,7 @@ namespace Physic new BulletShapeManager(); } //TODO:singleton? - ShapeLoader = new ManualBulletShapeLoader(); + mShapeLoader = shapeLoader; isDebugCreated = false; } @@ -178,14 +183,14 @@ namespace Physic } } - void PhysicEngine::setDebugRenderingMode(int mode) - { - if(!isDebugCreated) - { - createDebugRendering(); - } - mDebugDrawer->setDebugMode(mode); - } + void PhysicEngine::setDebugRenderingMode(int mode) + { + if(!isDebugCreated) + { + createDebugRendering(); + } + mDebugDrawer->setDebugMode(mode); + } PhysicEngine::~PhysicEngine() { @@ -194,13 +199,13 @@ namespace Physic delete collisionConfiguration; delete dispatcher; delete broadphase; - delete ShapeLoader; + delete mShapeLoader; } RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name) { //get the shape from the .nif - ShapeLoader->load(mesh,"General"); + mShapeLoader->load(mesh,"General"); BulletShapeManager::getSingletonPtr()->load(mesh,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); diff --git a/bullet/physic.hpp b/bullet/physic.hpp index 6115628fa3..9178a57799 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -6,6 +6,7 @@ #include #include #include +#include "BulletShapeLoader.h" class btRigidBody; class btBroadphaseInterface; @@ -20,9 +21,6 @@ namespace BtOgre class DebugDrawer; } -class BulletShapeManager; -class ManualBulletShapeLoader; - namespace MWWorld { class World; @@ -72,6 +70,8 @@ namespace Physic void enableCollisions(bool collision); + bool getCollisionMode(); + btVector3 getPosition(void); btQuaternion getRotation(void); @@ -113,7 +113,14 @@ namespace Physic class PhysicEngine { public: - PhysicEngine(); + /** + *Note that the shapeLoader IS destroyed by the phyic Engine!! + */ + PhysicEngine(BulletShapeLoader* shapeLoader); + + /** + *It DOES destroy the shape loader! + */ ~PhysicEngine(); /** @@ -200,7 +207,7 @@ namespace Physic btDiscreteDynamicsWorld* dynamicsWorld; //the NIF file loader. - ManualBulletShapeLoader* ShapeLoader; + BulletShapeLoader* mShapeLoader; std::map RigidBodyMap; std::map PhysicActorMap; From 8f9b8749d4b9e8c8c1093258a619997d704c6f76 Mon Sep 17 00:00:00 2001 From: gugus Date: Sun, 20 Mar 2011 21:51:40 +0100 Subject: [PATCH 146/269] fix the fall bug when using ToggleCollision --- bullet/btKinematicCharacterController.cpp | 4 ++++ bullet/btKinematicCharacterController.h | 1 + bullet/physic.cpp | 6 ++++++ bullet/physic.hpp | 2 ++ 4 files changed, 13 insertions(+) diff --git a/bullet/btKinematicCharacterController.cpp b/bullet/btKinematicCharacterController.cpp index e2bd63ebf3..d930349850 100644 --- a/bullet/btKinematicCharacterController.cpp +++ b/bullet/btKinematicCharacterController.cpp @@ -140,6 +140,10 @@ btKinematicCharacterController::~btKinematicCharacterController () { } +void btKinematicCharacterController::setVerticalVelocity(float z) +{ + m_verticalVelocity = z; +} bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld ) { diff --git a/bullet/btKinematicCharacterController.h b/bullet/btKinematicCharacterController.h index e851e1cb82..d58e242ad3 100644 --- a/bullet/btKinematicCharacterController.h +++ b/bullet/btKinematicCharacterController.h @@ -101,6 +101,7 @@ public: ~btKinematicCharacterController (); + void setVerticalVelocity(float z); ///btActionInterface interface virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime ) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index d0be072f33..742f994365 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -76,6 +76,7 @@ namespace Physic void PhysicActor::setGravity(float gravity) { mCharacter->setGravity(gravity); + //mCharacter-> } void PhysicActor::enableCollisions(bool collision) @@ -83,6 +84,11 @@ namespace Physic mCharacter->mCollision = collision; } + void PhysicActor::setVerticalVelocity(float z) + { + mCharacter->setVerticalVelocity(z); + } + bool PhysicActor::getCollisionMode() { return mCharacter->mCollision; diff --git a/bullet/physic.hpp b/bullet/physic.hpp index 9178a57799..0cf6abd4ff 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -67,6 +67,8 @@ namespace Physic void setRotation(const btQuaternion& quat); void setGravity(float gravity); + + void setVerticalVelocity(float z); void enableCollisions(bool collision); From 590f9e83d8158970dcbef1c5078a1a04cd8f1a59 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 22 Mar 2011 14:12:25 +0100 Subject: [PATCH 147/269] Windows-fix for gus --- sound/sources/audiere_source.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/sources/audiere_source.cpp b/sound/sources/audiere_source.cpp index 4e5b6adce2..faaa3c8c5b 100644 --- a/sound/sources/audiere_source.cpp +++ b/sound/sources/audiere_source.cpp @@ -14,7 +14,8 @@ using namespace Mangle::Sound; // --- SampleSource --- -void AudiereSource::getInfo(int32_t *rate, int32_t *channels, int32_t *bits) +void AudiereSource::getInfo(Mangle::Sound::int32_t *rate, + Mangle::Sound::int32_t *channels, Mangle::Sound::int32_t *bits) { SampleFormat fmt; int channels_, rate_; From 1ce6405e2bf5388a05a5cc0a60d1c5fe3dcadbfb Mon Sep 17 00:00:00 2001 From: gugus Date: Tue, 22 Mar 2011 21:28:18 +0100 Subject: [PATCH 148/269] fix the raycasting bug --- bullet/physic.cpp | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 742f994365..254e3db7d3 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -38,7 +38,7 @@ namespace Physic btScalar externalCapsuleWidth = 16; externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); - externalCollisionShape->setMargin( 0.05 ); + externalCollisionShape->setMargin( 0.1 ); externalGhostObject->setCollisionShape( externalCollisionShape ); externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); @@ -51,12 +51,12 @@ namespace Physic btScalar internalCapsuleWidth = 15; internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); - internalCollisionShape->setMargin( 0.05 ); + internalCollisionShape->setMargin( 0.1 ); internalGhostObject->setCollisionShape( internalCollisionShape ); internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); - mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 10 ),1,9.8,20,9.8,0.2 ); + mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 40 ),1,4,20,9.8,0.2 ); mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS); mCharacter->setUseGhostSweepTest(false); @@ -273,7 +273,7 @@ namespace Physic void PhysicEngine::stepSimulation(double deltaT) { - dynamicsWorld->stepSimulation(deltaT,1,1/30.); + dynamicsWorld->stepSimulation(deltaT,1,1/50.); if(isDebugCreated) { mDebugDrawer->step(); @@ -315,22 +315,33 @@ namespace Physic std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to) { std::string name = ""; - float d = -1.; - btCollisionWorld::ClosestRayResultCallback resultCallback(from, to); - dynamicsWorld->rayTest(from, to, resultCallback); + float d = -1; - if (resultCallback.hasHit()) + float d1 = 10000.; + btCollisionWorld::ClosestRayResultCallback resultCallback1(from, to); + resultCallback1.m_collisionFilterMask = COL_WORLD; + dynamicsWorld->rayTest(from, to, resultCallback1); + if (resultCallback1.hasHit()) { - if(resultCallback.m_collisionFilterGroup == COL_WORLD) - { - name = dynamic_cast(*resultCallback.m_collisionObject).mName; - } - if(resultCallback.m_collisionFilterGroup == COL_ACTOR_EXTERNAL || resultCallback.m_collisionFilterGroup == COL_ACTOR_INTERNAL) - { - name = dynamic_cast(*resultCallback.m_collisionObject).mName; - } - d = resultCallback.m_closestHitFraction; + name = static_cast(*resultCallback1.m_collisionObject).mName; + d1 = resultCallback1.m_closestHitFraction; + d = d1; } + + btCollisionWorld::ClosestRayResultCallback resultCallback2(from, to); + resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL; + dynamicsWorld->rayTest(from, to, resultCallback2); + float d2 = 10000.; + if (resultCallback2.hasHit()) + { + d2 = resultCallback1.m_closestHitFraction; + if(d2<=d1) + { + name = static_cast(*resultCallback2.m_collisionObject).mName; + d = d2; + } + } + return std::pair(name,d); } }}; From 1cada5032ec06e3dd6c0ff8ece49aa588667bd0f Mon Sep 17 00:00:00 2001 From: gugus Date: Wed, 23 Mar 2011 19:17:45 +0100 Subject: [PATCH 149/269] fix delete actor --- bullet/physic.cpp | 77 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 254e3db7d3..070f31779c 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -241,28 +241,37 @@ namespace Physic void PhysicEngine::removeRigidBody(std::string name) { - RigidBody* body = RigidBodyMap[name]; - if(body != NULL) - { - broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - std::map::iterator it = PhysicActorMap.begin(); - for(;it!=PhysicActorMap.end();it++) + std::map::iterator it = RigidBodyMap.find(name); + if (it != RigidBodyMap.end() ) + { + RigidBody* body = it->second; + if(body != NULL) { - it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + std::map::iterator it = PhysicActorMap.begin(); + for(;it!=PhysicActorMap.end();it++) + { + it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + } + dynamicsWorld->removeRigidBody(RigidBodyMap[name]); } - dynamicsWorld->removeRigidBody(RigidBodyMap[name]); - } + } } void PhysicEngine::deleteRigidBody(std::string name) { - RigidBody* body = RigidBodyMap[name]; - if(body != NULL) - { - delete body; - RigidBodyMap[name] = NULL; - } + std::map::iterator it = RigidBodyMap.find(name); + if (it != RigidBodyMap.end() ) + { + RigidBody* body = it->second; + if(body != NULL) + { + delete body; + RigidBodyMap[name] = NULL; + RigidBodyMap.erase(it); + } + } } RigidBody* PhysicEngine::getRigidBody(std::string name) @@ -291,15 +300,33 @@ namespace Physic void PhysicEngine::removeCharacter(std::string name) { - PhysicActor* act = PhysicActorMap[name]; - if(act != NULL) - { - dynamicsWorld->removeCollisionObject(act->externalGhostObject); - dynamicsWorld->removeCollisionObject(act->internalGhostObject); - dynamicsWorld->removeAction(act->mCharacter); - delete act; - PhysicActorMap[name] = NULL; - } + //std::cout << "remove"; + std::map::iterator it = PhysicActorMap.find(name); + if (it != PhysicActorMap.end() ) + { + PhysicActor* act = it->second; + if(act != NULL) + { + /*broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); + broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); + std::map::iterator it2 = PhysicActorMap.begin(); + for(;it2!=PhysicActorMap.end();it++) + { + it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); + it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); + it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); + it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); + }*/ + //act->externalGhostObject-> + dynamicsWorld->removeCollisionObject(act->externalGhostObject); + dynamicsWorld->removeCollisionObject(act->internalGhostObject); + dynamicsWorld->removeAction(act->mCharacter); + delete act; + PhysicActorMap[name] = NULL; + PhysicActorMap.erase(it); + } + } + //std::cout << "ok"; } PhysicActor* PhysicEngine::getCharacter(std::string name) From 32b475432f3e1c8d7275d17da0d692c173f30478 Mon Sep 17 00:00:00 2001 From: gugus Date: Wed, 23 Mar 2011 22:49:23 +0100 Subject: [PATCH 150/269] fix the bug when changing cell. --- bullet/physic.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 070f31779c..986b57be5d 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -247,13 +247,13 @@ namespace Physic RigidBody* body = it->second; if(body != NULL) { - broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - std::map::iterator it = PhysicActorMap.begin(); - for(;it!=PhysicActorMap.end();it++) + // broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + /*std::map::iterator it2 = PhysicActorMap.begin(); + for(;it2!=PhysicActorMap.end();it++) { - it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - } + it2->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + it2->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + }*/ dynamicsWorld->removeRigidBody(RigidBodyMap[name]); } } @@ -268,9 +268,8 @@ namespace Physic if(body != NULL) { delete body; - RigidBodyMap[name] = NULL; - RigidBodyMap.erase(it); } + RigidBodyMap.erase(it); } } @@ -322,9 +321,8 @@ namespace Physic dynamicsWorld->removeCollisionObject(act->internalGhostObject); dynamicsWorld->removeAction(act->mCharacter); delete act; - PhysicActorMap[name] = NULL; - PhysicActorMap.erase(it); } + PhysicActorMap.erase(it); } //std::cout << "ok"; } From c78e61c96ff5bdbb8e0deac4ef80f4064e58c837 Mon Sep 17 00:00:00 2001 From: Jan-Peter Nilsson Date: Sat, 2 Apr 2011 13:06:05 +0200 Subject: [PATCH 151/269] Initialize member variables in the same order they are defined to avoid compiler warning --- bullet/BtOgre.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bullet/BtOgre.cpp b/bullet/BtOgre.cpp index 82ed2cbc27..5dca915b61 100644 --- a/bullet/BtOgre.cpp +++ b/bullet/BtOgre.cpp @@ -379,10 +379,10 @@ namespace BtOgre { mIndexBuffer (0), mVertexCount (0), mIndexCount (0), - mBounds (Vector3(-1,-1,-1)), - mBoundRadius (-1), - mBoneIndex (0), mTransform (transform), + mBoundRadius (-1), + mBounds (Vector3(-1,-1,-1)), + mBoneIndex (0), mScale(1) { } From f3c9694bf249a34eae05f0304e6bfc120014ce8c Mon Sep 17 00:00:00 2001 From: Jan-Peter Nilsson Date: Sun, 3 Apr 2011 13:06:42 +0200 Subject: [PATCH 152/269] Whitespace changes only --- rend2d/servers/sdl_gl_driver.cpp | 14 +++++++------- sound/sources/ffmpeg_source.cpp | 2 +- sound/sources/sample_reader.cpp | 2 +- stream/tests/buffer_filter_test.cpp | 2 +- stream/tests/iostream_test.cpp | 2 +- stream/tests/memory_server_test.cpp | 2 +- vfs/clients/ogre_archive.cpp | 2 +- vfs/clients/ogre_archive.hpp | 2 +- vfs/vfs.hpp | 4 ++-- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/rend2d/servers/sdl_gl_driver.cpp b/rend2d/servers/sdl_gl_driver.cpp index e2e829a7ad..2bcb1d6777 100644 --- a/rend2d/servers/sdl_gl_driver.cpp +++ b/rend2d/servers/sdl_gl_driver.cpp @@ -169,10 +169,10 @@ void SDLGLDriver::setVideoMode(int width, int height, int bpp, bool fullscreen) // Have OpenGL generate a texture object handle for us glGenTextures( 1, &texture ); - + // Bind the texture object glBindTexture( GL_TEXTURE_2D, texture ); - + // Set the texture's stretching properties glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); @@ -213,24 +213,24 @@ void SDLGLDriver::updateNoSwap() // Bottom-left vertex (corner) glTexCoord2i( 0, 1 ); glVertex3f(0,diff,0); - + // Bottom-right vertex (corner) glTexCoord2i( 1, 1 ); glVertex3f( realDisp->w, diff, 0.f ); - + // Top-right vertex (corner) glTexCoord2i( 1, 0 ); glVertex3f( realDisp->w, screen->h, 0.f ); - + // Top-left vertex (corner) glTexCoord2i( 0, 0 ); glVertex3f( 0, screen->h, 0.f ); glEnd(); glMatrixMode(GL_PROJECTION); - glPopMatrix(); + glPopMatrix(); glMatrixMode(GL_MODELVIEW); - glPopMatrix(); + glPopMatrix(); } void SDLGLDriver::swap() diff --git a/sound/sources/ffmpeg_source.cpp b/sound/sources/ffmpeg_source.cpp index fdc2ff9541..6349be6913 100644 --- a/sound/sources/ffmpeg_source.cpp +++ b/sound/sources/ffmpeg_source.cpp @@ -34,7 +34,7 @@ FFMpegSource::FFMpegSource(const std::string &file) if(av_open_input_file(&FmtCtx, file.c_str(), NULL, 0, NULL) != 0) fail("Error loading audio file " + file); - + if(av_find_stream_info(FmtCtx) < 0) { msg = "Error in file stream " + file; diff --git a/sound/sources/sample_reader.cpp b/sound/sources/sample_reader.cpp index fb4be9d5a3..c30de654a5 100644 --- a/sound/sources/sample_reader.cpp +++ b/sound/sources/sample_reader.cpp @@ -72,7 +72,7 @@ size_t SampleReader::read(void *_data, size_t length) // Determine how much we read return data-(char*)_data; } - + // Determine the overshoot pullSize = length - num; assert(pullSize < frameSize && pullSize >= 0); diff --git a/stream/tests/buffer_filter_test.cpp b/stream/tests/buffer_filter_test.cpp index 971530d845..e53bda6510 100644 --- a/stream/tests/buffer_filter_test.cpp +++ b/stream/tests/buffer_filter_test.cpp @@ -36,6 +36,6 @@ int main() cout << "Result: " << data << endl; cout << "Eof: " << inp->eof() << endl; cout << "Pos: " << inp->tell() << endl; - + return 0; } diff --git a/stream/tests/iostream_test.cpp b/stream/tests/iostream_test.cpp index 6acebbbd61..60648c6c5f 100644 --- a/stream/tests/iostream_test.cpp +++ b/stream/tests/iostream_test.cpp @@ -76,7 +76,7 @@ void test2() struct Dummy2 : Stream { - Dummy2() + Dummy2() { isWritable = true; isReadable = false; diff --git a/stream/tests/memory_server_test.cpp b/stream/tests/memory_server_test.cpp index 24dea79a2d..24d3bb17e0 100644 --- a/stream/tests/memory_server_test.cpp +++ b/stream/tests/memory_server_test.cpp @@ -37,6 +37,6 @@ int main() cout << "Pos: " << inp->tell() << endl; cout << "Entire stream from pointer: " << (char*)inp->getPtr() << endl; - + return 0; } diff --git a/vfs/clients/ogre_archive.cpp b/vfs/clients/ogre_archive.cpp index ed63370539..2d3f7c5204 100644 --- a/vfs/clients/ogre_archive.cpp +++ b/vfs/clients/ogre_archive.cpp @@ -71,7 +71,7 @@ Ogre::StringVectorPtr MangleArchive::find(const Ogre::String& pattern, return Ogre::StringVectorPtr(res); } -Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, +Ogre::FileInfoListPtr MangleArchive::findFileInfo(const Ogre::String& pattern, bool recursive, bool dirs) { diff --git a/vfs/clients/ogre_archive.hpp b/vfs/clients/ogre_archive.hpp index 7cba6cfc8e..7b371c6efe 100644 --- a/vfs/clients/ogre_archive.hpp +++ b/vfs/clients/ogre_archive.hpp @@ -50,7 +50,7 @@ class MangleArchive : public Ogre::Archive // Find functions will only work if vfs->hasFind is set. Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true, bool dirs = false); - Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, + Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, bool recursive = true, bool dirs = false); }; diff --git a/vfs/vfs.hpp b/vfs/vfs.hpp index ce51be94b1..258a6b0a0e 100644 --- a/vfs/vfs.hpp +++ b/vfs/vfs.hpp @@ -13,7 +13,7 @@ struct FileInfo { /// Full name, including path std::string name; - + /// Base name, not including path std::string basename; @@ -22,7 +22,7 @@ struct FileInfo /// File size size_t size; - + /// Last modification date time_t time; }; From 0f7d59b4fb742c6479d988f6fc4ec9cdb4330b53 Mon Sep 17 00:00:00 2001 From: Jan-Peter Nilsson Date: Sun, 3 Apr 2011 13:12:12 +0200 Subject: [PATCH 153/269] Whitespace changes only (including new mangle with whitespace changes as well) --- bullet/BtOgre.cpp | 1762 +++++++++++---------- bullet/BtOgreExtras.h | 168 +- bullet/BtOgreGP.h | 127 +- bullet/BtOgrePG.h | 12 +- bullet/BulletShapeLoader.cpp | 18 +- bullet/BulletShapeLoader.h | 8 +- bullet/CMotionState.cpp | 54 +- bullet/CMotionState.h | 66 +- bullet/btKinematicCharacterController.cpp | 674 ++++---- bullet/btKinematicCharacterController.h | 138 +- bullet/physic.cpp | 390 ++--- bullet/physic.hpp | 284 ++-- mangle | 2 +- ogre/mouselook.cpp | 72 +- ogre/renderer.cpp | 2 +- 15 files changed, 1911 insertions(+), 1866 deletions(-) diff --git a/bullet/BtOgre.cpp b/bullet/BtOgre.cpp index 5dca915b61..6187390832 100644 --- a/bullet/BtOgre.cpp +++ b/bullet/BtOgre.cpp @@ -27,365 +27,380 @@ namespace BtOgre { * ============================================================================================= */ - void VertexIndexToShape::addStaticVertexData(const VertexData *vertex_data) - { - if (!vertex_data) - return; + void VertexIndexToShape::addStaticVertexData(const VertexData *vertex_data) + { + if (!vertex_data) + return; - const VertexData *data = vertex_data; + const VertexData *data = vertex_data; - const unsigned int prev_size = mVertexCount; - mVertexCount += (unsigned int)data->vertexCount; + const unsigned int prev_size = mVertexCount; + mVertexCount += (unsigned int)data->vertexCount; - Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount]; - if (mVertexBuffer) - { - memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); - delete[] mVertexBuffer; - } - mVertexBuffer = tmp_vert; + Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount]; + if (mVertexBuffer) + { + memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); + delete[] mVertexBuffer; + } + mVertexBuffer = tmp_vert; - // Get the positional buffer element - { - const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); - Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource()); - const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + // Get the positional buffer element + { + const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); + Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); - unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); - float* pReal; - Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; - const unsigned int vertexCount = (unsigned int)data->vertexCount; - for(unsigned int j = 0; j < vertexCount; ++j) - { - posElem->baseVertexPointerToElement(vertex, &pReal); - vertex += vSize; + unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); + float* pReal; + Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; + const unsigned int vertexCount = (unsigned int)data->vertexCount; + for(unsigned int j = 0; j < vertexCount; ++j) + { + posElem->baseVertexPointerToElement(vertex, &pReal); + vertex += vSize; - curVertices->x = (*pReal++); - curVertices->y = (*pReal++); - curVertices->z = (*pReal++); + curVertices->x = (*pReal++); + curVertices->y = (*pReal++); + curVertices->z = (*pReal++); - *curVertices = mTransform * (*curVertices); - - curVertices++; - } - vbuf->unlock(); - } - } - //------------------------------------------------------------------------------------------------ - void VertexIndexToShape::addAnimatedVertexData(const Ogre::VertexData *vertex_data, - const Ogre::VertexData *blend_data, - const Ogre::Mesh::IndexMap *indexMap) - { - // Get the bone index element - assert(vertex_data); + *curVertices = mTransform * (*curVertices); - const VertexData *data = blend_data; - const unsigned int prev_size = mVertexCount; - mVertexCount += (unsigned int)data->vertexCount; - Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount]; - if (mVertexBuffer) - { - memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); - delete[] mVertexBuffer; - } - mVertexBuffer = tmp_vert; + curVertices++; + } + vbuf->unlock(); + } + } - // Get the positional buffer element - { - const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); - assert (posElem); - Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource()); - const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + //------------------------------------------------------------------------------------------------ + void VertexIndexToShape::addAnimatedVertexData(const Ogre::VertexData *vertex_data, + const Ogre::VertexData *blend_data, + const Ogre::Mesh::IndexMap *indexMap) + { + // Get the bone index element + assert(vertex_data); - unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); - float* pReal; - Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; - const unsigned int vertexCount = (unsigned int)data->vertexCount; - for(unsigned int j = 0; j < vertexCount; ++j) - { - posElem->baseVertexPointerToElement(vertex, &pReal); - vertex += vSize; + const VertexData *data = blend_data; + const unsigned int prev_size = mVertexCount; + mVertexCount += (unsigned int)data->vertexCount; + Ogre::Vector3* tmp_vert = new Ogre::Vector3[mVertexCount]; + if (mVertexBuffer) + { + memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); + delete[] mVertexBuffer; + } + mVertexBuffer = tmp_vert; - curVertices->x = (*pReal++); - curVertices->y = (*pReal++); - curVertices->z = (*pReal++); + // Get the positional buffer element + { + const Ogre::VertexElement* posElem = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); + assert (posElem); + Ogre::HardwareVertexBufferSharedPtr vbuf = data->vertexBufferBinding->getBuffer(posElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); - *curVertices = mTransform * (*curVertices); + unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); + float* pReal; + Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; + const unsigned int vertexCount = (unsigned int)data->vertexCount; + for(unsigned int j = 0; j < vertexCount; ++j) + { + posElem->baseVertexPointerToElement(vertex, &pReal); + vertex += vSize; - curVertices++; - } - vbuf->unlock(); - } - { - const Ogre::VertexElement* bneElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_BLEND_INDICES); - assert (bneElem); - - Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(bneElem->getSource()); - const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); - unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); + curVertices->x = (*pReal++); + curVertices->y = (*pReal++); + curVertices->z = (*pReal++); - unsigned char* pBone; + *curVertices = mTransform * (*curVertices); - if (!mBoneIndex) - mBoneIndex = new BoneIndex(); - BoneIndex::iterator i; + curVertices++; + } + vbuf->unlock(); + } - Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; + { + const Ogre::VertexElement* bneElem = vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_BLEND_INDICES); + assert (bneElem); - const unsigned int vertexCount = (unsigned int)vertex_data->vertexCount; - for(unsigned int j = 0; j < vertexCount; ++j) - { - bneElem->baseVertexPointerToElement(vertex, &pBone); - vertex += vSize; + Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(bneElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + unsigned char* vertex = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); - const unsigned char currBone = (indexMap) ? (*indexMap)[*pBone] : *pBone; - i = mBoneIndex->find (currBone); - Vector3Array* l = 0; - if (i == mBoneIndex->end()) - { - l = new Vector3Array; - mBoneIndex->insert(BoneKeyIndex(currBone, l)); - } - else - { - l = i->second; - } + unsigned char* pBone; - l->push_back(*curVertices); + if (!mBoneIndex) + mBoneIndex = new BoneIndex(); + BoneIndex::iterator i; - curVertices++; - } - vbuf->unlock(); - } - } - //------------------------------------------------------------------------------------------------ - void VertexIndexToShape::addIndexData(IndexData *data, const unsigned int offset) - { - const unsigned int prev_size = mIndexCount; - mIndexCount += (unsigned int)data->indexCount; + Ogre::Vector3 * curVertices = &mVertexBuffer[prev_size]; - unsigned int* tmp_ind = new unsigned int[mIndexCount]; - if (mIndexBuffer) - { - memcpy (tmp_ind, mIndexBuffer, sizeof(unsigned int) * prev_size); - delete[] mIndexBuffer; - } - mIndexBuffer = tmp_ind; + const unsigned int vertexCount = (unsigned int)vertex_data->vertexCount; + for(unsigned int j = 0; j < vertexCount; ++j) + { + bneElem->baseVertexPointerToElement(vertex, &pBone); + vertex += vSize; - const unsigned int numTris = (unsigned int) data->indexCount / 3; - HardwareIndexBufferSharedPtr ibuf = data->indexBuffer; - const bool use32bitindexes = (ibuf->getType() == HardwareIndexBuffer::IT_32BIT); - unsigned int index_offset = prev_size; + const unsigned char currBone = (indexMap) ? (*indexMap)[*pBone] : *pBone; + i = mBoneIndex->find (currBone); + Vector3Array* l = 0; + if (i == mBoneIndex->end()) + { + l = new Vector3Array; + mBoneIndex->insert(BoneKeyIndex(currBone, l)); + } + else + { + l = i->second; + } - if (use32bitindexes) - { - const unsigned int* pInt = static_cast(ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); - for(unsigned int k = 0; k < numTris; ++k) - { - mIndexBuffer[index_offset ++] = offset + *pInt++; - mIndexBuffer[index_offset ++] = offset + *pInt++; - mIndexBuffer[index_offset ++] = offset + *pInt++; - } - ibuf->unlock(); - } - else - { - const unsigned short* pShort = static_cast(ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); - for(unsigned int k = 0; k < numTris; ++k) - { - mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); - mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); - mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); - } - ibuf->unlock(); - } + l->push_back(*curVertices); - } - //------------------------------------------------------------------------------------------------ - Real VertexIndexToShape::getRadius() - { - if (mBoundRadius == (-1)) - { - getSize(); - mBoundRadius = (std::max(mBounds.x,std::max(mBounds.y,mBounds.z)) * 0.5); - } - return mBoundRadius; - } - //------------------------------------------------------------------------------------------------ - Vector3 VertexIndexToShape::getSize() - { - const unsigned int vCount = getVertexCount(); - if (mBounds == Ogre::Vector3(-1,-1,-1) && vCount > 0) - { + curVertices++; + } + vbuf->unlock(); + } + } - const Ogre::Vector3 * const v = getVertices(); + //------------------------------------------------------------------------------------------------ + void VertexIndexToShape::addIndexData(IndexData *data, const unsigned int offset) + { + const unsigned int prev_size = mIndexCount; + mIndexCount += (unsigned int)data->indexCount; - Ogre::Vector3 vmin(v[0]); - Ogre::Vector3 vmax(v[0]); + unsigned int* tmp_ind = new unsigned int[mIndexCount]; + if (mIndexBuffer) + { + memcpy (tmp_ind, mIndexBuffer, sizeof(unsigned int) * prev_size); + delete[] mIndexBuffer; + } + mIndexBuffer = tmp_ind; - for(unsigned int j = 1; j < vCount; j++) - { - vmin.x = std::min(vmin.x, v[j].x); - vmin.y = std::min(vmin.y, v[j].y); - vmin.z = std::min(vmin.z, v[j].z); + const unsigned int numTris = (unsigned int) data->indexCount / 3; + HardwareIndexBufferSharedPtr ibuf = data->indexBuffer; + const bool use32bitindexes = (ibuf->getType() == HardwareIndexBuffer::IT_32BIT); + unsigned int index_offset = prev_size; - vmax.x = std::max(vmax.x, v[j].x); - vmax.y = std::max(vmax.y, v[j].y); - vmax.z = std::max(vmax.z, v[j].z); - } + if (use32bitindexes) + { + const unsigned int* pInt = static_cast(ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); + for(unsigned int k = 0; k < numTris; ++k) + { + mIndexBuffer[index_offset ++] = offset + *pInt++; + mIndexBuffer[index_offset ++] = offset + *pInt++; + mIndexBuffer[index_offset ++] = offset + *pInt++; + } + ibuf->unlock(); + } + else + { + const unsigned short* pShort = static_cast(ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); + for(unsigned int k = 0; k < numTris; ++k) + { + mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); + mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); + mIndexBuffer[index_offset ++] = offset + static_cast (*pShort++); + } + ibuf->unlock(); + } - mBounds.x = vmax.x - vmin.x; - mBounds.y = vmax.y - vmin.y; - mBounds.z = vmax.z - vmin.z; - } + } - return mBounds; - } - //------------------------------------------------------------------------------------------------ - const Ogre::Vector3* VertexIndexToShape::getVertices() - { - return mVertexBuffer; - } - //------------------------------------------------------------------------------------------------ - unsigned int VertexIndexToShape::getVertexCount() - { - return mVertexCount; - } - //------------------------------------------------------------------------------------------------ - const unsigned int* VertexIndexToShape::getIndices() - { - return mIndexBuffer; - } - //------------------------------------------------------------------------------------------------ - unsigned int VertexIndexToShape::getIndexCount() - { - return mIndexCount; - } + //------------------------------------------------------------------------------------------------ + Real VertexIndexToShape::getRadius() + { + if (mBoundRadius == (-1)) + { + getSize(); + mBoundRadius = (std::max(mBounds.x,std::max(mBounds.y,mBounds.z)) * 0.5); + } + return mBoundRadius; + } - //------------------------------------------------------------------------------------------------ - btSphereShape* VertexIndexToShape::createSphere() - { - const Ogre::Real rad = getRadius(); - assert((rad > 0.0) && - ("Sphere radius must be greater than zero")); - btSphereShape* shape = new btSphereShape(rad); - - shape->setLocalScaling(Convert::toBullet(mScale)); + //------------------------------------------------------------------------------------------------ + Vector3 VertexIndexToShape::getSize() + { + const unsigned int vCount = getVertexCount(); + if (mBounds == Ogre::Vector3(-1,-1,-1) && vCount > 0) + { - return shape; - } - //------------------------------------------------------------------------------------------------ - btBoxShape* VertexIndexToShape::createBox() - { - const Ogre::Vector3 sz = getSize(); + const Ogre::Vector3 * const v = getVertices(); - assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && - ("Size of box must be greater than zero on all axes")); + Ogre::Vector3 vmin(v[0]); + Ogre::Vector3 vmax(v[0]); - btBoxShape* shape = new btBoxShape(Convert::toBullet(sz * 0.5)); + for(unsigned int j = 1; j < vCount; j++) + { + vmin.x = std::min(vmin.x, v[j].x); + vmin.y = std::min(vmin.y, v[j].y); + vmin.z = std::min(vmin.z, v[j].z); - shape->setLocalScaling(Convert::toBullet(mScale)); + vmax.x = std::max(vmax.x, v[j].x); + vmax.y = std::max(vmax.y, v[j].y); + vmax.z = std::max(vmax.z, v[j].z); + } - return shape; - } - //------------------------------------------------------------------------------------------------ - btCylinderShape* VertexIndexToShape::createCylinder() - { - const Ogre::Vector3 sz = getSize(); + mBounds.x = vmax.x - vmin.x; + mBounds.y = vmax.y - vmin.y; + mBounds.z = vmax.z - vmin.z; + } - assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && - ("Size of Cylinder must be greater than zero on all axes")); + return mBounds; + } - btCylinderShape* shape = new btCylinderShapeX(Convert::toBullet(sz * 0.5)); + //------------------------------------------------------------------------------------------------ + const Ogre::Vector3* VertexIndexToShape::getVertices() + { + return mVertexBuffer; + } - shape->setLocalScaling(Convert::toBullet(mScale)); + //------------------------------------------------------------------------------------------------ + unsigned int VertexIndexToShape::getVertexCount() + { + return mVertexCount; + } - return shape; - } - //------------------------------------------------------------------------------------------------ - btConvexHullShape* VertexIndexToShape::createConvex() - { - assert(mVertexCount && (mIndexCount >= 6) && - ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + //------------------------------------------------------------------------------------------------ + const unsigned int* VertexIndexToShape::getIndices() + { + return mIndexBuffer; + } - return new btConvexHullShape((btScalar*) &mVertexBuffer[0].x, mVertexCount, sizeof(Vector3)); - } - //------------------------------------------------------------------------------------------------ - btBvhTriangleMeshShape* VertexIndexToShape::createTrimesh() - { - assert(mVertexCount && (mIndexCount >= 6) && - ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + //------------------------------------------------------------------------------------------------ + unsigned int VertexIndexToShape::getIndexCount() + { + return mIndexCount; + } - unsigned int numFaces = mIndexCount / 3; + //------------------------------------------------------------------------------------------------ + btSphereShape* VertexIndexToShape::createSphere() + { + const Ogre::Real rad = getRadius(); + assert((rad > 0.0) && + ("Sphere radius must be greater than zero")); + btSphereShape* shape = new btSphereShape(rad); - btTriangleMesh *trimesh = new btTriangleMesh(); - unsigned int *indices = mIndexBuffer; - Vector3 *vertices = mVertexBuffer; + shape->setLocalScaling(Convert::toBullet(mScale)); - btVector3 vertexPos[3]; - for (unsigned int n = 0; n < numFaces; ++n) - { - { - const Vector3 &vec = vertices[*indices]; - vertexPos[0][0] = vec.x; - vertexPos[0][1] = vec.y; - vertexPos[0][2] = vec.z; - } - { - const Vector3 &vec = vertices[*(indices + 1)]; - vertexPos[1][0] = vec.x; - vertexPos[1][1] = vec.y; - vertexPos[1][2] = vec.z; - } - { - const Vector3 &vec = vertices[*(indices + 2)]; - vertexPos[2][0] = vec.x; - vertexPos[2][1] = vec.y; - vertexPos[2][2] = vec.z; - } + return shape; + } - indices += 3; + //------------------------------------------------------------------------------------------------ + btBoxShape* VertexIndexToShape::createBox() + { + const Ogre::Vector3 sz = getSize(); - trimesh->addTriangle(vertexPos[0], vertexPos[1], vertexPos[2]); - } + assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && + ("Size of box must be greater than zero on all axes")); - const bool useQuantizedAABB = true; - btBvhTriangleMeshShape *shape = new btBvhTriangleMeshShape(trimesh, useQuantizedAABB); + btBoxShape* shape = new btBoxShape(Convert::toBullet(sz * 0.5)); - shape->setLocalScaling(Convert::toBullet(mScale)); + shape->setLocalScaling(Convert::toBullet(mScale)); - return shape; - } - //------------------------------------------------------------------------------------------------ - VertexIndexToShape::~VertexIndexToShape() - { - delete[] mVertexBuffer; - delete[] mIndexBuffer; + return shape; + } - if (mBoneIndex) - { - for(BoneIndex::iterator i = mBoneIndex->begin(); - i != mBoneIndex->end(); - ++i) - { - delete i->second; - } - delete mBoneIndex; - } - } - //------------------------------------------------------------------------------------------------ - VertexIndexToShape::VertexIndexToShape(const Matrix4 &transform) : - mVertexBuffer (0), - mIndexBuffer (0), - mVertexCount (0), - mIndexCount (0), - mTransform (transform), - mBoundRadius (-1), - mBounds (Vector3(-1,-1,-1)), - mBoneIndex (0), - mScale(1) - { - } + //------------------------------------------------------------------------------------------------ + btCylinderShape* VertexIndexToShape::createCylinder() + { + const Ogre::Vector3 sz = getSize(); + + assert((sz.x > 0.0) && (sz.y > 0.0) && (sz.y > 0.0) && + ("Size of Cylinder must be greater than zero on all axes")); + + btCylinderShape* shape = new btCylinderShapeX(Convert::toBullet(sz * 0.5)); + + shape->setLocalScaling(Convert::toBullet(mScale)); + + return shape; + } + + //------------------------------------------------------------------------------------------------ + btConvexHullShape* VertexIndexToShape::createConvex() + { + assert(mVertexCount && (mIndexCount >= 6) && + ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + + return new btConvexHullShape((btScalar*) &mVertexBuffer[0].x, mVertexCount, sizeof(Vector3)); + } + + //------------------------------------------------------------------------------------------------ + btBvhTriangleMeshShape* VertexIndexToShape::createTrimesh() + { + assert(mVertexCount && (mIndexCount >= 6) && + ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + + unsigned int numFaces = mIndexCount / 3; + + btTriangleMesh *trimesh = new btTriangleMesh(); + unsigned int *indices = mIndexBuffer; + Vector3 *vertices = mVertexBuffer; + + btVector3 vertexPos[3]; + for (unsigned int n = 0; n < numFaces; ++n) + { + { + const Vector3 &vec = vertices[*indices]; + vertexPos[0][0] = vec.x; + vertexPos[0][1] = vec.y; + vertexPos[0][2] = vec.z; + } + { + const Vector3 &vec = vertices[*(indices + 1)]; + vertexPos[1][0] = vec.x; + vertexPos[1][1] = vec.y; + vertexPos[1][2] = vec.z; + } + { + const Vector3 &vec = vertices[*(indices + 2)]; + vertexPos[2][0] = vec.x; + vertexPos[2][1] = vec.y; + vertexPos[2][2] = vec.z; + } + + indices += 3; + + trimesh->addTriangle(vertexPos[0], vertexPos[1], vertexPos[2]); + } + + const bool useQuantizedAABB = true; + btBvhTriangleMeshShape *shape = new btBvhTriangleMeshShape(trimesh, useQuantizedAABB); + + shape->setLocalScaling(Convert::toBullet(mScale)); + + return shape; + } + + //------------------------------------------------------------------------------------------------ + VertexIndexToShape::~VertexIndexToShape() + { + delete[] mVertexBuffer; + delete[] mIndexBuffer; + + if (mBoneIndex) + { + for(BoneIndex::iterator i = mBoneIndex->begin(); + i != mBoneIndex->end(); + ++i) + { + delete i->second; + } + delete mBoneIndex; + } + } + + //------------------------------------------------------------------------------------------------ + VertexIndexToShape::VertexIndexToShape(const Matrix4 &transform) : + mVertexBuffer (0), + mIndexBuffer (0), + mVertexCount (0), + mIndexCount (0), + mTransform (transform), + mBoundRadius (-1), + mBounds (Vector3(-1,-1,-1)), + mBoneIndex (0), + mScale(1) + { + } /* * ============================================================================================= @@ -393,107 +408,112 @@ namespace BtOgre { * ============================================================================================= */ - StaticMeshToShapeConverter::StaticMeshToShapeConverter() : - VertexIndexToShape(), - mEntity (0), - mNode (0) - { - } - //------------------------------------------------------------------------------------------------ - StaticMeshToShapeConverter::~StaticMeshToShapeConverter() - { - } - //------------------------------------------------------------------------------------------------ - StaticMeshToShapeConverter::StaticMeshToShapeConverter(Entity *entity, const Matrix4 &transform) : - VertexIndexToShape(transform), - mEntity (0), - mNode (0) - { - addEntity(entity, transform); - } - //------------------------------------------------------------------------------------------------ - StaticMeshToShapeConverter::StaticMeshToShapeConverter(Renderable *rend, const Matrix4 &transform) : - VertexIndexToShape(transform), - mEntity (0), - mNode (0) - { - RenderOperation op; - rend->getRenderOperation(op); - VertexIndexToShape::addStaticVertexData(op.vertexData); - if(op.useIndexes) - VertexIndexToShape::addIndexData(op.indexData); + StaticMeshToShapeConverter::StaticMeshToShapeConverter() : + VertexIndexToShape(), + mEntity (0), + mNode (0) + { + } - } - //------------------------------------------------------------------------------------------------ - void StaticMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform) - { - // Each entity added need to reset size and radius - // next time getRadius and getSize are asked, they're computed. - mBounds = Ogre::Vector3(-1,-1,-1); - mBoundRadius = -1; + //------------------------------------------------------------------------------------------------ + StaticMeshToShapeConverter::~StaticMeshToShapeConverter() + { + } - mEntity = entity; - mNode = (SceneNode*)(mEntity->getParentNode()); - mTransform = transform; - mScale = mNode->getScale(); + //------------------------------------------------------------------------------------------------ + StaticMeshToShapeConverter::StaticMeshToShapeConverter(Entity *entity, const Matrix4 &transform) : + VertexIndexToShape(transform), + mEntity (0), + mNode (0) + { + addEntity(entity, transform); + } - if (mEntity->getMesh()->sharedVertexData) - { - VertexIndexToShape::addStaticVertexData (mEntity->getMesh()->sharedVertexData); - } + //------------------------------------------------------------------------------------------------ + StaticMeshToShapeConverter::StaticMeshToShapeConverter(Renderable *rend, const Matrix4 &transform) : + VertexIndexToShape(transform), + mEntity (0), + mNode (0) + { + RenderOperation op; + rend->getRenderOperation(op); + VertexIndexToShape::addStaticVertexData(op.vertexData); + if(op.useIndexes) + VertexIndexToShape::addIndexData(op.indexData); - for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i) - { - SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh(); + } - if (!sub_mesh->useSharedVertices) - { - VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); - VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData); - } - else - { - VertexIndexToShape::addIndexData (sub_mesh->indexData); - } + //------------------------------------------------------------------------------------------------ + void StaticMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; - } - } - //------------------------------------------------------------------------------------------------ - void StaticMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform) - { - // Each entity added need to reset size and radius - // next time getRadius and getSize are asked, they're computed. - mBounds = Ogre::Vector3(-1,-1,-1); - mBoundRadius = -1; + mEntity = entity; + mNode = (SceneNode*)(mEntity->getParentNode()); + mTransform = transform; + mScale = mNode->getScale(); - //_entity = entity; - //_node = (SceneNode*)(_entity->getParentNode()); - mTransform = transform; + if (mEntity->getMesh()->sharedVertexData) + { + VertexIndexToShape::addStaticVertexData (mEntity->getMesh()->sharedVertexData); + } - if (mesh->hasSkeleton ()) - Ogre::LogManager::getSingleton().logMessage("MeshToShapeConverter::addMesh : Mesh " + mesh->getName () + " as skeleton but added to trimesh non animated"); + for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i) + { + SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh(); - if (mesh->sharedVertexData) - { - VertexIndexToShape::addStaticVertexData (mesh->sharedVertexData); - } + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } - for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i) - { - SubMesh *sub_mesh = mesh->getSubMesh(i); + } + } - if (!sub_mesh->useSharedVertices) - { - VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); - VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData); - } - else - { - VertexIndexToShape::addIndexData (sub_mesh->indexData); - } + //------------------------------------------------------------------------------------------------ + void StaticMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; - } - } + //_entity = entity; + //_node = (SceneNode*)(_entity->getParentNode()); + mTransform = transform; + + if (mesh->hasSkeleton ()) + Ogre::LogManager::getSingleton().logMessage("MeshToShapeConverter::addMesh : Mesh " + mesh->getName () + " as skeleton but added to trimesh non animated"); + + if (mesh->sharedVertexData) + { + VertexIndexToShape::addStaticVertexData (mesh->sharedVertexData); + } + + for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i) + { + SubMesh *sub_mesh = mesh->getSubMesh(i); + + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + VertexIndexToShape::addStaticVertexData (sub_mesh->vertexData); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } + + } + } /* * ============================================================================================= @@ -501,284 +521,292 @@ namespace BtOgre { * ============================================================================================= */ - AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter(Entity *entity,const Matrix4 &transform) : - VertexIndexToShape(transform), - mEntity (0), - mNode (0), - mTransformedVerticesTemp(0), - mTransformedVerticesTempSize(0) - { - addEntity(entity, transform); - } - //------------------------------------------------------------------------------------------------ - AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter() : - VertexIndexToShape(), - mEntity (0), - mNode (0), - mTransformedVerticesTemp(0), - mTransformedVerticesTempSize(0) - { - } - //------------------------------------------------------------------------------------------------ - AnimatedMeshToShapeConverter::~AnimatedMeshToShapeConverter() - { - delete[] mTransformedVerticesTemp; - } - //------------------------------------------------------------------------------------------------ - void AnimatedMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform) - { - // Each entity added need to reset size and radius - // next time getRadius and getSize are asked, they're computed. - mBounds = Ogre::Vector3(-1,-1,-1); - mBoundRadius = -1; + AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter(Entity *entity,const Matrix4 &transform) : + VertexIndexToShape(transform), + mEntity (0), + mNode (0), + mTransformedVerticesTemp(0), + mTransformedVerticesTempSize(0) + { + addEntity(entity, transform); + } - mEntity = entity; - mNode = (SceneNode*)(mEntity->getParentNode()); - mTransform = transform; + //------------------------------------------------------------------------------------------------ + AnimatedMeshToShapeConverter::AnimatedMeshToShapeConverter() : + VertexIndexToShape(), + mEntity (0), + mNode (0), + mTransformedVerticesTemp(0), + mTransformedVerticesTempSize(0) + { + } - assert (entity->getMesh()->hasSkeleton ()); + //------------------------------------------------------------------------------------------------ + AnimatedMeshToShapeConverter::~AnimatedMeshToShapeConverter() + { + delete[] mTransformedVerticesTemp; + } - mEntity->addSoftwareAnimationRequest(false); - mEntity->_updateAnimation(); + //------------------------------------------------------------------------------------------------ + void AnimatedMeshToShapeConverter::addEntity(Entity *entity,const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; - if (mEntity->getMesh()->sharedVertexData) - { - VertexIndexToShape::addAnimatedVertexData (mEntity->getMesh()->sharedVertexData, - mEntity->_getSkelAnimVertexData(), - &mEntity->getMesh()->sharedBlendIndexToBoneIndexMap); - } + mEntity = entity; + mNode = (SceneNode*)(mEntity->getParentNode()); + mTransform = transform; - for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i) - { - SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh(); + assert (entity->getMesh()->hasSkeleton ()); - if (!sub_mesh->useSharedVertices) - { - VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + mEntity->addSoftwareAnimationRequest(false); + mEntity->_updateAnimation(); - VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData, - mEntity->getSubEntity(i)->_getSkelAnimVertexData(), - &sub_mesh->blendIndexToBoneIndexMap); - } - else - { - VertexIndexToShape::addIndexData (sub_mesh->indexData); - } + if (mEntity->getMesh()->sharedVertexData) + { + VertexIndexToShape::addAnimatedVertexData (mEntity->getMesh()->sharedVertexData, + mEntity->_getSkelAnimVertexData(), + &mEntity->getMesh()->sharedBlendIndexToBoneIndexMap); + } - } + for (unsigned int i = 0;i < mEntity->getNumSubEntities();++i) + { + SubMesh *sub_mesh = mEntity->getSubEntity(i)->getSubMesh(); - mEntity->removeSoftwareAnimationRequest(false); - } - //------------------------------------------------------------------------------------------------ - void AnimatedMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform) - { - // Each entity added need to reset size and radius - // next time getRadius and getSize are asked, they're computed. - mBounds = Ogre::Vector3(-1,-1,-1); - mBoundRadius = -1; + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); - //_entity = entity; - //_node = (SceneNode*)(_entity->getParentNode()); - mTransform = transform; + VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData, + mEntity->getSubEntity(i)->_getSkelAnimVertexData(), + &sub_mesh->blendIndexToBoneIndexMap); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } - assert (mesh->hasSkeleton ()); + } - if (mesh->sharedVertexData) - { - VertexIndexToShape::addAnimatedVertexData (mesh->sharedVertexData, - 0, - &mesh->sharedBlendIndexToBoneIndexMap); - } + mEntity->removeSoftwareAnimationRequest(false); + } - for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i) - { - SubMesh *sub_mesh = mesh->getSubMesh(i); + //------------------------------------------------------------------------------------------------ + void AnimatedMeshToShapeConverter::addMesh(const MeshPtr &mesh, const Matrix4 &transform) + { + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Ogre::Vector3(-1,-1,-1); + mBoundRadius = -1; - if (!sub_mesh->useSharedVertices) - { - VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); + //_entity = entity; + //_node = (SceneNode*)(_entity->getParentNode()); + mTransform = transform; - VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData, - 0, - &sub_mesh->blendIndexToBoneIndexMap); - } - else - { - VertexIndexToShape::addIndexData (sub_mesh->indexData); - } + assert (mesh->hasSkeleton ()); - } - } - //------------------------------------------------------------------------------------------------ - bool AnimatedMeshToShapeConverter::getBoneVertices(unsigned char bone, - unsigned int &vertex_count, - Ogre::Vector3* &vertices, - const Vector3 &bonePosition) - { - BoneIndex::iterator i = mBoneIndex->find(bone); + if (mesh->sharedVertexData) + { + VertexIndexToShape::addAnimatedVertexData (mesh->sharedVertexData, + 0, + &mesh->sharedBlendIndexToBoneIndexMap); + } - if (i == mBoneIndex->end()) - return false; + for(unsigned int i = 0;i < mesh->getNumSubMeshes();++i) + { + SubMesh *sub_mesh = mesh->getSubMesh(i); - if (i->second->empty()) - return false; + if (!sub_mesh->useSharedVertices) + { + VertexIndexToShape::addIndexData(sub_mesh->indexData, mVertexCount); - vertex_count = (unsigned int) i->second->size() + 1; - if (vertex_count > mTransformedVerticesTempSize) - { - if (mTransformedVerticesTemp) - delete[] mTransformedVerticesTemp; + VertexIndexToShape::addAnimatedVertexData (sub_mesh->vertexData, + 0, + &sub_mesh->blendIndexToBoneIndexMap); + } + else + { + VertexIndexToShape::addIndexData (sub_mesh->indexData); + } - mTransformedVerticesTemp = new Ogre::Vector3[vertex_count]; + } + } - } + //------------------------------------------------------------------------------------------------ + bool AnimatedMeshToShapeConverter::getBoneVertices(unsigned char bone, + unsigned int &vertex_count, + Ogre::Vector3* &vertices, + const Vector3 &bonePosition) + { + BoneIndex::iterator i = mBoneIndex->find(bone); - vertices = mTransformedVerticesTemp; - vertices[0] = bonePosition; - //mEntity->_getParentNodeFullTransform() * - // mEntity->getSkeleton()->getBone(bone)->_getDerivedPosition(); + if (i == mBoneIndex->end()) + return false; - //mEntity->getSkeleton()->getBone(bone)->_getDerivedOrientation() - unsigned int currBoneVertex = 1; - Vector3Array::iterator j = i->second->begin(); - while(j != i->second->end()) - { - vertices[currBoneVertex] = (*j); - ++j; - ++currBoneVertex; - } - return true; - } - //------------------------------------------------------------------------------------------------ - btBoxShape* AnimatedMeshToShapeConverter::createAlignedBox(unsigned char bone, - const Vector3 &bonePosition, - const Quaternion &boneOrientation) - { - unsigned int vertex_count; - Vector3* vertices; + if (i->second->empty()) + return false; - if (!getBoneVertices(bone, vertex_count, vertices, bonePosition)) - return 0; + vertex_count = (unsigned int) i->second->size() + 1; + if (vertex_count > mTransformedVerticesTempSize) + { + if (mTransformedVerticesTemp) + delete[] mTransformedVerticesTemp; - Vector3 min_vec(vertices[0]); - Vector3 max_vec(vertices[0]); + mTransformedVerticesTemp = new Ogre::Vector3[vertex_count]; - for(unsigned int j = 1; j < vertex_count ;j++) - { - min_vec.x = std::min(min_vec.x,vertices[j].x); - min_vec.y = std::min(min_vec.y,vertices[j].y); - min_vec.z = std::min(min_vec.z,vertices[j].z); + } - max_vec.x = std::max(max_vec.x,vertices[j].x); - max_vec.y = std::max(max_vec.y,vertices[j].y); - max_vec.z = std::max(max_vec.z,vertices[j].z); - } - const Ogre::Vector3 maxMinusMin(max_vec - min_vec); - btBoxShape* box = new btBoxShape(Convert::toBullet(maxMinusMin)); + vertices = mTransformedVerticesTemp; + vertices[0] = bonePosition; + //mEntity->_getParentNodeFullTransform() * + //mEntity->getSkeleton()->getBone(bone)->_getDerivedPosition(); - /*const Ogre::Vector3 pos - (min_vec.x + (maxMinusMin.x * 0.5), - min_vec.y + (maxMinusMin.y * 0.5), - min_vec.z + (maxMinusMin.z * 0.5));*/ + //mEntity->getSkeleton()->getBone(bone)->_getDerivedOrientation() + unsigned int currBoneVertex = 1; + Vector3Array::iterator j = i->second->begin(); + while(j != i->second->end()) + { + vertices[currBoneVertex] = (*j); + ++j; + ++currBoneVertex; + } + return true; + } - //box->setPosition(pos); + //------------------------------------------------------------------------------------------------ + btBoxShape* AnimatedMeshToShapeConverter::createAlignedBox(unsigned char bone, + const Vector3 &bonePosition, + const Quaternion &boneOrientation) + { + unsigned int vertex_count; + Vector3* vertices; - return box; - } - //------------------------------------------------------------------------------------------------ - bool AnimatedMeshToShapeConverter::getOrientedBox(unsigned char bone, - const Vector3 &bonePosition, - const Quaternion &boneOrientation, - Vector3 &box_afExtent, - Vector3 *box_akAxis, - Vector3 &box_kCenter) - { - unsigned int vertex_count; - Vector3* vertices; + if (!getBoneVertices(bone, vertex_count, vertices, bonePosition)) + return 0; - if (!getBoneVertices(bone, vertex_count, vertices, bonePosition)) - return false; + Vector3 min_vec(vertices[0]); + Vector3 max_vec(vertices[0]); - box_kCenter = Vector3::ZERO; + for(unsigned int j = 1; j < vertex_count ;j++) + { + min_vec.x = std::min(min_vec.x,vertices[j].x); + min_vec.y = std::min(min_vec.y,vertices[j].y); + min_vec.z = std::min(min_vec.z,vertices[j].z); - { - for(unsigned int c = 0 ;c < vertex_count;c++) - { - box_kCenter += vertices[c]; - } - const Ogre::Real invVertexCount = 1.0 / vertex_count; - box_kCenter *= invVertexCount; - } - Quaternion orient = boneOrientation; - orient.ToAxes(box_akAxis); + max_vec.x = std::max(max_vec.x,vertices[j].x); + max_vec.y = std::max(max_vec.y,vertices[j].y); + max_vec.z = std::max(max_vec.z,vertices[j].z); + } + const Ogre::Vector3 maxMinusMin(max_vec - min_vec); + btBoxShape* box = new btBoxShape(Convert::toBullet(maxMinusMin)); - // Let C be the box center and let U0, U1, and U2 be the box axes. Each - // input point is of the form X = C + y0*U0 + y1*U1 + y2*U2. The - // following code computes min(y0), max(y0), min(y1), max(y1), min(y2), - // and max(y2). The box center is then adjusted to be - // C' = C + 0.5*(min(y0)+max(y0))*U0 + 0.5*(min(y1)+max(y1))*U1 + - // 0.5*(min(y2)+max(y2))*U2 + /*const Ogre::Vector3 pos + (min_vec.x + (maxMinusMin.x * 0.5), + min_vec.y + (maxMinusMin.y * 0.5), + min_vec.z + (maxMinusMin.z * 0.5));*/ - Ogre::Vector3 kDiff (vertices[1] - box_kCenter); - Ogre::Real fY0Min = kDiff.dotProduct(box_akAxis[0]), fY0Max = fY0Min; - Ogre::Real fY1Min = kDiff.dotProduct(box_akAxis[1]), fY1Max = fY1Min; - Ogre::Real fY2Min = kDiff.dotProduct(box_akAxis[2]), fY2Max = fY2Min; + //box->setPosition(pos); - for (unsigned int i = 2; i < vertex_count; i++) - { - kDiff = vertices[i] - box_kCenter; + return box; + } - const Ogre::Real fY0 = kDiff.dotProduct(box_akAxis[0]); - if ( fY0 < fY0Min ) - fY0Min = fY0; - else if ( fY0 > fY0Max ) - fY0Max = fY0; + //------------------------------------------------------------------------------------------------ + bool AnimatedMeshToShapeConverter::getOrientedBox(unsigned char bone, + const Vector3 &bonePosition, + const Quaternion &boneOrientation, + Vector3 &box_afExtent, + Vector3 *box_akAxis, + Vector3 &box_kCenter) + { + unsigned int vertex_count; + Vector3* vertices; - const Ogre::Real fY1 = kDiff.dotProduct(box_akAxis[1]); - if ( fY1 < fY1Min ) - fY1Min = fY1; - else if ( fY1 > fY1Max ) - fY1Max = fY1; + if (!getBoneVertices(bone, vertex_count, vertices, bonePosition)) + return false; - const Ogre::Real fY2 = kDiff.dotProduct(box_akAxis[2]); - if ( fY2 < fY2Min ) - fY2Min = fY2; - else if ( fY2 > fY2Max ) - fY2Max = fY2; - } + box_kCenter = Vector3::ZERO; - box_afExtent.x = ((Real)0.5)*(fY0Max - fY0Min); - box_afExtent.y = ((Real)0.5)*(fY1Max - fY1Min); - box_afExtent.z = ((Real)0.5)*(fY2Max - fY2Min); + { + for(unsigned int c = 0 ;c < vertex_count;c++) + { + box_kCenter += vertices[c]; + } + const Ogre::Real invVertexCount = 1.0 / vertex_count; + box_kCenter *= invVertexCount; + } + Quaternion orient = boneOrientation; + orient.ToAxes(box_akAxis); - box_kCenter += (0.5*(fY0Max+fY0Min))*box_akAxis[0] + - (0.5*(fY1Max+fY1Min))*box_akAxis[1] + - (0.5*(fY2Max+fY2Min))*box_akAxis[2]; + // Let C be the box center and let U0, U1, and U2 be the box axes. Each + // input point is of the form X = C + y0*U0 + y1*U1 + y2*U2. The + // following code computes min(y0), max(y0), min(y1), max(y1), min(y2), + // and max(y2). The box center is then adjusted to be + // C' = C + 0.5*(min(y0)+max(y0))*U0 + 0.5*(min(y1)+max(y1))*U1 + + // 0.5*(min(y2)+max(y2))*U2 - box_afExtent *= 2.0; + Ogre::Vector3 kDiff (vertices[1] - box_kCenter); + Ogre::Real fY0Min = kDiff.dotProduct(box_akAxis[0]), fY0Max = fY0Min; + Ogre::Real fY1Min = kDiff.dotProduct(box_akAxis[1]), fY1Max = fY1Min; + Ogre::Real fY2Min = kDiff.dotProduct(box_akAxis[2]), fY2Max = fY2Min; - return true; - } - //------------------------------------------------------------------------------------------------ - btBoxShape *AnimatedMeshToShapeConverter::createOrientedBox(unsigned char bone, - const Vector3 &bonePosition, - const Quaternion &boneOrientation) - { - Ogre::Vector3 box_akAxis[3]; - Ogre::Vector3 box_afExtent; - Ogre::Vector3 box_afCenter; + for (unsigned int i = 2; i < vertex_count; i++) + { + kDiff = vertices[i] - box_kCenter; - if (!getOrientedBox(bone, bonePosition, boneOrientation, - box_afExtent, - box_akAxis, - box_afCenter)) - return 0; + const Ogre::Real fY0 = kDiff.dotProduct(box_akAxis[0]); + if ( fY0 < fY0Min ) + fY0Min = fY0; + else if ( fY0 > fY0Max ) + fY0Max = fY0; - btBoxShape *geom = new btBoxShape(Convert::toBullet(box_afExtent)); - //geom->setOrientation(Quaternion(box_akAxis[0],box_akAxis[1],box_akAxis[2])); - //geom->setPosition(box_afCenter); - return geom; - } + const Ogre::Real fY1 = kDiff.dotProduct(box_akAxis[1]); + if ( fY1 < fY1Min ) + fY1Min = fY1; + else if ( fY1 > fY1Max ) + fY1Max = fY1; + + const Ogre::Real fY2 = kDiff.dotProduct(box_akAxis[2]); + if ( fY2 < fY2Min ) + fY2Min = fY2; + else if ( fY2 > fY2Max ) + fY2Max = fY2; + } + + box_afExtent.x = ((Real)0.5)*(fY0Max - fY0Min); + box_afExtent.y = ((Real)0.5)*(fY1Max - fY1Min); + box_afExtent.z = ((Real)0.5)*(fY2Max - fY2Min); + + box_kCenter += (0.5*(fY0Max+fY0Min))*box_akAxis[0] + + (0.5*(fY1Max+fY1Min))*box_akAxis[1] + + (0.5*(fY2Max+fY2Min))*box_akAxis[2]; + + box_afExtent *= 2.0; + + return true; + } + + //------------------------------------------------------------------------------------------------ + btBoxShape *AnimatedMeshToShapeConverter::createOrientedBox(unsigned char bone, + const Vector3 &bonePosition, + const Quaternion &boneOrientation) + { + Ogre::Vector3 box_akAxis[3]; + Ogre::Vector3 box_afExtent; + Ogre::Vector3 box_afCenter; + + if (!getOrientedBox(bone, bonePosition, boneOrientation, + box_afExtent, + box_akAxis, + box_afCenter)) + return 0; + + btBoxShape *geom = new btBoxShape(Convert::toBullet(box_afExtent)); + //geom->setOrientation(Quaternion(box_akAxis[0],box_akAxis[1],box_akAxis[2])); + //geom->setPosition(box_afCenter); + return geom; + } /* * ============================================================================================= @@ -786,133 +814,138 @@ namespace BtOgre { * ============================================================================================= */ - DynamicRenderable::DynamicRenderable() - { - } - //------------------------------------------------------------------------------------------------ - DynamicRenderable::~DynamicRenderable() - { - delete mRenderOp.vertexData; - delete mRenderOp.indexData; - } - //------------------------------------------------------------------------------------------------ - void DynamicRenderable::initialize(RenderOperation::OperationType operationType, - bool useIndices) - { - // Initialize render operation - mRenderOp.operationType = operationType; - mRenderOp.useIndexes = useIndices; - mRenderOp.vertexData = new VertexData; - if (mRenderOp.useIndexes) - mRenderOp.indexData = new IndexData; + DynamicRenderable::DynamicRenderable() + { + } - // Reset buffer capacities - mVertexBufferCapacity = 0; - mIndexBufferCapacity = 0; + //------------------------------------------------------------------------------------------------ + DynamicRenderable::~DynamicRenderable() + { + delete mRenderOp.vertexData; + delete mRenderOp.indexData; + } - // Create vertex declaration - createVertexDeclaration(); - } - //------------------------------------------------------------------------------------------------ - void DynamicRenderable::prepareHardwareBuffers(size_t vertexCount, - size_t indexCount) - { - // Prepare vertex buffer - size_t newVertCapacity = mVertexBufferCapacity; - if ((vertexCount > mVertexBufferCapacity) || - (!mVertexBufferCapacity)) - { - // vertexCount exceeds current capacity! - // It is necessary to reallocate the buffer. + //------------------------------------------------------------------------------------------------ + void DynamicRenderable::initialize(RenderOperation::OperationType operationType, + bool useIndices) + { + // Initialize render operation + mRenderOp.operationType = operationType; + mRenderOp.useIndexes = useIndices; + mRenderOp.vertexData = new VertexData; + if (mRenderOp.useIndexes) + mRenderOp.indexData = new IndexData; - // Check if this is the first call - if (!newVertCapacity) - newVertCapacity = 1; + // Reset buffer capacities + mVertexBufferCapacity = 0; + mIndexBufferCapacity = 0; - // Make capacity the next power of two - while (newVertCapacity < vertexCount) - newVertCapacity <<= 1; - } - else if (vertexCount < mVertexBufferCapacity>>1) { - // Make capacity the previous power of two - while (vertexCount < newVertCapacity>>1) - newVertCapacity >>= 1; - } - if (newVertCapacity != mVertexBufferCapacity) - { - mVertexBufferCapacity = newVertCapacity; - // Create new vertex buffer - HardwareVertexBufferSharedPtr vbuf = - HardwareBufferManager::getSingleton().createVertexBuffer( - mRenderOp.vertexData->vertexDeclaration->getVertexSize(0), - mVertexBufferCapacity, - HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_? + // Create vertex declaration + createVertexDeclaration(); + } - // Bind buffer - mRenderOp.vertexData->vertexBufferBinding->setBinding(0, vbuf); - } - // Update vertex count in the render operation - mRenderOp.vertexData->vertexCount = vertexCount; + //------------------------------------------------------------------------------------------------ + void DynamicRenderable::prepareHardwareBuffers(size_t vertexCount, + size_t indexCount) + { + // Prepare vertex buffer + size_t newVertCapacity = mVertexBufferCapacity; + if ((vertexCount > mVertexBufferCapacity) || + (!mVertexBufferCapacity)) + { + // vertexCount exceeds current capacity! + // It is necessary to reallocate the buffer. - if (mRenderOp.useIndexes) - { - OgreAssert(indexCount <= std::numeric_limits::max(), "indexCount exceeds 16 bit"); + // Check if this is the first call + if (!newVertCapacity) + newVertCapacity = 1; - size_t newIndexCapacity = mIndexBufferCapacity; - // Prepare index buffer - if ((indexCount > newIndexCapacity) || - (!newIndexCapacity)) - { - // indexCount exceeds current capacity! - // It is necessary to reallocate the buffer. + // Make capacity the next power of two + while (newVertCapacity < vertexCount) + newVertCapacity <<= 1; + } + else if (vertexCount < mVertexBufferCapacity>>1) { + // Make capacity the previous power of two + while (vertexCount < newVertCapacity>>1) + newVertCapacity >>= 1; + } + if (newVertCapacity != mVertexBufferCapacity) + { + mVertexBufferCapacity = newVertCapacity; + // Create new vertex buffer + HardwareVertexBufferSharedPtr vbuf = + HardwareBufferManager::getSingleton().createVertexBuffer( + mRenderOp.vertexData->vertexDeclaration->getVertexSize(0), + mVertexBufferCapacity, + HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_? - // Check if this is the first call - if (!newIndexCapacity) - newIndexCapacity = 1; + // Bind buffer + mRenderOp.vertexData->vertexBufferBinding->setBinding(0, vbuf); + } + // Update vertex count in the render operation + mRenderOp.vertexData->vertexCount = vertexCount; - // Make capacity the next power of two - while (newIndexCapacity < indexCount) - newIndexCapacity <<= 1; + if (mRenderOp.useIndexes) + { + OgreAssert(indexCount <= std::numeric_limits::max(), "indexCount exceeds 16 bit"); - } - else if (indexCount < newIndexCapacity>>1) - { - // Make capacity the previous power of two - while (indexCount < newIndexCapacity>>1) - newIndexCapacity >>= 1; - } + size_t newIndexCapacity = mIndexBufferCapacity; + // Prepare index buffer + if ((indexCount > newIndexCapacity) || + (!newIndexCapacity)) + { + // indexCount exceeds current capacity! + // It is necessary to reallocate the buffer. - if (newIndexCapacity != mIndexBufferCapacity) - { - mIndexBufferCapacity = newIndexCapacity; - // Create new index buffer - mRenderOp.indexData->indexBuffer = - HardwareBufferManager::getSingleton().createIndexBuffer( - HardwareIndexBuffer::IT_16BIT, - mIndexBufferCapacity, - HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_? - } + // Check if this is the first call + if (!newIndexCapacity) + newIndexCapacity = 1; - // Update index count in the render operation - mRenderOp.indexData->indexCount = indexCount; - } - } - //------------------------------------------------------------------------------------------------ - Real DynamicRenderable::getBoundingRadius(void) const - { - return Math::Sqrt(std::max(mBox.getMaximum().squaredLength(), mBox.getMinimum().squaredLength())); - } - //------------------------------------------------------------------------------------------------ - Real DynamicRenderable::getSquaredViewDepth(const Camera* cam) const - { - Vector3 vMin, vMax, vMid, vDist; - vMin = mBox.getMinimum(); - vMax = mBox.getMaximum(); - vMid = ((vMax - vMin) * 0.5) + vMin; - vDist = cam->getDerivedPosition() - vMid; + // Make capacity the next power of two + while (newIndexCapacity < indexCount) + newIndexCapacity <<= 1; - return vDist.squaredLength(); - } + } + else if (indexCount < newIndexCapacity>>1) + { + // Make capacity the previous power of two + while (indexCount < newIndexCapacity>>1) + newIndexCapacity >>= 1; + } + + if (newIndexCapacity != mIndexBufferCapacity) + { + mIndexBufferCapacity = newIndexCapacity; + // Create new index buffer + mRenderOp.indexData->indexBuffer = + HardwareBufferManager::getSingleton().createIndexBuffer( + HardwareIndexBuffer::IT_16BIT, + mIndexBufferCapacity, + HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_? + } + + // Update index count in the render operation + mRenderOp.indexData->indexCount = indexCount; + } + } + + //------------------------------------------------------------------------------------------------ + Real DynamicRenderable::getBoundingRadius(void) const + { + return Math::Sqrt(std::max(mBox.getMaximum().squaredLength(), mBox.getMinimum().squaredLength())); + } + + //------------------------------------------------------------------------------------------------ + Real DynamicRenderable::getSquaredViewDepth(const Camera* cam) const + { + Vector3 vMin, vMax, vMid, vDist; + vMin = mBox.getMinimum(); + vMax = mBox.getMaximum(); + vMid = ((vMax - vMin) * 0.5) + vMin; + vDist = cam->getDerivedPosition() - vMid; + + return vDist.squaredLength(); + } /* * ============================================================================================= @@ -920,125 +953,138 @@ namespace BtOgre { * ============================================================================================= */ - enum { - POSITION_BINDING, - TEXCOORD_BINDING - }; - //------------------------------------------------------------------------------------------------ - DynamicLines::DynamicLines(OperationType opType) - { - initialize(opType,false); - setMaterial("BaseWhiteNoLighting"); - mDirty = true; - } - //------------------------------------------------------------------------------------------------ - DynamicLines::~DynamicLines() - { - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::setOperationType(OperationType opType) - { - mRenderOp.operationType = opType; - } - //------------------------------------------------------------------------------------------------ - RenderOperation::OperationType DynamicLines::getOperationType() const - { - return mRenderOp.operationType; - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::addPoint(const Vector3 &p) - { - mPoints.push_back(p); - mDirty = true; - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::addPoint(Real x, Real y, Real z) - { - mPoints.push_back(Vector3(x,y,z)); - mDirty = true; - } - //------------------------------------------------------------------------------------------------ - const Vector3& DynamicLines::getPoint(unsigned short index) const - { - assert(index < mPoints.size() && "Point index is out of bounds!!"); - return mPoints[index]; - } - //------------------------------------------------------------------------------------------------ - unsigned short DynamicLines::getNumPoints(void) const - { - return (unsigned short)mPoints.size(); - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::setPoint(unsigned short index, const Vector3 &value) - { - assert(index < mPoints.size() && "Point index is out of bounds!!"); + enum { + POSITION_BINDING, + TEXCOORD_BINDING + }; - mPoints[index] = value; - mDirty = true; - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::clear() - { - mPoints.clear(); - mDirty = true; - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::update() - { - if (mDirty) fillHardwareBuffers(); - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::createVertexDeclaration() - { - VertexDeclaration *decl = mRenderOp.vertexData->vertexDeclaration; - decl->addElement(POSITION_BINDING, 0, VET_FLOAT3, VES_POSITION); - } - //------------------------------------------------------------------------------------------------ - void DynamicLines::fillHardwareBuffers() - { - int size = mPoints.size(); + //------------------------------------------------------------------------------------------------ + DynamicLines::DynamicLines(OperationType opType) + { + initialize(opType,false); + setMaterial("BaseWhiteNoLighting"); + mDirty = true; + } - prepareHardwareBuffers(size,0); + //------------------------------------------------------------------------------------------------ + DynamicLines::~DynamicLines() + { + } - if (!size) { - mBox.setExtents(Vector3::ZERO,Vector3::ZERO); - mDirty=false; - return; - } - - Vector3 vaabMin = mPoints[0]; - Vector3 vaabMax = mPoints[0]; + //------------------------------------------------------------------------------------------------ + void DynamicLines::setOperationType(OperationType opType) + { + mRenderOp.operationType = opType; + } - HardwareVertexBufferSharedPtr vbuf = - mRenderOp.vertexData->vertexBufferBinding->getBuffer(0); + //------------------------------------------------------------------------------------------------ + RenderOperation::OperationType DynamicLines::getOperationType() const + { + return mRenderOp.operationType; + } - Real *prPos = static_cast(vbuf->lock(HardwareBuffer::HBL_DISCARD)); - { - for(int i = 0; i < size; i++) - { - *prPos++ = mPoints[i].x; - *prPos++ = mPoints[i].y; - *prPos++ = mPoints[i].z; + //------------------------------------------------------------------------------------------------ + void DynamicLines::addPoint(const Vector3 &p) + { + mPoints.push_back(p); + mDirty = true; + } - if(mPoints[i].x < vaabMin.x) - vaabMin.x = mPoints[i].x; - if(mPoints[i].y < vaabMin.y) - vaabMin.y = mPoints[i].y; - if(mPoints[i].z < vaabMin.z) - vaabMin.z = mPoints[i].z; + //------------------------------------------------------------------------------------------------ + void DynamicLines::addPoint(Real x, Real y, Real z) + { + mPoints.push_back(Vector3(x,y,z)); + mDirty = true; + } - if(mPoints[i].x > vaabMax.x) - vaabMax.x = mPoints[i].x; - if(mPoints[i].y > vaabMax.y) - vaabMax.y = mPoints[i].y; - if(mPoints[i].z > vaabMax.z) - vaabMax.z = mPoints[i].z; - } - } - vbuf->unlock(); + //------------------------------------------------------------------------------------------------ + const Vector3& DynamicLines::getPoint(unsigned short index) const + { + assert(index < mPoints.size() && "Point index is out of bounds!!"); + return mPoints[index]; + } - mBox.setExtents(vaabMin, vaabMax); + //------------------------------------------------------------------------------------------------ + unsigned short DynamicLines::getNumPoints(void) const + { + return (unsigned short)mPoints.size(); + } - mDirty = false; - } -} + //------------------------------------------------------------------------------------------------ + void DynamicLines::setPoint(unsigned short index, const Vector3 &value) + { + assert(index < mPoints.size() && "Point index is out of bounds!!"); + + mPoints[index] = value; + mDirty = true; + } + + //------------------------------------------------------------------------------------------------ + void DynamicLines::clear() + { + mPoints.clear(); + mDirty = true; + } + + //------------------------------------------------------------------------------------------------ + void DynamicLines::update() + { + if (mDirty) fillHardwareBuffers(); + } + + //------------------------------------------------------------------------------------------------ + void DynamicLines::createVertexDeclaration() + { + VertexDeclaration *decl = mRenderOp.vertexData->vertexDeclaration; + decl->addElement(POSITION_BINDING, 0, VET_FLOAT3, VES_POSITION); + } + + //------------------------------------------------------------------------------------------------ + void DynamicLines::fillHardwareBuffers() + { + int size = mPoints.size(); + + prepareHardwareBuffers(size,0); + + if (!size) { + mBox.setExtents(Vector3::ZERO,Vector3::ZERO); + mDirty=false; + return; + } + + Vector3 vaabMin = mPoints[0]; + Vector3 vaabMax = mPoints[0]; + + HardwareVertexBufferSharedPtr vbuf = + mRenderOp.vertexData->vertexBufferBinding->getBuffer(0); + + Real *prPos = static_cast(vbuf->lock(HardwareBuffer::HBL_DISCARD)); + { + for(int i = 0; i < size; i++) + { + *prPos++ = mPoints[i].x; + *prPos++ = mPoints[i].y; + *prPos++ = mPoints[i].z; + + if(mPoints[i].x < vaabMin.x) + vaabMin.x = mPoints[i].x; + if(mPoints[i].y < vaabMin.y) + vaabMin.y = mPoints[i].y; + if(mPoints[i].z < vaabMin.z) + vaabMin.z = mPoints[i].z; + + if(mPoints[i].x > vaabMax.x) + vaabMax.x = mPoints[i].x; + if(mPoints[i].y > vaabMax.y) + vaabMax.y = mPoints[i].y; + if(mPoints[i].z > vaabMax.z) + vaabMax.z = mPoints[i].z; + } + } + vbuf->unlock(); + + mBox.setExtents(vaabMin, vaabMax); + + mDirty = false; + } +} diff --git a/bullet/BtOgreExtras.h b/bullet/BtOgreExtras.h index f959433774..f3e1aa87ab 100644 --- a/bullet/BtOgreExtras.h +++ b/bullet/BtOgreExtras.h @@ -35,26 +35,26 @@ typedef std::vector Vector3Array; class Convert { public: - Convert() {}; - ~Convert() {}; + Convert() {}; + ~Convert() {}; - static btQuaternion toBullet(const Ogre::Quaternion &q) - { - return btQuaternion(q.x, q.y, q.z, q.w); - } - static btVector3 toBullet(const Ogre::Vector3 &v) - { - return btVector3(v.x, v.y, v.z); - } + static btQuaternion toBullet(const Ogre::Quaternion &q) + { + return btQuaternion(q.x, q.y, q.z, q.w); + } + static btVector3 toBullet(const Ogre::Vector3 &v) + { + return btVector3(v.x, v.y, v.z); + } - static Ogre::Quaternion toOgre(const btQuaternion &q) - { - return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z()); - } - static Ogre::Vector3 toOgre(const btVector3 &v) - { - return Ogre::Vector3(v.x(), v.y(), v.z()); - } + static Ogre::Quaternion toOgre(const btQuaternion &q) + { + return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z()); + } + static Ogre::Vector3 toOgre(const btVector3 &v) + { + return Ogre::Vector3(v.x(), v.y(), v.z()); + } }; //From here on its debug-drawing stuff. ------------------------------------------------------------------ @@ -151,11 +151,11 @@ public: /// Remove all points from the point list void clear(); - /// Call this to update the hardware buffer after making changes. + /// Call this to update the hardware buffer after making changes. void update(); /** Set the type of operation to draw with. - * @param opType Can be one of + * @param opType Can be one of * - RenderOperation::OT_LINE_STRIP * - RenderOperation::OT_LINE_LIST * - RenderOperation::OT_POINT_LIST @@ -181,20 +181,20 @@ private: class DebugDrawer : public btIDebugDraw { protected: - Ogre::SceneNode *mNode; - btDynamicsWorld *mWorld; - DynamicLines *mLineDrawer; - bool mDebugOn; + Ogre::SceneNode *mNode; + btDynamicsWorld *mWorld; + DynamicLines *mLineDrawer; + bool mDebugOn; public: - DebugDrawer(Ogre::SceneNode *node, btDynamicsWorld *world) - : mNode(node), - mWorld(world), - mDebugOn(true) - { - mLineDrawer = new DynamicLines(Ogre::RenderOperation::OT_LINE_LIST); - mNode->attachObject(mLineDrawer); + DebugDrawer(Ogre::SceneNode *node, btDynamicsWorld *world) + : mNode(node), + mWorld(world), + mDebugOn(true) + { + mLineDrawer = new DynamicLines(Ogre::RenderOperation::OT_LINE_LIST); + mNode->attachObject(mLineDrawer); if (!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists("BtOgre")) Ogre::ResourceGroupManager::getSingleton().createResourceGroup("BtOgre"); @@ -205,68 +205,68 @@ public: mat->setSelfIllumination(1,1,1); } - mLineDrawer->setMaterial("BtOgre/DebugLines"); - } + mLineDrawer->setMaterial("BtOgre/DebugLines"); + } - ~DebugDrawer() - { + ~DebugDrawer() + { Ogre::MaterialManager::getSingleton().remove("BtOgre/DebugLines"); Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup("BtOgre"); - delete mLineDrawer; - } + delete mLineDrawer; + } - void step() - { - if (mDebugOn) - { - mWorld->debugDrawWorld(); - mLineDrawer->update(); - mNode->needUpdate(); - mLineDrawer->clear(); - } - else - { - mLineDrawer->clear(); - mLineDrawer->update(); - mNode->needUpdate(); - } - } + void step() + { + if (mDebugOn) + { + mWorld->debugDrawWorld(); + mLineDrawer->update(); + mNode->needUpdate(); + mLineDrawer->clear(); + } + else + { + mLineDrawer->clear(); + mLineDrawer->update(); + mNode->needUpdate(); + } + } - void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) - { - mLineDrawer->addPoint(Convert::toOgre(from)); - mLineDrawer->addPoint(Convert::toOgre(to)); - } + void drawLine(const btVector3& from,const btVector3& to,const btVector3& color) + { + mLineDrawer->addPoint(Convert::toOgre(from)); + mLineDrawer->addPoint(Convert::toOgre(to)); + } - void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color) - { - mLineDrawer->addPoint(Convert::toOgre(PointOnB)); - mLineDrawer->addPoint(Convert::toOgre(PointOnB) + (Convert::toOgre(normalOnB) * distance * 20)); - } + void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color) + { + mLineDrawer->addPoint(Convert::toOgre(PointOnB)); + mLineDrawer->addPoint(Convert::toOgre(PointOnB) + (Convert::toOgre(normalOnB) * distance * 20)); + } - void reportErrorWarning(const char* warningString) - { - Ogre::LogManager::getSingleton().logMessage(warningString); - } + void reportErrorWarning(const char* warningString) + { + Ogre::LogManager::getSingleton().logMessage(warningString); + } - void draw3dText(const btVector3& location,const char* textString) - { - } - - //0 for off, anything else for on. - void setDebugMode(int isOn) - { - mDebugOn = (isOn == 0) ? false : true; + void draw3dText(const btVector3& location,const char* textString) + { + } - if (!mDebugOn) - mLineDrawer->clear(); - } - - //0 for off, anything else for on. - int getDebugMode() const - { - return mDebugOn; - } + //0 for off, anything else for on. + void setDebugMode(int isOn) + { + mDebugOn = (isOn == 0) ? false : true; + + if (!mDebugOn) + mLineDrawer->clear(); + } + + //0 for off, anything else for on. + int getDebugMode() const + { + return mDebugOn; + } }; diff --git a/bullet/BtOgreGP.h b/bullet/BtOgreGP.h index 1894a79b3b..f0534de4bb 100644 --- a/bullet/BtOgreGP.h +++ b/bullet/BtOgreGP.h @@ -3,7 +3,7 @@ * * Filename: BtOgreGP.h * - * Description: The part of BtOgre that handles information transfer from Ogre to + * Description: The part of BtOgre that handles information transfer from Ogre to * Bullet (like mesh data for making trimeshes). * * Version: 1.0 @@ -29,49 +29,49 @@ typedef std::pair BoneKeyIndex; class VertexIndexToShape { public: - VertexIndexToShape(const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); - ~VertexIndexToShape(); + VertexIndexToShape(const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + ~VertexIndexToShape(); - Ogre::Real getRadius(); - Ogre::Vector3 getSize(); + Ogre::Real getRadius(); + Ogre::Vector3 getSize(); - btSphereShape* createSphere(); - btBoxShape* createBox(); - btBvhTriangleMeshShape* createTrimesh(); - btCylinderShape* createCylinder(); - btConvexHullShape* createConvex(); + btSphereShape* createSphere(); + btBoxShape* createBox(); + btBvhTriangleMeshShape* createTrimesh(); + btCylinderShape* createCylinder(); + btConvexHullShape* createConvex(); - const Ogre::Vector3* getVertices(); - unsigned int getVertexCount(); - const unsigned int* getIndices(); - unsigned int getIndexCount(); + const Ogre::Vector3* getVertices(); + unsigned int getVertexCount(); + const unsigned int* getIndices(); + unsigned int getIndexCount(); protected: - void addStaticVertexData(const Ogre::VertexData *vertex_data); + void addStaticVertexData(const Ogre::VertexData *vertex_data); - void addAnimatedVertexData(const Ogre::VertexData *vertex_data, - const Ogre::VertexData *blended_data, - const Ogre::Mesh::IndexMap *indexMap); + void addAnimatedVertexData(const Ogre::VertexData *vertex_data, + const Ogre::VertexData *blended_data, + const Ogre::Mesh::IndexMap *indexMap); - void addIndexData(Ogre::IndexData *data, const unsigned int offset = 0); + void addIndexData(Ogre::IndexData *data, const unsigned int offset = 0); protected: - Ogre::Vector3* mVertexBuffer; - unsigned int* mIndexBuffer; - unsigned int mVertexCount; - unsigned int mIndexCount; + Ogre::Vector3* mVertexBuffer; + unsigned int* mIndexBuffer; + unsigned int mVertexCount; + unsigned int mIndexCount; - Ogre::Matrix4 mTransform; + Ogre::Matrix4 mTransform; - Ogre::Real mBoundRadius; - Ogre::Vector3 mBounds; + Ogre::Real mBoundRadius; + Ogre::Vector3 mBounds; - BoneIndex *mBoneIndex; + BoneIndex *mBoneIndex; - Ogre::Vector3 mScale; + Ogre::Vector3 mScale; }; //For static (non-animated) meshes. @@ -79,21 +79,21 @@ class StaticMeshToShapeConverter : public VertexIndexToShape { public: - StaticMeshToShapeConverter(Ogre::Renderable *rend, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); - StaticMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); - StaticMeshToShapeConverter(); + StaticMeshToShapeConverter(Ogre::Renderable *rend, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + StaticMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + StaticMeshToShapeConverter(); - ~StaticMeshToShapeConverter(); + ~StaticMeshToShapeConverter(); - void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); - - void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + + void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); protected: - Ogre::Entity* mEntity; - Ogre::SceneNode* mNode; + Ogre::Entity* mEntity; + Ogre::SceneNode* mNode; }; //For animated meshes. @@ -101,43 +101,42 @@ class AnimatedMeshToShapeConverter : public VertexIndexToShape { public: - AnimatedMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); - AnimatedMeshToShapeConverter(); - ~AnimatedMeshToShapeConverter(); + AnimatedMeshToShapeConverter(Ogre::Entity *entity, const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + AnimatedMeshToShapeConverter(); + ~AnimatedMeshToShapeConverter(); - void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); - void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform); + void addEntity(Ogre::Entity *entity,const Ogre::Matrix4 &transform = Ogre::Matrix4::IDENTITY); + void addMesh(const Ogre::MeshPtr &mesh, const Ogre::Matrix4 &transform); - btBoxShape* createAlignedBox(unsigned char bone, - const Ogre::Vector3 &bonePosition, - const Ogre::Quaternion &boneOrientation); + btBoxShape* createAlignedBox(unsigned char bone, + const Ogre::Vector3 &bonePosition, + const Ogre::Quaternion &boneOrientation); - btBoxShape* createOrientedBox(unsigned char bone, - const Ogre::Vector3 &bonePosition, - const Ogre::Quaternion &boneOrientation); + btBoxShape* createOrientedBox(unsigned char bone, + const Ogre::Vector3 &bonePosition, + const Ogre::Quaternion &boneOrientation); protected: - bool getBoneVertices(unsigned char bone, - unsigned int &vertex_count, - Ogre::Vector3* &vertices, - const Ogre::Vector3 &bonePosition); + bool getBoneVertices(unsigned char bone, + unsigned int &vertex_count, + Ogre::Vector3* &vertices, + const Ogre::Vector3 &bonePosition); - bool getOrientedBox(unsigned char bone, - const Ogre::Vector3 &bonePosition, - const Ogre::Quaternion &boneOrientation, - Ogre::Vector3 &extents, - Ogre::Vector3 *axis, - Ogre::Vector3 ¢er); + bool getOrientedBox(unsigned char bone, + const Ogre::Vector3 &bonePosition, + const Ogre::Quaternion &boneOrientation, + Ogre::Vector3 &extents, + Ogre::Vector3 *axis, + Ogre::Vector3 ¢er); - - Ogre::Entity* mEntity; - Ogre::SceneNode* mNode; + Ogre::Entity* mEntity; + Ogre::SceneNode* mNode; - Ogre::Vector3 *mTransformedVerticesTemp; - size_t mTransformedVerticesTempSize; + Ogre::Vector3 *mTransformedVerticesTemp; + size_t mTransformedVerticesTempSize; }; } -#endif \ No newline at end of file +#endif diff --git a/bullet/BtOgrePG.h b/bullet/BtOgrePG.h index b1d2715409..9ff069a8f9 100644 --- a/bullet/BtOgrePG.h +++ b/bullet/BtOgrePG.h @@ -3,7 +3,7 @@ * * Filename: BtOgrePG.h * - * Description: The part of BtOgre that handles information transfer from Bullet to + * Description: The part of BtOgre that handles information transfer from Bullet to * Ogre (like updating graphics object positions). * * Version: 1.0 @@ -25,7 +25,7 @@ namespace BtOgre { //A MotionState is Bullet's way of informing you about updates to an object. //Pass this MotionState to a btRigidBody to have your SceneNode updated automaticaly. -class RigidBodyState : public btMotionState +class RigidBodyState : public btMotionState { protected: btTransform mTransform; @@ -42,19 +42,19 @@ class RigidBodyState : public btMotionState } RigidBodyState(Ogre::SceneNode *node) - : mTransform(((node != NULL) ? BtOgre::Convert::toBullet(node->getOrientation()) : btQuaternion(0,0,0,1)), + : mTransform(((node != NULL) ? BtOgre::Convert::toBullet(node->getOrientation()) : btQuaternion(0,0,0,1)), ((node != NULL) ? BtOgre::Convert::toBullet(node->getPosition()) : btVector3(0,0,0))), mCenterOfMassOffset(btTransform::getIdentity()), mNode(node) { } - virtual void getWorldTransform(btTransform &ret) const + virtual void getWorldTransform(btTransform &ret) const { ret = mCenterOfMassOffset.inverse() * mTransform; } - virtual void setWorldTransform(const btTransform &in) + virtual void setWorldTransform(const btTransform &in) { if (mNode == NULL) return; @@ -68,7 +68,7 @@ class RigidBodyState : public btMotionState mNode->setPosition(pos.x(), pos.y(), pos.z()); } - void setNode(Ogre::SceneNode *node) + void setNode(Ogre::SceneNode *node) { mNode = node; } diff --git a/bullet/BulletShapeLoader.cpp b/bullet/BulletShapeLoader.cpp index 927ec6a27a..48b87e1e8c 100644 --- a/bullet/BulletShapeLoader.cpp +++ b/bullet/BulletShapeLoader.cpp @@ -2,8 +2,8 @@ -BulletShape::BulletShape(Ogre::ResourceManager* creator, const Ogre::String &name, - Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual, +BulletShape::BulletShape(Ogre::ResourceManager* creator, const Ogre::String &name, + Ogre::ResourceHandle handle, const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader) : Ogre::Resource(creator, name, handle, group, isManual, loader) { @@ -12,9 +12,9 @@ Ogre::Resource(creator, name, handle, group, isManual, loader) /* For consistency with StringInterface, but we don't add any parameters here That's because the Resource implementation of StringInterface is to - list all the options that need to be set before loading, of which + list all the options that need to be set before loading, of which we have none as such. Full details can be set through scripts. - */ + */ Shape = NULL; collide = true; createParamDictionary("BulletShape"); @@ -70,8 +70,8 @@ BulletShapeManager *BulletShapeManager::getSingletonPtr() } BulletShapeManager &BulletShapeManager::getSingleton() -{ - assert(ms_Singleton); +{ + assert(ms_Singleton); return(*ms_Singleton); } @@ -103,8 +103,8 @@ BulletShapePtr BulletShapeManager::load(const Ogre::String &name, const Ogre::St return textf; } -Ogre::Resource *BulletShapeManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle, - const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, +Ogre::Resource *BulletShapeManager::createImpl(const Ogre::String &name, Ogre::ResourceHandle handle, + const Ogre::String &group, bool isManual, Ogre::ManualResourceLoader *loader, const Ogre::NameValuePairList *createParams) { BulletShape* res = new BulletShape(this, name, handle, group, isManual, loader); @@ -121,4 +121,4 @@ void BulletShapeLoader::loadResource(Ogre::Resource *resource) {} void BulletShapeLoader::load(const std::string &name,const std::string &group) -{} \ No newline at end of file +{} diff --git a/bullet/BulletShapeLoader.h b/bullet/BulletShapeLoader.h index e28a7175e3..316ee523c2 100644 --- a/bullet/BulletShapeLoader.h +++ b/bullet/BulletShapeLoader.h @@ -128,11 +128,11 @@ class BulletShapeLoader : public Ogre::ManualResourceLoader public: BulletShapeLoader(){}; - virtual ~BulletShapeLoader() {} + virtual ~BulletShapeLoader() {} - virtual void loadResource(Ogre::Resource *resource); + virtual void loadResource(Ogre::Resource *resource); - virtual void load(const std::string &name,const std::string &group); + virtual void load(const std::string &name,const std::string &group); }; -#endif \ No newline at end of file +#endif diff --git a/bullet/CMotionState.cpp b/bullet/CMotionState.cpp index 3725fd77a8..5ddef51752 100644 --- a/bullet/CMotionState.cpp +++ b/bullet/CMotionState.cpp @@ -10,36 +10,36 @@ namespace OEngine { namespace Physic { - CMotionState::CMotionState(PhysicEngine* eng,std::string name) - { - pEng = eng; - tr.setIdentity(); - pName = name; - }; + CMotionState::CMotionState(PhysicEngine* eng,std::string name) + { + pEng = eng; + tr.setIdentity(); + pName = name; + }; - void CMotionState::getWorldTransform(btTransform &worldTrans) const - { - worldTrans = tr; - } + void CMotionState::getWorldTransform(btTransform &worldTrans) const + { + worldTrans = tr; + } - void CMotionState::setWorldTransform(const btTransform &worldTrans) - { - tr = worldTrans; + void CMotionState::setWorldTransform(const btTransform &worldTrans) + { + tr = worldTrans; - PhysicEvent evt; - evt.isNPC = isNPC; - evt.isPC = isPC; - evt.newTransform = tr; - evt.RigidBodyName = pName; + PhysicEvent evt; + evt.isNPC = isNPC; + evt.isPC = isPC; + evt.newTransform = tr; + evt.RigidBodyName = pName; - if(isPC) - { - pEng->PEventList.push_back(evt); - } - else - { - pEng->NPEventList.push_back(evt); - } - } + if(isPC) + { + pEng->PEventList.push_back(evt); + } + else + { + pEng->NPEventList.push_back(evt); + } + } }} diff --git a/bullet/CMotionState.h b/bullet/CMotionState.h index 3dfb3a05cf..3508ab4ef1 100644 --- a/bullet/CMotionState.h +++ b/bullet/CMotionState.h @@ -7,46 +7,46 @@ namespace OEngine { namespace Physic { - class PhysicEngine; + class PhysicEngine; - /** - *A CMotionState is associated with a single RigidBody. - *When the RigidBody is moved by bullet, bullet will call the function setWorldTransform. - *for more info, see the bullet Wiki at btMotionState. - */ - class CMotionState:public btMotionState - { - public: + /** + * A CMotionState is associated with a single RigidBody. + * When the RigidBody is moved by bullet, bullet will call the function setWorldTransform. + * for more info, see the bullet Wiki at btMotionState. + */ + class CMotionState:public btMotionState + { + public: - CMotionState(PhysicEngine* eng,std::string name); + CMotionState(PhysicEngine* eng,std::string name); - /** - *Return the position of the RigidBody. - */ - virtual void getWorldTransform(btTransform &worldTrans) const; + /** + * Return the position of the RigidBody. + */ + virtual void getWorldTransform(btTransform &worldTrans) const; - /** - *Function called by bullet when the RigidBody is moved. - *It add an event to the EventList of the PhysicEngine class. - */ - virtual void setWorldTransform(const btTransform &worldTrans); + /** + * Function called by bullet when the RigidBody is moved. + * It add an event to the EventList of the PhysicEngine class. + */ + virtual void setWorldTransform(const btTransform &worldTrans); - protected: - PhysicEngine* pEng; - btTransform tr; - bool isNPC; - bool isPC; + protected: + PhysicEngine* pEng; + btTransform tr; + bool isNPC; + bool isPC; - std::string pName; - }; + std::string pName; + }; - struct PhysicEvent - { - bool isNPC; - bool isPC; - btTransform newTransform; - std::string RigidBodyName; - }; + struct PhysicEvent + { + bool isNPC; + bool isPC; + btTransform newTransform; + std::string RigidBodyName; + }; }} #endif diff --git a/bullet/btKinematicCharacterController.cpp b/bullet/btKinematicCharacterController.cpp index d930349850..fc4f3278f4 100644 --- a/bullet/btKinematicCharacterController.cpp +++ b/bullet/btKinematicCharacterController.cpp @@ -4,8 +4,8 @@ Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -28,109 +28,109 @@ subject to the following restrictions: class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: - btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - { - m_me[0] = me; - count = 1; - } + btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + { + m_me[0] = me; + count = 1; + } - btKinematicClosestNotMeRayResultCallback (btCollisionObject* me[], int count_) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - { - count = count_; + btKinematicClosestNotMeRayResultCallback (btCollisionObject* me[], int count_) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + { + count = count_; - for(int i = 0; i < count; i++) - m_me[i] = me[i]; - } + for(int i = 0; i < count; i++) + m_me[i] = me[i]; + } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) - { - for(int i = 0; i < count; i++) - if (rayResult.m_collisionObject == m_me[i]) - return 1.0; + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) + { + for(int i = 0; i < count; i++) + if (rayResult.m_collisionObject == m_me[i]) + return 1.0; - return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); - } + return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); + } protected: - btCollisionObject* m_me[10]; - int count; + btCollisionObject* m_me[10]; + int count; }; class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: - btKinematicClosestNotMeConvexResultCallback( btCollisionObject* me, const btVector3& up, btScalar minSlopeDot ) - : btCollisionWorld::ClosestConvexResultCallback( btVector3( 0.0, 0.0, 0.0 ), btVector3( 0.0, 0.0, 0.0 ) ), - m_me( me ), m_up( up ), m_minSlopeDot( minSlopeDot ) - { - } + btKinematicClosestNotMeConvexResultCallback( btCollisionObject* me, const btVector3& up, btScalar minSlopeDot ) + : btCollisionWorld::ClosestConvexResultCallback( btVector3( 0.0, 0.0, 0.0 ), btVector3( 0.0, 0.0, 0.0 ) ), + m_me( me ), m_up( up ), m_minSlopeDot( minSlopeDot ) + { + } - virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) - { - if( convexResult.m_hitCollisionObject == m_me ) - return btScalar( 1 ); + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if( convexResult.m_hitCollisionObject == m_me ) + return btScalar( 1 ); - btVector3 hitNormalWorld; - if( normalInWorldSpace ) - { - hitNormalWorld = convexResult.m_hitNormalLocal; - } - else - { - ///need to transform normal into worldspace - hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; - } + btVector3 hitNormalWorld; + if( normalInWorldSpace ) + { + hitNormalWorld = convexResult.m_hitNormalLocal; + } + else + { + ///need to transform normal into worldspace + hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; + } - // NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box... + // NOTE : m_hitNormalLocal is not always vertical on the ground with a capsule or a box... - btScalar dotUp = m_up.dot(hitNormalWorld); - if( dotUp < m_minSlopeDot ) - return btScalar( 1 ); + btScalar dotUp = m_up.dot(hitNormalWorld); + if( dotUp < m_minSlopeDot ) + return btScalar( 1 ); - return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace); - } + return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace); + } protected: - btCollisionObject* m_me; - const btVector3 m_up; - btScalar m_minSlopeDot; + btCollisionObject* m_me; + const btVector3 m_up; + btScalar m_minSlopeDot; }; btKinematicCharacterController::btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject_, - btPairCachingGhostObject* internalGhostObject_, - btScalar stepHeight, - btScalar constantScale, - btScalar gravity, - btScalar fallVelocity, - btScalar jumpVelocity, - btScalar recoveringFactor ) + btPairCachingGhostObject* internalGhostObject_, + btScalar stepHeight, + btScalar constantScale, + btScalar gravity, + btScalar fallVelocity, + btScalar jumpVelocity, + btScalar recoveringFactor ) { - m_upAxis = btKinematicCharacterController::Y_AXIS; + m_upAxis = btKinematicCharacterController::Y_AXIS; - m_walkDirection.setValue( btScalar( 0 ), btScalar( 0 ), btScalar( 0 ) ); + m_walkDirection.setValue( btScalar( 0 ), btScalar( 0 ), btScalar( 0 ) ); - m_useGhostObjectSweepTest = true; + m_useGhostObjectSweepTest = true; - externalGhostObject = externalGhostObject_; - internalGhostObject = internalGhostObject_; + externalGhostObject = externalGhostObject_; + internalGhostObject = internalGhostObject_; - m_recoveringFactor = recoveringFactor; + m_recoveringFactor = recoveringFactor; - m_stepHeight = stepHeight; + m_stepHeight = stepHeight; - m_useWalkDirection = true; // use walk direction by default, legacy behavior - m_velocityTimeInterval = btScalar( 0 ); - m_verticalVelocity = btScalar( 0 ); - m_verticalOffset = btScalar( 0 ); + m_useWalkDirection = true; // use walk direction by default, legacy behavior + m_velocityTimeInterval = btScalar( 0 ); + m_verticalVelocity = btScalar( 0 ); + m_verticalOffset = btScalar( 0 ); - m_gravity = constantScale * gravity; - m_fallSpeed = constantScale * fallVelocity; // Terminal velocity of a sky diver in m/s. + m_gravity = constantScale * gravity; + m_fallSpeed = constantScale * fallVelocity; // Terminal velocity of a sky diver in m/s. - m_jumpSpeed = constantScale * jumpVelocity; // ? - m_wasJumping = false; + m_jumpSpeed = constantScale * jumpVelocity; // ? + m_wasJumping = false; - setMaxSlope( btRadians( 45.0 ) ); + setMaxSlope( btRadians( 45.0 ) ); mCollision = true; } @@ -147,78 +147,78 @@ void btKinematicCharacterController::setVerticalVelocity(float z) bool btKinematicCharacterController::recoverFromPenetration( btCollisionWorld* collisionWorld ) { - bool penetration = false; + bool penetration = false; if(!mCollision) return penetration; - collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(), - collisionWorld->getDispatchInfo(), - collisionWorld->getDispatcher() ); + collisionWorld->getDispatcher()->dispatchAllCollisionPairs( internalGhostObject->getOverlappingPairCache(), + collisionWorld->getDispatchInfo(), + collisionWorld->getDispatcher() ); - btVector3 currentPosition = internalGhostObject->getWorldTransform().getOrigin(); + btVector3 currentPosition = internalGhostObject->getWorldTransform().getOrigin(); - btScalar maxPen = btScalar( 0 ); + btScalar maxPen = btScalar( 0 ); - for( int i = 0; i < internalGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ ) - { - m_manifoldArray.resize(0); + for( int i = 0; i < internalGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ ) + { + m_manifoldArray.resize(0); - btBroadphasePair* collisionPair = &internalGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; + btBroadphasePair* collisionPair = &internalGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; - if( collisionPair->m_algorithm ) - collisionPair->m_algorithm->getAllContactManifolds( m_manifoldArray ); + if( collisionPair->m_algorithm ) + collisionPair->m_algorithm->getAllContactManifolds( m_manifoldArray ); - for( int j = 0; j < m_manifoldArray.size(); j++ ) - { - btPersistentManifold* manifold = m_manifoldArray[j]; + for( int j = 0; j < m_manifoldArray.size(); j++ ) + { + btPersistentManifold* manifold = m_manifoldArray[j]; - btScalar directionSign = manifold->getBody0() == internalGhostObject ? btScalar( -1.0 ) : btScalar( 1.0 ); + btScalar directionSign = manifold->getBody0() == internalGhostObject ? btScalar( -1.0 ) : btScalar( 1.0 ); - for( int p = 0; p < manifold->getNumContacts(); p++ ) - { - const btManifoldPoint&pt = manifold->getContactPoint( p ); - if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject) - ||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) ) - { - } - else - { - btScalar dist = pt.getDistance(); + for( int p = 0; p < manifold->getNumContacts(); p++ ) + { + const btManifoldPoint&pt = manifold->getContactPoint( p ); + if( (manifold->getBody1() == externalGhostObject && manifold->getBody0() == internalGhostObject) + ||(manifold->getBody0() == externalGhostObject && manifold->getBody1() == internalGhostObject) ) + { + } + else + { + btScalar dist = pt.getDistance(); - if( dist < 0.0 ) - { - if( dist < maxPen ) - maxPen = dist; + if( dist < 0.0 ) + { + if( dist < maxPen ) + maxPen = dist; - // NOTE : btScalar affects the stairs but the parkinson... - // 0.0 , the capsule can break the walls... - currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor; + // NOTE : btScalar affects the stairs but the parkinson... + // 0.0 , the capsule can break the walls... + currentPosition += pt.m_normalWorldOnB * directionSign * dist * m_recoveringFactor; - penetration = true; - } - } - } + penetration = true; + } + } + } - // ??? - //manifold->clearManifold(); - } - } + // ??? + //manifold->clearManifold(); + } + } - btTransform transform = internalGhostObject->getWorldTransform(); + btTransform transform = internalGhostObject->getWorldTransform(); - transform.setOrigin( currentPosition ); + transform.setOrigin( currentPosition ); - internalGhostObject->setWorldTransform( transform ); - externalGhostObject->setWorldTransform( transform ); + internalGhostObject->setWorldTransform( transform ); + externalGhostObject->setWorldTransform( transform ); - return penetration; + return penetration; } btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const btVector3& currentPosition, btScalar& currentStepOffset ) { - btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) ); + btVector3 targetPosition = currentPosition + getUpAxisDirections()[ m_upAxis ] * ( m_stepHeight + ( m_verticalOffset > btScalar( 0.0 ) ? m_verticalOffset : 0.0 ) ); //if the no collisions mode is on, no need to go any further if(!mCollision) @@ -227,248 +227,248 @@ btVector3 btKinematicCharacterController::stepUp( btCollisionWorld* world, const return targetPosition; } - // Retrieve the collision shape - // - btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); - btAssert( collisionShape->isConvex() ); + // Retrieve the collision shape + // + btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); + btAssert( collisionShape->isConvex() ); - btConvexShape* convexShape = ( btConvexShape* )collisionShape; + btConvexShape* convexShape = ( btConvexShape* )collisionShape; - // FIXME: Handle penetration properly - // - btTransform start; - start.setIdentity(); - start.setOrigin( currentPosition + getUpAxisDirections()[ m_upAxis ] * ( convexShape->getMargin() ) ); + // FIXME: Handle penetration properly + // + btTransform start; + start.setIdentity(); + start.setOrigin( currentPosition + getUpAxisDirections()[ m_upAxis ] * ( convexShape->getMargin() ) ); - btTransform end; - end.setIdentity(); - end.setOrigin( targetPosition ); + btTransform end; + end.setIdentity(); + end.setOrigin( targetPosition ); - btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, -getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); - callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; - callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, -getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); + callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; - // Sweep test - // - if( m_useGhostObjectSweepTest ) - externalGhostObject->convexSweepTest( convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration ); + // Sweep test + // + if( m_useGhostObjectSweepTest ) + externalGhostObject->convexSweepTest( convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration ); - else - world->convexSweepTest( convexShape, start, end, callback ); + else + world->convexSweepTest( convexShape, start, end, callback ); - if( callback.hasHit() ) - { - // Only modify the position if the hit was a slope and not a wall or ceiling. - // - if( callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > btScalar( 0.0 ) ) - { - // We moved up only a fraction of the step height - // - currentStepOffset = m_stepHeight * callback.m_closestHitFraction; + if( callback.hasHit() ) + { + // Only modify the position if the hit was a slope and not a wall or ceiling. + // + if( callback.m_hitNormalWorld.dot(getUpAxisDirections()[m_upAxis]) > btScalar( 0.0 ) ) + { + // We moved up only a fraction of the step height + // + currentStepOffset = m_stepHeight * callback.m_closestHitFraction; - return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); - } + return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); + } - m_verticalVelocity = btScalar( 0.0 ); - m_verticalOffset = btScalar( 0.0 ); + m_verticalVelocity = btScalar( 0.0 ); + m_verticalOffset = btScalar( 0.0 ); - return currentPosition; - } - else - { - currentStepOffset = m_stepHeight; - return targetPosition; - } + return currentPosition; + } + else + { + currentStepOffset = m_stepHeight; + return targetPosition; + } } ///Reflect the vector d around the vector r inline btVector3 reflect( const btVector3& d, const btVector3& r ) { - return d - ( btScalar( 2.0 ) * d.dot( r ) ) * r; + return d - ( btScalar( 2.0 ) * d.dot( r ) ) * r; } ///Project a vector u on another vector v inline btVector3 project( const btVector3& u, const btVector3& v ) { - return v * u.dot( v ); + return v * u.dot( v ); } ///Helper for computing the character sliding inline btVector3 slide( const btVector3& direction, const btVector3& planeNormal ) { - return direction - project( direction, planeNormal ); + return direction - project( direction, planeNormal ); } btVector3 slideOnCollision( const btVector3& fromPosition, const btVector3& toPosition, const btVector3& hitNormal ) { - btVector3 moveDirection = toPosition - fromPosition; - btScalar moveLength = moveDirection.length(); + btVector3 moveDirection = toPosition - fromPosition; + btScalar moveLength = moveDirection.length(); - if( moveLength <= btScalar( SIMD_EPSILON ) ) - return toPosition; + if( moveLength <= btScalar( SIMD_EPSILON ) ) + return toPosition; - moveDirection.normalize(); + moveDirection.normalize(); - btVector3 reflectDir = reflect( moveDirection, hitNormal ); - reflectDir.normalize(); + btVector3 reflectDir = reflect( moveDirection, hitNormal ); + reflectDir.normalize(); - return fromPosition + slide( reflectDir, hitNormal ) * moveLength; + return fromPosition + slide( reflectDir, hitNormal ) * moveLength; } btVector3 btKinematicCharacterController::stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove ) { - // We go to ! - // - btVector3 targetPosition = currentPosition + walkMove; + // We go to ! + // + btVector3 targetPosition = currentPosition + walkMove; //if the no collisions mode is on, no need to go any further if(!mCollision) return targetPosition; - // Retrieve the collision shape - // - btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); - btAssert( collisionShape->isConvex() ); + // Retrieve the collision shape + // + btCollisionShape* collisionShape = externalGhostObject->getCollisionShape(); + btAssert( collisionShape->isConvex() ); - btConvexShape* convexShape = ( btConvexShape* )collisionShape; + btConvexShape* convexShape = ( btConvexShape* )collisionShape; - btTransform start; - start.setIdentity(); + btTransform start; + start.setIdentity(); - btTransform end; - end.setIdentity(); + btTransform end; + end.setIdentity(); - btScalar fraction = btScalar( 1.0 ); + btScalar fraction = btScalar( 1.0 ); - // This optimization scheme suffers in the corners. - // It basically jumps from a wall to another, then fails to find a new - // position (after 4 iterations here) and finally don't move at all. - // - // The stepping algorithm adds some problems with stairs. It seems - // the treads create some fake corner using capsules for collisions. - // - for( int i = 0; i < 4 && fraction > btScalar( 0.01 ); i++ ) - { - start.setOrigin( currentPosition ); - end.setOrigin( targetPosition ); + // This optimization scheme suffers in the corners. + // It basically jumps from a wall to another, then fails to find a new + // position (after 4 iterations here) and finally don't move at all. + // + // The stepping algorithm adds some problems with stairs. It seems + // the treads create some fake corner using capsules for collisions. + // + for( int i = 0; i < 4 && fraction > btScalar( 0.01 ); i++ ) + { + start.setOrigin( currentPosition ); + end.setOrigin( targetPosition ); - btVector3 sweepDirNegative = currentPosition - targetPosition; + btVector3 sweepDirNegative = currentPosition - targetPosition; - btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, sweepDirNegative, btScalar( 0.0 ) ); - callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; - callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + btKinematicClosestNotMeConvexResultCallback callback( externalGhostObject, sweepDirNegative, btScalar( 0.0 ) ); + callback.m_collisionFilterGroup = externalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = externalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; - if( m_useGhostObjectSweepTest ) - externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + if( m_useGhostObjectSweepTest ) + externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - else - collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + else + collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - if( callback.hasHit() ) - { - // Try another target position - // - targetPosition = slideOnCollision( currentPosition, targetPosition, callback.m_hitNormalWorld ); - fraction = callback.m_closestHitFraction; - } - else + if( callback.hasHit() ) + { + // Try another target position + // + targetPosition = slideOnCollision( currentPosition, targetPosition, callback.m_hitNormalWorld ); + fraction = callback.m_closestHitFraction; + } + else - // Move to the valid target position - // - return targetPosition; - } + // Move to the valid target position + // + return targetPosition; + } - // Don't move if you can't find a valid target position... - // It prevents some flickering. - // - return currentPosition; + // Don't move if you can't find a valid target position... + // It prevents some flickering. + // + return currentPosition; } ///Handle the gravity btScalar btKinematicCharacterController::addFallOffset( bool wasOnGround, btScalar currentStepOffset, btScalar dt ) { - btScalar downVelocity = ( m_verticalVelocity < 0.0 ? -m_verticalVelocity : btScalar( 0.0 ) ) * dt; + btScalar downVelocity = ( m_verticalVelocity < 0.0 ? -m_verticalVelocity : btScalar( 0.0 ) ) * dt; - if( downVelocity > btScalar( 0.0 ) && downVelocity < m_stepHeight && ( wasOnGround || !m_wasJumping ) ) - downVelocity = m_stepHeight; + if( downVelocity > btScalar( 0.0 ) && downVelocity < m_stepHeight && ( wasOnGround || !m_wasJumping ) ) + downVelocity = m_stepHeight; - return currentStepOffset + downVelocity; + return currentStepOffset + downVelocity; } btVector3 btKinematicCharacterController::stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset ) { - btVector3 stepDrop = getUpAxisDirections()[ m_upAxis ] * currentStepOffset; + btVector3 stepDrop = getUpAxisDirections()[ m_upAxis ] * currentStepOffset; - // Be sure we are falling from the last m_currentPosition - // It prevents some flickering - // - btVector3 targetPosition = currentPosition - stepDrop; + // Be sure we are falling from the last m_currentPosition + // It prevents some flickering + // + btVector3 targetPosition = currentPosition - stepDrop; //if the no collisions mode is on, no need to go any further if(!mCollision) return targetPosition; - btTransform start; - start.setIdentity(); - start.setOrigin( currentPosition ); + btTransform start; + start.setIdentity(); + start.setOrigin( currentPosition ); - btTransform end; - end.setIdentity(); - end.setOrigin( targetPosition ); + btTransform end; + end.setIdentity(); + end.setOrigin( targetPosition ); - btKinematicClosestNotMeConvexResultCallback callback( internalGhostObject, getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); - callback.m_collisionFilterGroup = internalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; - callback.m_collisionFilterMask = internalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + btKinematicClosestNotMeConvexResultCallback callback( internalGhostObject, getUpAxisDirections()[ m_upAxis ], m_maxSlopeCosine ); + callback.m_collisionFilterGroup = internalGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = internalGhostObject->getBroadphaseHandle()->m_collisionFilterMask; - // Retrieve the collision shape - // - btCollisionShape* collisionShape = internalGhostObject->getCollisionShape(); - btAssert( collisionShape->isConvex() ); - btConvexShape* convexShape = ( btConvexShape* )collisionShape; + // Retrieve the collision shape + // + btCollisionShape* collisionShape = internalGhostObject->getCollisionShape(); + btAssert( collisionShape->isConvex() ); + btConvexShape* convexShape = ( btConvexShape* )collisionShape; - if( m_useGhostObjectSweepTest ) - externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + if( m_useGhostObjectSweepTest ) + externalGhostObject->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - else - collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); + else + collisionWorld->convexSweepTest( convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration ); - if( callback.hasHit() ) - { - m_verticalVelocity = btScalar( 0.0 ); - m_verticalOffset = btScalar( 0.0 ); - m_wasJumping = false; + if( callback.hasHit() ) + { + m_verticalVelocity = btScalar( 0.0 ); + m_verticalOffset = btScalar( 0.0 ); + m_wasJumping = false; - // We dropped a fraction of the height -> hit floor - // - return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); - } - else + // We dropped a fraction of the height -> hit floor + // + return currentPosition.lerp( targetPosition, callback.m_closestHitFraction ); + } + else - // We dropped the full height - // - return targetPosition; + // We dropped the full height + // + return targetPosition; } void btKinematicCharacterController::setWalkDirection( const btVector3& walkDirection ) { - m_useWalkDirection = true; - m_walkDirection = walkDirection; + m_useWalkDirection = true; + m_walkDirection = walkDirection; } void btKinematicCharacterController::setVelocityForTimeInterval( const btVector3& velocity, btScalar timeInterval ) { - m_useWalkDirection = false; - m_walkDirection = velocity; - m_velocityTimeInterval = timeInterval; + m_useWalkDirection = false; + m_walkDirection = velocity; + m_velocityTimeInterval = timeInterval; } @@ -479,162 +479,162 @@ void btKinematicCharacterController::reset() void btKinematicCharacterController::warp( const btVector3& origin ) { - btTransform transform; - transform.setIdentity(); - transform.setOrigin( -origin ); + btTransform transform; + transform.setIdentity(); + transform.setOrigin( -origin ); - externalGhostObject->setWorldTransform( transform ); - internalGhostObject->setWorldTransform( transform ); + externalGhostObject->setWorldTransform( transform ); + internalGhostObject->setWorldTransform( transform ); } void btKinematicCharacterController::preStep( btCollisionWorld* collisionWorld ) { - BT_PROFILE( "preStep" ); + BT_PROFILE( "preStep" ); - for( int i = 0; i < 4 && recoverFromPenetration ( collisionWorld ); i++ ); + for( int i = 0; i < 4 && recoverFromPenetration ( collisionWorld ); i++ ); } void btKinematicCharacterController::playerStep( btCollisionWorld* collisionWorld, btScalar dt ) { - BT_PROFILE( "playerStep" ); + BT_PROFILE( "playerStep" ); - if( !m_useWalkDirection && m_velocityTimeInterval <= btScalar( 0.0 ) ) - return; + if( !m_useWalkDirection && m_velocityTimeInterval <= btScalar( 0.0 ) ) + return; - bool wasOnGround = onGround(); + bool wasOnGround = onGround(); - // Handle the gravity - // - m_verticalVelocity -= m_gravity * dt; + // Handle the gravity + // + m_verticalVelocity -= m_gravity * dt; - if( m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed ) - m_verticalVelocity = m_jumpSpeed; + if( m_verticalVelocity > 0.0 && m_verticalVelocity > m_jumpSpeed ) + m_verticalVelocity = m_jumpSpeed; - if( m_verticalVelocity < 0.0 && btFabs( m_verticalVelocity ) > btFabs( m_fallSpeed ) ) - m_verticalVelocity = -btFabs( m_fallSpeed ); + if( m_verticalVelocity < 0.0 && btFabs( m_verticalVelocity ) > btFabs( m_fallSpeed ) ) + m_verticalVelocity = -btFabs( m_fallSpeed ); - m_verticalOffset = m_verticalVelocity * dt; + m_verticalOffset = m_verticalVelocity * dt; - // This forced stepping up can cause problems when the character - // walks (jump in fact...) under too low ceilings. - // - btVector3 currentPosition = externalGhostObject->getWorldTransform().getOrigin(); - btScalar currentStepOffset; + // This forced stepping up can cause problems when the character + // walks (jump in fact...) under too low ceilings. + // + btVector3 currentPosition = externalGhostObject->getWorldTransform().getOrigin(); + btScalar currentStepOffset; - currentPosition = stepUp( collisionWorld, currentPosition, currentStepOffset ); + currentPosition = stepUp( collisionWorld, currentPosition, currentStepOffset ); - // Move in the air and slide against the walls ignoring the stair steps. - // - if( m_useWalkDirection ) - currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, m_walkDirection ); + // Move in the air and slide against the walls ignoring the stair steps. + // + if( m_useWalkDirection ) + currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, m_walkDirection ); - else - { - btScalar dtMoving = ( dt < m_velocityTimeInterval ) ? dt : m_velocityTimeInterval; - m_velocityTimeInterval -= dt; + else + { + btScalar dtMoving = ( dt < m_velocityTimeInterval ) ? dt : m_velocityTimeInterval; + m_velocityTimeInterval -= dt; - // How far will we move while we are moving ? - // - btVector3 moveDirection = m_walkDirection * dtMoving; + // How far will we move while we are moving ? + // + btVector3 moveDirection = m_walkDirection * dtMoving; - currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, moveDirection ); - } + currentPosition = stepForwardAndStrafe( collisionWorld, currentPosition, moveDirection ); + } - // Finally find the ground. - // - currentStepOffset = addFallOffset( wasOnGround, currentStepOffset, dt ); + // Finally find the ground. + // + currentStepOffset = addFallOffset( wasOnGround, currentStepOffset, dt ); - currentPosition = stepDown( collisionWorld, currentPosition, currentStepOffset ); + currentPosition = stepDown( collisionWorld, currentPosition, currentStepOffset ); - // Apply the new position to the collision objects. - // - btTransform tranform; - tranform = externalGhostObject->getWorldTransform(); - tranform.setOrigin( currentPosition ); + // Apply the new position to the collision objects. + // + btTransform tranform; + tranform = externalGhostObject->getWorldTransform(); + tranform.setOrigin( currentPosition ); - externalGhostObject->setWorldTransform( tranform ); - internalGhostObject->setWorldTransform( tranform ); + externalGhostObject->setWorldTransform( tranform ); + internalGhostObject->setWorldTransform( tranform ); } void btKinematicCharacterController::setFallSpeed( btScalar fallSpeed ) { - m_fallSpeed = fallSpeed; + m_fallSpeed = fallSpeed; } void btKinematicCharacterController::setJumpSpeed( btScalar jumpSpeed ) { - m_jumpSpeed = jumpSpeed; + m_jumpSpeed = jumpSpeed; } void btKinematicCharacterController::setMaxJumpHeight( btScalar maxJumpHeight ) { - m_maxJumpHeight = maxJumpHeight; + m_maxJumpHeight = maxJumpHeight; } bool btKinematicCharacterController::canJump() const { - return onGround(); + return onGround(); } void btKinematicCharacterController::jump() { - if( !canJump() ) - return; + if( !canJump() ) + return; - m_verticalVelocity = m_jumpSpeed; - m_wasJumping = true; + m_verticalVelocity = m_jumpSpeed; + m_wasJumping = true; } void btKinematicCharacterController::setGravity( btScalar gravity ) { - m_gravity = gravity; + m_gravity = gravity; } btScalar btKinematicCharacterController::getGravity() const { - return m_gravity; + return m_gravity; } void btKinematicCharacterController::setMaxSlope( btScalar slopeRadians ) { - m_maxSlopeRadians = slopeRadians; - m_maxSlopeCosine = btCos( slopeRadians ); + m_maxSlopeRadians = slopeRadians; + m_maxSlopeCosine = btCos( slopeRadians ); } btScalar btKinematicCharacterController::getMaxSlope() const { - return m_maxSlopeRadians; + return m_maxSlopeRadians; } bool btKinematicCharacterController::onGround() const { - return btFabs( m_verticalVelocity ) < btScalar( SIMD_EPSILON ) && - btFabs( m_verticalOffset ) < btScalar( SIMD_EPSILON ); + return btFabs( m_verticalVelocity ) < btScalar( SIMD_EPSILON ) && + btFabs( m_verticalOffset ) < btScalar( SIMD_EPSILON ); } btVector3* btKinematicCharacterController::getUpAxisDirections() { - static btVector3 sUpAxisDirection[] = - { - btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 0.0 ) ), - btVector3( btScalar( 0.0 ), btScalar( 1.0 ), btScalar( 0.0 ) ), - btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 1.0 ) ) - }; + static btVector3 sUpAxisDirection[] = + { + btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 0.0 ) ), + btVector3( btScalar( 0.0 ), btScalar( 1.0 ), btScalar( 0.0 ) ), + btVector3( btScalar( 0.0 ), btScalar( 0.0 ), btScalar( 1.0 ) ) + }; - return sUpAxisDirection; + return sUpAxisDirection; } diff --git a/bullet/btKinematicCharacterController.h b/bullet/btKinematicCharacterController.h index d58e242ad3..d24cd97222 100644 --- a/bullet/btKinematicCharacterController.h +++ b/bullet/btKinematicCharacterController.h @@ -44,41 +44,41 @@ public: }; private: - btPairCachingGhostObject* externalGhostObject; // use this for querying collisions for sliding and move - btPairCachingGhostObject* internalGhostObject; // and this for recoreving from penetrations + btPairCachingGhostObject* externalGhostObject; // use this for querying collisions for sliding and move + btPairCachingGhostObject* internalGhostObject; // and this for recoreving from penetrations - btScalar m_verticalVelocity; - btScalar m_verticalOffset; - btScalar m_fallSpeed; - btScalar m_jumpSpeed; - btScalar m_maxJumpHeight; - btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) - btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) - btScalar m_gravity; - btScalar m_recoveringFactor; + btScalar m_verticalVelocity; + btScalar m_verticalOffset; + btScalar m_fallSpeed; + btScalar m_jumpSpeed; + btScalar m_maxJumpHeight; + btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value) + btScalar m_maxSlopeCosine; // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization) + btScalar m_gravity; + btScalar m_recoveringFactor; - btScalar m_stepHeight; + btScalar m_stepHeight; - ///this is the desired walk direction, set by the user - btVector3 m_walkDirection; + ///this is the desired walk direction, set by the user + btVector3 m_walkDirection; - ///keep track of the contact manifolds - btManifoldArray m_manifoldArray; + ///keep track of the contact manifolds + btManifoldArray m_manifoldArray; ///Gravity attributes - bool m_wasJumping; + bool m_wasJumping; - bool m_useGhostObjectSweepTest; - bool m_useWalkDirection; - btScalar m_velocityTimeInterval; + bool m_useGhostObjectSweepTest; + bool m_useWalkDirection; + btScalar m_velocityTimeInterval; - UpAxis m_upAxis; + UpAxis m_upAxis; - static btVector3* getUpAxisDirections(); + static btVector3* getUpAxisDirections(); - bool recoverFromPenetration ( btCollisionWorld* collisionWorld ); + bool recoverFromPenetration ( btCollisionWorld* collisionWorld ); - btVector3 stepUp( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar& currentStepOffset ); + btVector3 stepUp( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar& currentStepOffset ); btVector3 stepForwardAndStrafe( btCollisionWorld* collisionWorld, const btVector3& currentPosition, const btVector3& walkMove ); btScalar addFallOffset( bool wasJumping, btScalar currentStepOffset, btScalar dt ); btVector3 stepDown( btCollisionWorld* collisionWorld, const btVector3& currentPosition, btScalar currentStepOffset ); @@ -90,7 +90,7 @@ public: /// Using a smaller internalGhostObject can help for removing some flickering but create some /// stopping artefacts when sliding along stairs or small walls. /// Don't forget to scale gravity and fallSpeed if you scale the world. - btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject, + btKinematicCharacterController( btPairCachingGhostObject* externalGhostObject, btPairCachingGhostObject* internalGhostObject, btScalar stepHeight, btScalar constantScale = btScalar( 1.0 ), @@ -99,67 +99,67 @@ public: btScalar jumpVelocity = btScalar( 9.8 ), btScalar recoveringFactor = btScalar( 0.2 ) ); - ~btKinematicCharacterController (); + ~btKinematicCharacterController (); void setVerticalVelocity(float z); - ///btActionInterface interface - virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime ) - { + ///btActionInterface interface + virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTime ) + { preStep( collisionWorld ); - playerStep( collisionWorld, deltaTime ); - } + playerStep( collisionWorld, deltaTime ); + } - ///btActionInterface interface - void debugDraw( btIDebugDraw* debugDrawer ); + ///btActionInterface interface + void debugDraw( btIDebugDraw* debugDrawer ); void setUpAxis( UpAxis axis ) - { - m_upAxis = axis; - } + { + m_upAxis = axis; + } - /// This should probably be called setPositionIncrementPerSimulatorStep. - /// This is neither a direction nor a velocity, but the amount to - /// increment the position each simulation iteration, regardless - /// of dt. - /// This call will reset any velocity set by setVelocityForTimeInterval(). - virtual void setWalkDirection(const btVector3& walkDirection); + /// This should probably be called setPositionIncrementPerSimulatorStep. + /// This is neither a direction nor a velocity, but the amount to + /// increment the position each simulation iteration, regardless + /// of dt. + /// This call will reset any velocity set by setVelocityForTimeInterval(). + virtual void setWalkDirection(const btVector3& walkDirection); - /// Caller provides a velocity with which the character should move for - /// the given time period. After the time period, velocity is reset - /// to zero. - /// This call will reset any walk direction set by setWalkDirection(). - /// Negative time intervals will result in no motion. - virtual void setVelocityForTimeInterval(const btVector3& velocity, - btScalar timeInterval); + /// Caller provides a velocity with which the character should move for + /// the given time period. After the time period, velocity is reset + /// to zero. + /// This call will reset any walk direction set by setWalkDirection(). + /// Negative time intervals will result in no motion. + virtual void setVelocityForTimeInterval(const btVector3& velocity, + btScalar timeInterval); - void reset(); - void warp( const btVector3& origin ); + void reset(); + void warp( const btVector3& origin ); - void preStep( btCollisionWorld* collisionWorld ); - void playerStep( btCollisionWorld* collisionWorld, btScalar dt ); + void preStep( btCollisionWorld* collisionWorld ); + void playerStep( btCollisionWorld* collisionWorld, btScalar dt ); - void setFallSpeed( btScalar fallSpeed ); - void setJumpSpeed( btScalar jumpSpeed ); - void setMaxJumpHeight( btScalar maxJumpHeight ); - bool canJump() const; + void setFallSpeed( btScalar fallSpeed ); + void setJumpSpeed( btScalar jumpSpeed ); + void setMaxJumpHeight( btScalar maxJumpHeight ); + bool canJump() const; - void jump(); + void jump(); - void setGravity( btScalar gravity ); - btScalar getGravity() const; + void setGravity( btScalar gravity ); + btScalar getGravity() const; - /// The max slope determines the maximum angle that the controller can walk up. - /// The slope angle is measured in radians. - void setMaxSlope( btScalar slopeRadians ); - btScalar getMaxSlope() const; + /// The max slope determines the maximum angle that the controller can walk up. + /// The slope angle is measured in radians. + void setMaxSlope( btScalar slopeRadians ); + btScalar getMaxSlope() const; - void setUseGhostSweepTest( bool useGhostObjectSweepTest ) - { - m_useGhostObjectSweepTest = useGhostObjectSweepTest; - } + void setUseGhostSweepTest( bool useGhostObjectSweepTest ) + { + m_useGhostObjectSweepTest = useGhostObjectSweepTest; + } - bool onGround() const; + bool onGround() const; //if set to false, there will be no collision. bool mCollision; diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 986b57be5d..8cf7b8eb64 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -15,63 +15,63 @@ namespace OEngine { namespace Physic { - enum collisiontypes { - COL_NOTHING = 0, //setWorldTransform( transform ); + // External capsule + externalGhostObject = new PairCachingGhostObject(name); + externalGhostObject->setWorldTransform( transform ); - btScalar externalCapsuleHeight = 130; - btScalar externalCapsuleWidth = 16; + btScalar externalCapsuleHeight = 130; + btScalar externalCapsuleWidth = 16; - externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); - externalCollisionShape->setMargin( 0.1 ); + externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); + externalCollisionShape->setMargin( 0.1 ); - externalGhostObject->setCollisionShape( externalCollisionShape ); - externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); + externalGhostObject->setCollisionShape( externalCollisionShape ); + externalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); - // Internal capsule - internalGhostObject = new PairCachingGhostObject(name); - internalGhostObject->setWorldTransform( transform ); - //internalGhostObject->getBroadphaseHandle()->s - btScalar internalCapsuleHeight = 120; - btScalar internalCapsuleWidth = 15; + // Internal capsule + internalGhostObject = new PairCachingGhostObject(name); + internalGhostObject->setWorldTransform( transform ); + //internalGhostObject->getBroadphaseHandle()->s + btScalar internalCapsuleHeight = 120; + btScalar internalCapsuleWidth = 15; - internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); - internalCollisionShape->setMargin( 0.1 ); + internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); + internalCollisionShape->setMargin( 0.1 ); - internalGhostObject->setCollisionShape( internalCollisionShape ); - internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); + internalGhostObject->setCollisionShape( internalCollisionShape ); + internalGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); - mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 40 ),1,4,20,9.8,0.2 ); - mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS); + mCharacter = new btKinematicCharacterController( externalGhostObject,internalGhostObject,btScalar( 40 ),1,4,20,9.8,0.2 ); + mCharacter->setUpAxis(btKinematicCharacterController::Z_AXIS); mCharacter->setUseGhostSweepTest(false); mCharacter->mCollision = false; setGravity(0); - } + } - PhysicActor::~PhysicActor() - { - delete mCharacter; - delete internalGhostObject; - delete internalCollisionShape; - delete externalGhostObject; - delete externalCollisionShape; - } + PhysicActor::~PhysicActor() + { + delete mCharacter; + delete internalGhostObject; + delete internalCollisionShape; + delete externalGhostObject; + delete externalCollisionShape; + } void PhysicActor::setGravity(float gravity) { @@ -94,100 +94,100 @@ namespace Physic return mCharacter->mCollision; } - void PhysicActor::setWalkDirection(const btVector3& mvt) - { - mCharacter->setWalkDirection( mvt ); - } + void PhysicActor::setWalkDirection(const btVector3& mvt) + { + mCharacter->setWalkDirection( mvt ); + } - void PhysicActor::Rotate(const btQuaternion& quat) - { - externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat ); - internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat ); - } + void PhysicActor::Rotate(const btQuaternion& quat) + { + externalGhostObject->getWorldTransform().setRotation( externalGhostObject->getWorldTransform().getRotation() * quat ); + internalGhostObject->getWorldTransform().setRotation( internalGhostObject->getWorldTransform().getRotation() * quat ); + } - void PhysicActor::setRotation(const btQuaternion& quat) - { - externalGhostObject->getWorldTransform().setRotation( quat ); - internalGhostObject->getWorldTransform().setRotation( quat ); - } + void PhysicActor::setRotation(const btQuaternion& quat) + { + externalGhostObject->getWorldTransform().setRotation( quat ); + internalGhostObject->getWorldTransform().setRotation( quat ); + } - btVector3 PhysicActor::getPosition(void) - { - return internalGhostObject->getWorldTransform().getOrigin(); - } + btVector3 PhysicActor::getPosition(void) + { + return internalGhostObject->getWorldTransform().getOrigin(); + } - btQuaternion PhysicActor::getRotation(void) - { - return internalGhostObject->getWorldTransform().getRotation(); - } + btQuaternion PhysicActor::getRotation(void) + { + return internalGhostObject->getWorldTransform().getRotation(); + } - void PhysicActor::setPosition(const btVector3& pos) - { - internalGhostObject->getWorldTransform().setOrigin(pos); - externalGhostObject->getWorldTransform().setOrigin(pos); - } + void PhysicActor::setPosition(const btVector3& pos) + { + internalGhostObject->getWorldTransform().setOrigin(pos); + externalGhostObject->getWorldTransform().setOrigin(pos); + } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name) - :btRigidBody(CI),mName(name) - { + RigidBody::RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name) + :btRigidBody(CI),mName(name) + { - }; + }; - /////////////////////////////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////// - PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) - { - // Set up the collision configuration and dispatcher - collisionConfiguration = new btDefaultCollisionConfiguration(); - dispatcher = new btCollisionDispatcher(collisionConfiguration); + PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) + { + // Set up the collision configuration and dispatcher + collisionConfiguration = new btDefaultCollisionConfiguration(); + dispatcher = new btCollisionDispatcher(collisionConfiguration); - // The actual physics solver - solver = new btSequentialImpulseConstraintSolver; + // The actual physics solver + solver = new btSequentialImpulseConstraintSolver; - //TODO: memory leak? - btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); - pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); + //TODO: memory leak? + btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); + pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); - broadphase = new btDbvtBroadphase(pairCache); + broadphase = new btDbvtBroadphase(pairCache); - // The world. - dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); - dynamicsWorld->setGravity(btVector3(0,0,-10)); + // The world. + dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,broadphase,solver,collisionConfiguration); + dynamicsWorld->setGravity(btVector3(0,0,-10)); - if(BulletShapeManager::getSingletonPtr() == NULL) - { - new BulletShapeManager(); - } - //TODO:singleton? - mShapeLoader = shapeLoader; + if(BulletShapeManager::getSingletonPtr() == NULL) + { + new BulletShapeManager(); + } + //TODO:singleton? + mShapeLoader = shapeLoader; - isDebugCreated = false; - } + isDebugCreated = false; + } - void PhysicEngine::createDebugRendering() - { - if(!isDebugCreated) - { - Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator(); - iter.begin(); - Ogre::SceneManager* scn = iter.getNext(); - Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode(); - node->pitch(Ogre::Degree(-90)); - mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); - dynamicsWorld->setDebugDrawer(mDebugDrawer); - isDebugCreated = true; - dynamicsWorld->debugDrawWorld(); - } - } + void PhysicEngine::createDebugRendering() + { + if(!isDebugCreated) + { + Ogre::SceneManagerEnumerator::SceneManagerIterator iter = Ogre::Root::getSingleton().getSceneManagerIterator(); + iter.begin(); + Ogre::SceneManager* scn = iter.getNext(); + Ogre::SceneNode* node = scn->getRootSceneNode()->createChildSceneNode(); + node->pitch(Ogre::Degree(-90)); + mDebugDrawer = new BtOgre::DebugDrawer(node, dynamicsWorld); + dynamicsWorld->setDebugDrawer(mDebugDrawer); + isDebugCreated = true; + dynamicsWorld->debugDrawWorld(); + } + } void PhysicEngine::setDebugRenderingMode(int mode) { @@ -198,69 +198,69 @@ namespace Physic mDebugDrawer->setDebugMode(mode); } - PhysicEngine::~PhysicEngine() - { - delete dynamicsWorld; - delete solver; - delete collisionConfiguration; - delete dispatcher; - delete broadphase; - delete mShapeLoader; - } + PhysicEngine::~PhysicEngine() + { + delete dynamicsWorld; + delete solver; + delete collisionConfiguration; + delete dispatcher; + delete broadphase; + delete mShapeLoader; + } - RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name) - { - //get the shape from the .nif - mShapeLoader->load(mesh,"General"); - BulletShapeManager::getSingletonPtr()->load(mesh,"General"); - BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); + RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name) + { + //get the shape from the .nif + mShapeLoader->load(mesh,"General"); + BulletShapeManager::getSingletonPtr()->load(mesh,"General"); + BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); - //create the motionState - CMotionState* newMotionState = new CMotionState(this,name); + //create the motionState + CMotionState* newMotionState = new CMotionState(this,name); - //create the real body - btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape); - RigidBody* body = new RigidBody(CI,name); + //create the real body + btRigidBody::btRigidBodyConstructionInfo CI = btRigidBody::btRigidBodyConstructionInfo(0,newMotionState,shape->Shape); + RigidBody* body = new RigidBody(CI,name); body->collide = shape->collide; - return body; - } + return body; + } - void PhysicEngine::addRigidBody(RigidBody* body) - { + void PhysicEngine::addRigidBody(RigidBody* body) + { if(body->collide) { - dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL); + dynamicsWorld->addRigidBody(body,COL_WORLD,COL_WORLD|COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL); } else { dynamicsWorld->addRigidBody(body,COL_WORLD,COL_NOTHING); } - body->setActivationState(DISABLE_DEACTIVATION); - RigidBodyMap[body->mName] = body; - } + body->setActivationState(DISABLE_DEACTIVATION); + RigidBodyMap[body->mName] = body; + } - void PhysicEngine::removeRigidBody(std::string name) - { + void PhysicEngine::removeRigidBody(std::string name) + { std::map::iterator it = RigidBodyMap.find(name); if (it != RigidBodyMap.end() ) { RigidBody* body = it->second; if(body != NULL) { - // broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + // broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); /*std::map::iterator it2 = PhysicActorMap.begin(); - for(;it2!=PhysicActorMap.end();it++) - { - it2->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - it2->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); - }*/ + for(;it2!=PhysicActorMap.end();it++) + { + it2->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + it2->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(body->getBroadphaseProxy(),dispatcher); + }*/ dynamicsWorld->removeRigidBody(RigidBodyMap[name]); } } - } + } - void PhysicEngine::deleteRigidBody(std::string name) - { + void PhysicEngine::deleteRigidBody(std::string name) + { std::map::iterator it = RigidBodyMap.find(name); if (it != RigidBodyMap.end() ) { @@ -271,34 +271,34 @@ namespace Physic } RigidBodyMap.erase(it); } - } + } - RigidBody* PhysicEngine::getRigidBody(std::string name) - { - RigidBody* body = RigidBodyMap[name]; - return body; - } + RigidBody* PhysicEngine::getRigidBody(std::string name) + { + RigidBody* body = RigidBodyMap[name]; + return body; + } - void PhysicEngine::stepSimulation(double deltaT) - { - dynamicsWorld->stepSimulation(deltaT,1,1/50.); - if(isDebugCreated) - { - mDebugDrawer->step(); - } - } + void PhysicEngine::stepSimulation(double deltaT) + { + dynamicsWorld->stepSimulation(deltaT,1,1/50.); + if(isDebugCreated) + { + mDebugDrawer->step(); + } + } - void PhysicEngine::addCharacter(std::string name) - { - PhysicActor* newActor = new PhysicActor(name); - dynamicsWorld->addCollisionObject( newActor->externalGhostObject, COL_ACTOR_EXTERNAL, COL_WORLD |COL_ACTOR_EXTERNAL ); - dynamicsWorld->addCollisionObject( newActor->internalGhostObject, COL_ACTOR_INTERNAL, COL_WORLD |COL_ACTOR_INTERNAL ); - dynamicsWorld->addAction( newActor->mCharacter ); - PhysicActorMap[name] = newActor; - } + void PhysicEngine::addCharacter(std::string name) + { + PhysicActor* newActor = new PhysicActor(name); + dynamicsWorld->addCollisionObject( newActor->externalGhostObject, COL_ACTOR_EXTERNAL, COL_WORLD |COL_ACTOR_EXTERNAL ); + dynamicsWorld->addCollisionObject( newActor->internalGhostObject, COL_ACTOR_INTERNAL, COL_WORLD |COL_ACTOR_INTERNAL ); + dynamicsWorld->addAction( newActor->mCharacter ); + PhysicActorMap[name] = newActor; + } - void PhysicEngine::removeCharacter(std::string name) - { + void PhysicEngine::removeCharacter(std::string name) + { //std::cout << "remove"; std::map::iterator it = PhysicActorMap.find(name); if (it != PhysicActorMap.end() ) @@ -307,15 +307,15 @@ namespace Physic if(act != NULL) { /*broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); - broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); - std::map::iterator it2 = PhysicActorMap.begin(); - for(;it2!=PhysicActorMap.end();it++) - { - it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); - it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); - it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); - it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); - }*/ + broadphase->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); + std::map::iterator it2 = PhysicActorMap.begin(); + for(;it2!=PhysicActorMap.end();it++) + { + it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); + it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->externalGhostObject->getBroadphaseHandle(),dispatcher); + it->second->internalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); + it->second->externalGhostObject->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(act->internalGhostObject->getBroadphaseHandle(),dispatcher); + }*/ //act->externalGhostObject-> dynamicsWorld->removeCollisionObject(act->externalGhostObject); dynamicsWorld->removeCollisionObject(act->internalGhostObject); @@ -325,17 +325,17 @@ namespace Physic PhysicActorMap.erase(it); } //std::cout << "ok"; - } + } - PhysicActor* PhysicEngine::getCharacter(std::string name) - { - PhysicActor* act = PhysicActorMap[name]; - return act; - } + PhysicActor* PhysicEngine::getCharacter(std::string name) + { + PhysicActor* act = PhysicActorMap[name]; + return act; + } - void PhysicEngine::emptyEventLists(void) - { - } + void PhysicEngine::emptyEventLists(void) + { + } std::pair PhysicEngine::rayTest(btVector3& from,btVector3& to) { diff --git a/bullet/physic.hpp b/bullet/physic.hpp index 0cf6abd4ff..d4dfde4673 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -18,19 +18,19 @@ class btKinematicCharacterController; namespace BtOgre { - class DebugDrawer; + class DebugDrawer; } namespace MWWorld { - class World; + class World; } namespace OEngine { namespace Physic { - class CMotionState; - struct PhysicEvent; + class CMotionState; + struct PhysicEvent; /** *This is just used to be able to name objects. @@ -45,179 +45,179 @@ namespace Physic std::string mName; }; - /** - *A physic Actor use a modifed KinematicCharacterController taken in the bullet forum. - */ - class PhysicActor - { - public: - PhysicActor(std::string name); + /** + * A physic Actor use a modifed KinematicCharacterController taken in the bullet forum. + */ + class PhysicActor + { + public: + PhysicActor(std::string name); - ~PhysicActor(); + ~PhysicActor(); - /** - *This function set the walkDirection. This is not relative to the actor orientation. - *I think it's also needed to take time into account. A typical call should look like this: - *setWalkDirection( mvt * orientation * dt) - */ - void setWalkDirection(const btVector3& mvt); + /** + * This function set the walkDirection. This is not relative to the actor orientation. + * I think it's also needed to take time into account. A typical call should look like this: + * setWalkDirection( mvt * orientation * dt) + */ + void setWalkDirection(const btVector3& mvt); - void Rotate(const btQuaternion& quat); + void Rotate(const btQuaternion& quat); - void setRotation(const btQuaternion& quat); + void setRotation(const btQuaternion& quat); void setGravity(float gravity); - + void setVerticalVelocity(float z); void enableCollisions(bool collision); bool getCollisionMode(); - btVector3 getPosition(void); + btVector3 getPosition(void); - btQuaternion getRotation(void); + btQuaternion getRotation(void); - void setPosition(const btVector3& pos); + void setPosition(const btVector3& pos); - btKinematicCharacterController* mCharacter; + btKinematicCharacterController* mCharacter; - PairCachingGhostObject* internalGhostObject; - btCollisionShape* internalCollisionShape; + PairCachingGhostObject* internalGhostObject; + btCollisionShape* internalCollisionShape; - PairCachingGhostObject* externalGhostObject; - btCollisionShape* externalCollisionShape; + PairCachingGhostObject* externalGhostObject; + btCollisionShape* externalCollisionShape; - std::string mName; - }; + std::string mName; + }; - /** - *This class is just an extension of normal btRigidBody in order to add extra info. - *When bullet give back a btRigidBody, you can just do a static_cast to RigidBody, - *so one never should use btRigidBody directly! - */ - class RigidBody: public btRigidBody - { - public: - RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); - std::string mName; + /** + *This class is just an extension of normal btRigidBody in order to add extra info. + *When bullet give back a btRigidBody, you can just do a static_cast to RigidBody, + *so one never should use btRigidBody directly! + */ + class RigidBody: public btRigidBody + { + public: + RigidBody(btRigidBody::btRigidBodyConstructionInfo& CI,std::string name); + std::string mName; //is this body used for raycasting only? bool collide; - }; + }; - /** - *The PhysicEngine class contain everything which is needed for Physic. - *It's needed that Ogre Resources are set up before the PhysicEngine is created. - *Note:deleting it WILL NOT delete the RigidBody! - *TODO:unload unused resources? - */ - class PhysicEngine - { - public: + /** + * The PhysicEngine class contain everything which is needed for Physic. + * It's needed that Ogre Resources are set up before the PhysicEngine is created. + * Note:deleting it WILL NOT delete the RigidBody! + * TODO:unload unused resources? + */ + class PhysicEngine + { + public: /** - *Note that the shapeLoader IS destroyed by the phyic Engine!! - */ - PhysicEngine(BulletShapeLoader* shapeLoader); + * Note that the shapeLoader IS destroyed by the phyic Engine!! + */ + PhysicEngine(BulletShapeLoader* shapeLoader); /** - *It DOES destroy the shape loader! - */ - ~PhysicEngine(); - - /** - *create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map, - *so you can get it with the getRigidBody function. - */ - RigidBody* createRigidBody(std::string mesh,std::string name); - - /** - *Add a RigidBody to the simulation - */ - void addRigidBody(RigidBody* body); - - /** - *Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. - */ - void removeRigidBody(std::string name); - - /** - *delete a RigidBody, and remove it from RigidBodyMap. - */ - void deleteRigidBody(std::string name); - - /** - *Return a pointer to a given rigid body. - *TODO:check if exist - */ - RigidBody* getRigidBody(std::string name); - - /** - *Create and add a character to the scene, and add it to the ActorMap. - */ - void addCharacter(std::string name); - - /** - *Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done? - */ - void removeCharacter(std::string name); - - /** - *return a pointer to a character - *TODO:check if the actor exist... - */ - PhysicActor* getCharacter(std::string name); - - /** - *This step the simulation of a given time. - */ - void stepSimulation(double deltaT); - - /** - *Empty events lists - */ - void emptyEventLists(void); - - /** - *Create a debug rendering. It is called by setDebgRenderingMode if it's not created yet. - *Important Note: this will crash if the Render is not yet initialise! - */ - void createDebugRendering(); - - /** - *Set the debug rendering mode. 0 to turn it off. - *Important Note: this will crash if the Render is not yet initialise! - */ - void setDebugRenderingMode(int mode); + * It DOES destroy the shape loader! + */ + ~PhysicEngine(); /** - *Return the closest object hit by a ray. If there are no objects, it will return ("",-1). - */ + * Create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map, + * so you can get it with the getRigidBody function. + */ + RigidBody* createRigidBody(std::string mesh,std::string name); + + /** + * Add a RigidBody to the simulation + */ + void addRigidBody(RigidBody* body); + + /** + * Remove a RigidBody from the simulation. It does not delete it, and does not remove it from the RigidBodyMap. + */ + void removeRigidBody(std::string name); + + /** + * Delete a RigidBody, and remove it from RigidBodyMap. + */ + void deleteRigidBody(std::string name); + + /** + * Return a pointer to a given rigid body. + * TODO:check if exist + */ + RigidBody* getRigidBody(std::string name); + + /** + * Create and add a character to the scene, and add it to the ActorMap. + */ + void addCharacter(std::string name); + + /** + * Remove a character from the scene. TODO:delete it! for now, a small memory leak^^ done? + */ + void removeCharacter(std::string name); + + /** + * Return a pointer to a character + * TODO:check if the actor exist... + */ + PhysicActor* getCharacter(std::string name); + + /** + * This step the simulation of a given time. + */ + void stepSimulation(double deltaT); + + /** + * Empty events lists + */ + void emptyEventLists(void); + + /** + * Create a debug rendering. It is called by setDebgRenderingMode if it's not created yet. + * Important Note: this will crash if the Render is not yet initialise! + */ + void createDebugRendering(); + + /** + * Set the debug rendering mode. 0 to turn it off. + * Important Note: this will crash if the Render is not yet initialise! + */ + void setDebugRenderingMode(int mode); + + /** + * Return the closest object hit by a ray. If there are no objects, it will return ("",-1). + */ std::pair rayTest(btVector3& from,btVector3& to); - //event list of non player object - std::list NPEventList; + //event list of non player object + std::list NPEventList; - //event list affecting the player - std::list PEventList; + //event list affecting the player + std::list PEventList; - //Bullet Stuff - btBroadphaseInterface* broadphase; - btDefaultCollisionConfiguration* collisionConfiguration; - btSequentialImpulseConstraintSolver* solver; - btCollisionDispatcher* dispatcher; - btDiscreteDynamicsWorld* dynamicsWorld; + //Bullet Stuff + btBroadphaseInterface* broadphase; + btDefaultCollisionConfiguration* collisionConfiguration; + btSequentialImpulseConstraintSolver* solver; + btCollisionDispatcher* dispatcher; + btDiscreteDynamicsWorld* dynamicsWorld; - //the NIF file loader. - BulletShapeLoader* mShapeLoader; + //the NIF file loader. + BulletShapeLoader* mShapeLoader; - std::map RigidBodyMap; - std::map PhysicActorMap; + std::map RigidBodyMap; + std::map PhysicActorMap; - //debug rendering - BtOgre::DebugDrawer* mDebugDrawer; - bool isDebugCreated; - }; + //debug rendering + BtOgre::DebugDrawer* mDebugDrawer; + bool isDebugCreated; + }; }} diff --git a/mangle b/mangle index a05046026e..f3c9694bf2 160000 --- a/mangle +++ b/mangle @@ -1 +1 @@ -Subproject commit a05046026ec9edb1e528fac2c70f887239302237 +Subproject commit f3c9694bf249a34eae05f0304e6bfc120014ce8c diff --git a/ogre/mouselook.cpp b/ogre/mouselook.cpp index c74f5365c4..841bab603a 100644 --- a/ogre/mouselook.cpp +++ b/ogre/mouselook.cpp @@ -10,48 +10,48 @@ using namespace OEngine::Render; void MouseLookEvent::event(Type type, int index, const void *p) { - if(type != EV_MouseMove || camera == NULL) return; + if(type != EV_MouseMove || camera == NULL) return; - MouseEvent *arg = (MouseEvent*)(p); + MouseEvent *arg = (MouseEvent*)(p); - float x = arg->state.X.rel * sensX; - float y = arg->state.Y.rel * sensY; + float x = arg->state.X.rel * sensX; + float y = arg->state.Y.rel * sensY; - camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x)); - camera->getParentSceneNode()->pitch(Degree(-y)); - if(flipProt) - { - // The camera before pitching - /*Quaternion nopitch = camera->getParentSceneNode()->getOrientation(); + camera->getParentSceneNode()->getParentSceneNode()->yaw(Degree(-x)); + camera->getParentSceneNode()->pitch(Degree(-y)); + if(flipProt) + { + // The camera before pitching + /*Quaternion nopitch = camera->getParentSceneNode()->getOrientation(); - camera->getParentSceneNode()->pitch(Degree(-y)); + camera->getParentSceneNode()->pitch(Degree(-y)); - // Apply some failsafe measures against the camera flipping - // upside down. Is the camera close to pointing straight up or - // down? - if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1) - // If so, undo the last pitch - camera->getParentSceneNode()->setOrientation(nopitch);*/ - //camera->getU + // Apply some failsafe measures against the camera flipping + // upside down. Is the camera close to pointing straight up or + // down? + if(Ogre::Vector3(camera->getParentSceneNode()->getOrientation()*Ogre::Vector3::UNIT_Y)[1] <= 0.1) + // If so, undo the last pitch + camera->getParentSceneNode()->setOrientation(nopitch);*/ + //camera->getU - // Angle of rotation around the X-axis. - float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees()); + // Angle of rotation around the X-axis. + float pitchAngle = (2 * Ogre::Degree(Ogre::Math::ACos(camera->getParentSceneNode()->getOrientation().w)).valueDegrees()); - // Just to determine the sign of the angle we pick up above, the - // value itself does not interest us. - float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x; + // Just to determine the sign of the angle we pick up above, the + // value itself does not interest us. + float pitchAngleSign = camera->getParentSceneNode()->getOrientation().x; - // Limit the pitch between -90 degress and +90 degrees, Quake3-style. - if (pitchAngle > 90.0f) - { - if (pitchAngleSign > 0) - // Set orientation to 90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - Ogre::Math::Sqrt(0.5f), 0, 0)); - else if (pitchAngleSign < 0) - // Sets orientation to -90 degrees on X-axis. - camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), - -Ogre::Math::Sqrt(0.5f), 0, 0)); - } - } + // Limit the pitch between -90 degress and +90 degrees, Quake3-style. + if (pitchAngle > 90.0f) + { + if (pitchAngleSign > 0) + // Set orientation to 90 degrees on X-axis. + camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), + Ogre::Math::Sqrt(0.5f), 0, 0)); + else if (pitchAngleSign < 0) + // Sets orientation to -90 degrees on X-axis. + camera->getParentSceneNode()->setOrientation(Ogre::Quaternion(Ogre::Math::Sqrt(0.5f), + -Ogre::Math::Sqrt(0.5f), 0, 0)); + } + } } diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index 9ce7a053f4..36291864e2 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -104,7 +104,7 @@ void OgreRenderer::createScene(const std::string camName, float fov, float nearC mCamera = mScene->createCamera(camName); mCamera->setNearClipDistance(nearClip); mCamera->setFOVy(Degree(fov)); - + // Create one viewport, entire window mView = mWindow->addViewport(mCamera); From d62fd96cd51ab6229a97a7f9bf426e306ef9b532 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 1 Sep 2011 21:48:36 +0200 Subject: [PATCH 154/269] Issue #168: Configuration cleanup, part 2 Updated configure() method. Signed-off-by: Lukasz Gromanowski --- ogre/renderer.cpp | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index 36291864e2..0cd75c6b6d 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -38,37 +38,25 @@ bool OgreRenderer::configure(bool showConfig, const std::string &pluginCfg, bool _logging) { - std::string theLogFile("Ogre.log"); - std::string theCfgFile("ogre.cfg"); + // Set up logging first + new LogManager; + Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); + logging = _logging; - theLogFile.insert(0, logPath); - theCfgFile.insert(0, cfgPath); - - // Set up logging first - new LogManager; - Log *log = LogManager::getSingleton().createLog(theLogFile); - logging = _logging; - - if(logging) + if(logging) // Full log detail log->setLogDetail(LL_BOREME); - else + else // Disable logging log->setDebugOutputEnabled(false); - mRoot = new Root(pluginCfg, theCfgFile, ""); + mRoot = new Root(pluginCfg, cfgPath, ""); - // Show the configuration dialog and initialise the system, if the - // showConfig parameter is specified. The settings are stored in - // ogre.cfg. If showConfig is false, the settings are assumed to - // already exist in ogre.cfg. - int result; - if(showConfig) - result = mRoot->showConfigDialog(); - else - result = mRoot->restoreConfig(); - - return !result; + // Show the configuration dialog and initialise the system, if the + // showConfig parameter is specified. The settings are stored in + // ogre.cfg. If showConfig is false, the settings are assumed to + // already exist in ogre.cfg. + return (showConfig) ? mRoot->showConfigDialog() : mRoot->restoreConfig(); } bool OgreRenderer::configure(bool showConfig, From 0b1d6d4330818ee3e491e1ec78928ff714e9bbaa Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 2 Sep 2011 19:55:10 +0200 Subject: [PATCH 155/269] Issue #168: Configuration cleanup, part 2 Corrected return code. Signed-off-by: Lukasz Gromanowski --- ogre/renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index 0cd75c6b6d..ca263d8152 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -56,7 +56,7 @@ bool OgreRenderer::configure(bool showConfig, // showConfig parameter is specified. The settings are stored in // ogre.cfg. If showConfig is false, the settings are assumed to // already exist in ogre.cfg. - return (showConfig) ? mRoot->showConfigDialog() : mRoot->restoreConfig(); + return (showConfig) ? !mRoot->showConfigDialog() : !mRoot->restoreConfig(); } bool OgreRenderer::configure(bool showConfig, From 26b9d0fdc374fac648f6293e1d4a4abdc2c20c19 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 27 Sep 2011 09:44:42 +0200 Subject: [PATCH 156/269] config fix --- ogre/renderer.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index ca263d8152..137b4dd213 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -38,25 +38,31 @@ bool OgreRenderer::configure(bool showConfig, const std::string &pluginCfg, bool _logging) { - // Set up logging first - new LogManager; - Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); - logging = _logging; + // Set up logging first + new LogManager; + Log *log = LogManager::getSingleton().createLog(logPath); + logging = _logging; - if(logging) + if(logging) // Full log detail log->setLogDetail(LL_BOREME); - else + else // Disable logging log->setDebugOutputEnabled(false); - mRoot = new Root(pluginCfg, cfgPath, ""); + mRoot = new Root(pluginCfg, cfgPath, ""); - // Show the configuration dialog and initialise the system, if the - // showConfig parameter is specified. The settings are stored in - // ogre.cfg. If showConfig is false, the settings are assumed to - // already exist in ogre.cfg. - return (showConfig) ? !mRoot->showConfigDialog() : !mRoot->restoreConfig(); + // Show the configuration dialog and initialise the system, if the + // showConfig parameter is specified. The settings are stored in + // ogre.cfg. If showConfig is false, the settings are assumed to + // already exist in ogre.cfg. + int result; + if(showConfig) + result = mRoot->showConfigDialog(); + else + result = mRoot->restoreConfig(); + + return !result; } bool OgreRenderer::configure(bool showConfig, From 51f859651dab6f0beeaba6444820c72d8beaef1c Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Wed, 9 Nov 2011 23:15:06 +0000 Subject: [PATCH 157/269] Exceptions from input handlers are now caught in OpenEngine as opposed to requring a try/catch in the input handler itself --- input/dispatcher.hpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/input/dispatcher.hpp b/input/dispatcher.hpp index dd6fb6bf6d..2cd1ed5ae2 100644 --- a/input/dispatcher.hpp +++ b/input/dispatcher.hpp @@ -47,7 +47,23 @@ struct Dispatcher : Mangle::Input::Event const _O &list = map.getList(index); _O::const_iterator it; for(it = list.begin(); it != list.end(); it++) - funcs.call(*it, p); + { + //catch exceptions thrown in the input handlers so that pressing a key + //doesn't cause OpenMw to crash + try + { + funcs.call(*it, p); + } + catch(const std::exception& e) + { + std::cerr << "Exception in input handler: " << e.what() << std::endl; + } + catch(...) + { + std::cerr << "Unknown exception in input handler" << std::endl; + } + + } } }; From 2f5eca9d878526bdd9dce93ece7f42093b481545 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 10 Nov 2011 08:36:38 +0100 Subject: [PATCH 158/269] include fix --- input/dispatcher.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/input/dispatcher.hpp b/input/dispatcher.hpp index 2cd1ed5ae2..a8d480d4b0 100644 --- a/input/dispatcher.hpp +++ b/input/dispatcher.hpp @@ -1,10 +1,13 @@ #ifndef OENGINE_INPUT_DISPATCHER_H #define OENGINE_INPUT_DISPATCHER_H +#include +#include + +#include + #include "dispatch_map.hpp" #include "func_binder.hpp" -#include -#include namespace OEngine { namespace Input { From 37e272b3b5e18d1a93a1d69804a96845f8762ba1 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Fri, 13 Jan 2012 20:41:38 +0100 Subject: [PATCH 159/269] Issue #178 - workaround for compilation problems with ogre 1.8.0. --- components/bsa/bsa_archive.cpp | 6 ++++++ extern/caelum/src/CaelumPlugin.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 2178be318e..87b46b34c4 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -221,6 +221,12 @@ class BSAArchive : public Archive { BSAFile arc; + FileInfoListPtr findFileInfo(const String&, bool, bool) const + { + static FileInfoListPtr filp(new FileInfoList()); + return filp; + } + public: BSAArchive(const String& name) : Archive(name, "BSA") diff --git a/extern/caelum/src/CaelumPlugin.cpp b/extern/caelum/src/CaelumPlugin.cpp index 288ad9220a..340a26559a 100644 --- a/extern/caelum/src/CaelumPlugin.cpp +++ b/extern/caelum/src/CaelumPlugin.cpp @@ -21,17 +21,17 @@ along with Caelum. If not, see . #include "CaelumPrecompiled.h" #include "CaelumPlugin.h" -template<> Caelum::CaelumPlugin* Ogre::Singleton::ms_Singleton = 0; +template<> Caelum::CaelumPlugin* Ogre::Singleton::msSingleton = 0; namespace Caelum { CaelumPlugin* CaelumPlugin::getSingletonPtr () { - return ms_Singleton; + return msSingleton; } CaelumPlugin& CaelumPlugin::getSingleton () { - assert (ms_Singleton); - return *ms_Singleton; + assert (msSingleton); + return *msSingleton; } extern "C" void CAELUM_EXPORT dllStartPlugin () { From 96ed96d4dd432b3ee78c081575f888d0692c78c5 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Mon, 16 Jan 2012 23:09:25 +0100 Subject: [PATCH 160/269] Quick test at getting Morrowinds install path from the registry on windows --- components/files/path.hpp | 2 +- components/files/windowspath.cpp | 37 +++++++++++++++++++++++++++++++- components/files/windowspath.hpp | 7 ++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/components/files/path.hpp b/components/files/path.hpp index 0788cefb15..5a4cf337b6 100644 --- a/components/files/path.hpp +++ b/components/files/path.hpp @@ -30,7 +30,7 @@ #include namespace Files { typedef LinuxPath TargetPathType; } -#elif defined(__WIN32) || defined(__WINDOWS__) +#elif defined(__WIN32) || defined(__WINDOWS__) || defined(_WINDOWS) #include namespace Files { typedef WindowsPath TargetPathType; } diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index f42f149c14..5c87eba92c 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -5,7 +5,10 @@ #include #include -#include +#include +#include + +#pragma comment(lib, "Shlwapi.lib") namespace Files { @@ -67,6 +70,38 @@ boost::filesystem::path WindowsPath::getRuntimeDataPath() const return boost::filesystem::path("./data/"); } +boost::filesystem::path WindowsPath::getInstallPath() const +{ + boost::filesystem::path installPath(""); + + HKEY hKey; + + BOOL f64 = FALSE; + LPCTSTR regkey; + if (IsWow64Process(GetCurrentProcess(), &f64) && f64) + { + regkey = "SOFTWARE\\Wow6432Node\\Bethesda Softworks\\Morrowind"; + } + else + { + regkey = "SOFTWARE\\Bethesda Softworks\\Morrowind"; + } + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(regkey), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) + { + //Key existed, let's try to read the install dir + char* data = new char[4096]; + int len = 4096; + + if (RegQueryValueEx(hKey, TEXT("Installed Path"), NULL, NULL, (LPBYTE)data, (LPDWORD)&len) == ERROR_SUCCESS) + { + installPath = data; + } + } + + return installPath; +} + } /* namespace Files */ #endif /* defined(_WIN32) || defined(__WINDOWS__) */ diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 47dfc08d8e..4550fc05ff 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -81,6 +81,13 @@ struct WindowsPath * \return boost::filesystem::path */ boost::filesystem::path getRuntimeDataPath() const; + + /** + * \brief Gets the path of the installed Morrowind version if there is one. + * + * \return boost::filesystem::path + */ + boost::filesystem::path getInstallPath() const; }; } /* namespace Files */ From 8663177ad175d549ef9d08c26e9e740d10d2b73e Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Mon, 16 Jan 2012 23:21:13 +0100 Subject: [PATCH 161/269] Oops, forgot the delete... --- components/files/windowspath.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 5c87eba92c..d838772325 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -97,6 +97,8 @@ boost::filesystem::path WindowsPath::getInstallPath() const { installPath = data; } + + delete[] data; } return installPath; From b4174b64195ddf6d03ded66dd574d80879c30708 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Tue, 17 Jan 2012 09:02:45 +0100 Subject: [PATCH 162/269] Vector instead of new/delete --- components/files/windowspath.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index d838772325..4fa70980ef 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -90,15 +90,13 @@ boost::filesystem::path WindowsPath::getInstallPath() const if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(regkey), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) { //Key existed, let's try to read the install dir - char* data = new char[4096]; - int len = 4096; + std::vector buf(512); + int len = 512; - if (RegQueryValueEx(hKey, TEXT("Installed Path"), NULL, NULL, (LPBYTE)data, (LPDWORD)&len) == ERROR_SUCCESS) + if (RegQueryValueEx(hKey, TEXT("Installed Path"), NULL, NULL, (LPBYTE)&buf[0], (LPDWORD)&len) == ERROR_SUCCESS) { - installPath = data; + installPath = &buf[0]; } - - delete[] data; } return installPath; From 16c214a17af6c4506d89bad139a4d66e7423f180 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 17 Jan 2012 19:18:17 +0100 Subject: [PATCH 163/269] find InstalledPath in wine registry; mInstalledPath in Files::Path --- components/files/linuxpath.cpp | 70 ++++++++++++++++++++++++++++++++++ components/files/linuxpath.hpp | 8 ++++ components/files/path.hpp | 22 +++++++++++ 3 files changed, 100 insertions(+) diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index c485002fdb..b11f273057 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -154,6 +154,76 @@ boost::filesystem::path LinuxPath::getRuntimeDataPath() const return boost::filesystem::path("./data/"); } +boost::filesystem::path LinuxPath::getInstallPath() const +{ + char *homePath = getenv("HOME"); + if(!homePath) + { + return boost::filesystem::path(""); + } + + boost::filesystem::path wineDefaultRegistry(homePath); + wineDefaultRegistry /= ".wine/system.reg"; + + boost::filesystem::path wineDriveC(homePath); + wineDriveC /= ".wine/drive_c"; + + boost::filesystem::file_status fileStatus = boost::filesystem::status(wineDefaultRegistry); + boost::filesystem::file_status dirStatus = boost::filesystem::status(wineDriveC); + if(!boost::filesystem::is_regular_file(fileStatus) || !boost::filesystem::is_directory(dirStatus)) + { + return boost::filesystem::path(""); + } + + + boost::filesystem::ifstream file(wineDefaultRegistry); + bool isRegEntry = false; + std::string line; + int startPos, pos; + + while (std::getline(file, line)) + { + if(line.length() > 0 && line[0] == '[') // we found an entry + { + std::string regkey = line.substr(1, line.find(']')-1); + if( regkey.compare("SOFTWARE\\\\Wow6432Node\\\\Bethesda Softworks\\\\Morrowind") == 0 + || regkey.compare("SOFTWARE\\\\Bethesda Softworks\\\\Morrowind") == 0 ) + { + isRegEntry = true; + } + } + else if(isRegEntry) + { + if(line.length() == 0 || line[0] != '"') // empty line means new registry key + { + break; + } + std::string key = line.substr(1, line.find('"', 1)-1); + if(key.compare("Installed Path") == 0) { + startPos = line.find('=')+2; + std::string installPath = line.substr(startPos, line.find('"', startPos+1)-startPos); + installPath.replace(0, 2, wineDriveC.string()); + + pos = -1; + do + { + pos = static_cast(installPath.find("\\\\", pos+1)); + if(static_cast(pos) == std::string::npos) + { + break; + } + + installPath.replace(pos, 2, "/"); + } while(true); + + return boost::filesystem::path(installPath); + } + } + } + + return boost::filesystem::path(""); +} + } /* namespace Files */ diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index d6e717fc44..62cf93664f 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -26,6 +26,7 @@ #if defined(__linux__) #include +#include /** * \namespace Files @@ -81,6 +82,13 @@ struct LinuxPath * \return boost::filesystem::path */ boost::filesystem::path getRuntimeDataPath() const; + + /** + * \brief Gets the path of the installed Morrowind version if there is one. + * + * \return boost::filesystem::path + */ + boost::filesystem::path getInstallPath() const; }; } /* namespace Files */ diff --git a/components/files/path.hpp b/components/files/path.hpp index 5a4cf337b6..5139d9f78a 100644 --- a/components/files/path.hpp +++ b/components/files/path.hpp @@ -76,6 +76,7 @@ struct Path , mLocalDataPath(mPath.getLocalDataPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) , mRuntimeDataPath(mPath.getRuntimeDataPath()) + , mInstalledPath(mPath.getInstallPath()) { if (!application_name.empty()) { @@ -209,6 +210,26 @@ struct Path mRuntimeDataPath = path; } + /** + * \brief Return path pointing to the directory where application was started. + * + * \return boost::filesystem::path + */ + const boost::filesystem::path& getInstalledPath() const + { + return mInstalledPath; + } + + /** + * \brief Sets new runtime data directory. + * + * \param [in] path - New path + */ + void setInstalledPath(const boost::filesystem::path& path) + { + mInstalledPath = path; + } + private: PathType mPath; @@ -223,6 +244,7 @@ struct Path boost::filesystem::path mRuntimeDataPath; /**< Runtime path to the configuration files. By default it is a 'data' directory in same directory where application was run */ + boost::filesystem::path mInstalledPath; /**< Runtime path to the configuration files. */ }; From 62eaaab69d607c11ae7eb645fe5df56243505383 Mon Sep 17 00:00:00 2001 From: Sebastian Wick Date: Tue, 17 Jan 2012 23:19:17 +0100 Subject: [PATCH 164/269] move include from .hpp to .cpp; line.empty() instead of line.size() > 0; change type of startPos and pos and move to other scope --- components/files/linuxpath.cpp | 12 +++++++----- components/files/linuxpath.hpp | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index b11f273057..5ca0856f1c 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -28,6 +28,7 @@ #include #include #include +#include /** * \namespace Files @@ -179,11 +180,10 @@ boost::filesystem::path LinuxPath::getInstallPath() const boost::filesystem::ifstream file(wineDefaultRegistry); bool isRegEntry = false; std::string line; - int startPos, pos; while (std::getline(file, line)) { - if(line.length() > 0 && line[0] == '[') // we found an entry + if(!line.empty() && line[0] == '[') // we found an entry { std::string regkey = line.substr(1, line.find(']')-1); if( regkey.compare("SOFTWARE\\\\Wow6432Node\\\\Bethesda Softworks\\\\Morrowind") == 0 @@ -194,12 +194,14 @@ boost::filesystem::path LinuxPath::getInstallPath() const } else if(isRegEntry) { - if(line.length() == 0 || line[0] != '"') // empty line means new registry key + if(line.empty() || line[0] != '"') // empty line means new registry key { break; } std::string key = line.substr(1, line.find('"', 1)-1); if(key.compare("Installed Path") == 0) { + std::string::size_type pos, startPos; + startPos = line.find('=')+2; std::string installPath = line.substr(startPos, line.find('"', startPos+1)-startPos); installPath.replace(0, 2, wineDriveC.string()); @@ -207,8 +209,8 @@ boost::filesystem::path LinuxPath::getInstallPath() const pos = -1; do { - pos = static_cast(installPath.find("\\\\", pos+1)); - if(static_cast(pos) == std::string::npos) + pos = installPath.find("\\\\", pos+1); + if(pos == std::string::npos) { break; } diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 62cf93664f..62cc14fff4 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -26,7 +26,6 @@ #if defined(__linux__) #include -#include /** * \namespace Files From 463acb2f75b339d075d08ed9285eaba3ab97044d Mon Sep 17 00:00:00 2001 From: Dmitry Marakasov Date: Wed, 28 Sep 2011 03:40:27 +0400 Subject: [PATCH 165/269] Use linuxpath for FreeBSD as well --- components/files/linuxpath.cpp | 4 ++-- components/files/linuxpath.hpp | 4 ++-- components/files/path.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index c485002fdb..27581a1e2f 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -22,7 +22,7 @@ #include "linuxpath.hpp" -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) #include #include @@ -157,4 +157,4 @@ boost::filesystem::path LinuxPath::getRuntimeDataPath() const } /* namespace Files */ -#endif /* defined(__linux__) */ +#endif /* defined(__linux__) || defined(__FreeBSD__) */ diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index d6e717fc44..53f7a73b43 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -23,7 +23,7 @@ #ifndef COMPONENTS_FILES_LINUXPATH_H #define COMPONENTS_FILES_LINUXPATH_H -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) #include @@ -85,6 +85,6 @@ struct LinuxPath } /* namespace Files */ -#endif /* defined(__linux__) */ +#endif /* defined(__linux__) || defined(__FreeBSD__) */ #endif /* COMPONENTS_FILES_LINUXPATH_H */ diff --git a/components/files/path.hpp b/components/files/path.hpp index 0788cefb15..78de9c585b 100644 --- a/components/files/path.hpp +++ b/components/files/path.hpp @@ -26,7 +26,7 @@ #include #include -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) #include namespace Files { typedef LinuxPath TargetPathType; } From 6b3242f5141b68b0a2c814fcb47adda814c52dfd Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 21 Jan 2012 01:09:06 +0100 Subject: [PATCH 166/269] Updated .gitignore file Updated .gitignore file - added ignore rules for: *.a, *.o, cmake_install.cmake, moc_*.cxx files. Signed-off-by: Lukasz Gromanowski --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ada874bb22..e57bcfc62f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ Docs/mainpage.hpp CMakeFiles */CMakeFiles CMakeCache.txt +moc_*.cxx +cmake_install.cmake +*.[ao] + From 7c24ae9ac7f48aa4b1ba3a17db2e9b6cd57b7d41 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 21 Jan 2012 01:14:35 +0100 Subject: [PATCH 167/269] Issue #168 - Configuration cleanup - WIP This is "work in progress" commit, it shall not be merged alone, without succeeding commits (it's not fully functional). Signed-off-by: Lukasz Gromanowski --- apps/launcher/datafilespage.cpp | 12 +- apps/launcher/datafilespage.hpp | 6 +- apps/launcher/graphicspage.cpp | 13 +- apps/launcher/graphicspage.hpp | 7 +- apps/launcher/maindialog.cpp | 10 +- apps/launcher/maindialog.hpp | 4 +- apps/openmw/engine.cpp | 6 +- apps/openmw/engine.hpp | 10 +- apps/openmw/main.cpp | 10 +- components/CMakeLists.txt | 6 +- components/cfg/configurationmanager.cpp | 157 ---------------- components/cfg/configurationmanager.hpp | 62 ------- components/files/configurationmanager.cpp | 177 +++++++++++++++++++ components/files/configurationmanager.hpp | 65 +++++++ components/files/{path.hpp => fixedpath.hpp} | 78 +++----- components/files/linuxpath.cpp | 82 ++------- components/files/linuxpath.hpp | 30 +--- components/files/macospath.cpp | 62 +------ components/files/macospath.hpp | 28 +-- components/files/windowspath.cpp | 37 ++-- components/files/windowspath.hpp | 30 +--- 21 files changed, 354 insertions(+), 538 deletions(-) delete mode 100644 components/cfg/configurationmanager.cpp delete mode 100644 components/cfg/configurationmanager.hpp create mode 100644 components/files/configurationmanager.cpp create mode 100644 components/files/configurationmanager.hpp rename components/files/{path.hpp => fixedpath.hpp} (68%) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index c8311846f5..8b59f1b819 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "datafilespage.hpp" #include "lineedit.hpp" @@ -26,7 +26,9 @@ bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2) return index1.row() <= index2.row(); } -DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) +DataFilesPage::DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent) + : QWidget(parent) + , mCfgMgr(cfg) { mDataFilesModel = new QStandardItemModel(); // Contains all plugins with masters mPluginsModel = new PluginsModel(); // Contains selectable plugins @@ -236,13 +238,11 @@ void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) void DataFilesPage::setupConfig() { - Cfg::ConfigurationManager cfg; - - QString config = (cfg.getRuntimeConfigPath() / "launcher.cfg").string().c_str(); + QString config = (mCfgMgr.getLocalPath() / "launcher.cfg").string().c_str(); QFile file(config); if (!file.exists()) { - config = QString::fromStdString((cfg.getLocalConfigPath() / "launcher.cfg").string()); + config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string()); } file.setFileName(config); // Just for displaying information diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 2d0a385a79..db1068abdc 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -19,12 +19,14 @@ class PluginsModel; class PluginsView; class ComboBox; +namespace Files { struct ConfigurationManager; } + class DataFilesPage : public QWidget { Q_OBJECT public: - DataFilesPage(QWidget *parent = 0); + DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent = 0); ComboBox *mProfilesComboBox; QSettings *mLauncherConfig; @@ -81,6 +83,8 @@ private: QAction *mCheckAction; QAction *mUncheckAction; + Files::ConfigurationManager& mCfgMgr; + void addPlugins(const QModelIndex &index); void removePlugins(const QModelIndex &index); void uncheckPlugins(); diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 92fbf3350b..d41a333563 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -1,8 +1,11 @@ #include #include "graphicspage.hpp" +#include -GraphicsPage::GraphicsPage(QWidget *parent) : QWidget(parent) +GraphicsPage::GraphicsPage(Files::ConfigurationManager& cfg, QWidget *parent) + : QWidget(parent) + , mCfgMgr(cfg) { QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this); @@ -147,21 +150,21 @@ void GraphicsPage::createPages() void GraphicsPage::setupConfig() { - QString ogreCfg = mCfg.getOgreConfigPath().string().c_str(); + QString ogreCfg = mCfgMgr.getOgreConfigPath().string().c_str(); QFile file(ogreCfg); mOgreConfig = new QSettings(ogreCfg, QSettings::IniFormat); } void GraphicsPage::setupOgre() { - QString pluginCfg = mCfg.getPluginsConfigPath().string().c_str(); + QString pluginCfg = mCfgMgr.getPluginsConfigPath().string().c_str(); QFile file(pluginCfg); // Create a log manager so we can surpress debug text to stdout/stderr Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager; - logMgr->createLog((mCfg.getLogPath().string() + "/launcherOgre.log"), true, false, false); + logMgr->createLog((mCfgMgr.getLogPath().string() + "/launcherOgre.log"), true, false, false); - QString ogreCfg = QString::fromStdString(mCfg.getOgreConfigPath().string()); + QString ogreCfg = QString::fromStdString(mCfgMgr.getOgreConfigPath().string()); file.setFileName(ogreCfg); //we need to check that the path to the configuration file exists before we diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 5d50cfc613..ffd7a41b8c 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -7,19 +7,20 @@ #include #include #include -#include class QComboBox; class QCheckBox; class QStackedWidget; class QSettings; +namespace Files { struct ConfigurationManager; } + class GraphicsPage : public QWidget { Q_OBJECT public: - GraphicsPage(QWidget *parent = 0); + GraphicsPage(Files::ConfigurationManager& cfg, QWidget *parent = 0); QSettings *mOgreConfig; @@ -29,7 +30,7 @@ public slots: void rendererChanged(const QString &renderer); private: - Cfg::ConfigurationManager mCfg; + Files::ConfigurationManager& mCfgMgr; Ogre::Root *mOgre; Ogre::RenderSystem *mSelectedRenderSystem; Ogre::RenderSystem *mOpenGLRenderSystem; diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 4ec8b309cc..3bef0b6f9b 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -152,20 +152,20 @@ QStringList MainDialog::readConfig(const QString &fileName) void MainDialog::createPages() { mPlayPage = new PlayPage(this); - mGraphicsPage = new GraphicsPage(this); - mDataFilesPage = new DataFilesPage(this); + mGraphicsPage = new GraphicsPage(mCfgMgr, this); + mDataFilesPage = new DataFilesPage(mCfgMgr, this); // Retrieve all data entries from the configs QStringList dataDirs; // Global location - QFile file(QString::fromStdString((mCfg.getGlobalConfigPath()/"openmw.cfg").string())); + QFile file(QString::fromStdString((mCfgMgr.getGlobalPath()/"openmw.cfg").string())); if (file.exists()) { dataDirs = readConfig(file.fileName()); } // User location - file.setFileName(QString::fromStdString((mCfg.getLocalConfigPath()/"openmw.cfg").string())); + file.setFileName(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); if (file.exists()) { dataDirs = readConfig(file.fileName()); } @@ -328,7 +328,7 @@ void MainDialog::writeConfig() dataFiles.append(mDataFilesPage->checkedPlugins()); // Open the config as a QFile - QFile file(QString::fromStdString((mCfg.getLocalConfigPath()/"openmw.cfg").string())); + QFile file(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { // File cannot be opened or created diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 047050902a..718fde4f7f 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -3,7 +3,7 @@ #include -#include +#include class QListWidget; class QListWidgetItem; @@ -47,7 +47,7 @@ private: QStringList mDataDirs; bool mStrict; - Cfg::ConfigurationManager mCfg; + Files::ConfigurationManager mCfgMgr; }; #endif diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 7fa98f8e2c..bbc68b8e23 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -17,7 +17,9 @@ #include #include #include -#include +#include +#include + #include #include "mwinput/inputmanager.hpp" @@ -154,7 +156,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) return true; } -OMW::Engine::Engine(Cfg::ConfigurationManager& configurationManager) +OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) : mOgre (0) , mPhysicEngine (0) , mShowFPS (false) diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 443f790a47..97079f5a5e 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -11,7 +11,6 @@ #include #include -#include #include "mwworld/environment.hpp" #include "mwworld/ptr.hpp" @@ -54,6 +53,11 @@ namespace OEngine } } +namespace Files +{ + struct ConfigurationManager; +} + namespace OMW { /// \brief Main engine class, that brings together all the components of OpenMW @@ -104,7 +108,7 @@ namespace OMW virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt); public: - Engine(Cfg::ConfigurationManager& configurationManager); + Engine(Files::ConfigurationManager& configurationManager); virtual ~Engine(); /// Enable strict filesystem mode (do not fold case) @@ -159,7 +163,7 @@ namespace OMW void setEncoding(const std::string& encoding); private: - Cfg::ConfigurationManager& mCfgMgr; + Files::ConfigurationManager& mCfgMgr; }; } diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 933d1c48aa..0f66925d38 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -6,9 +6,9 @@ #include #include -#include +#include #include -#include +#include #include "engine.hpp" @@ -46,7 +46,7 @@ using namespace std; * \retval true - Everything goes OK * \retval false - Error */ -bool parseOptions (int argc, char** argv, OMW::Engine& engine, Cfg::ConfigurationManager& cfgMgr) +bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::ConfigurationManager& cfgMgr) { // Create a local alias for brevity namespace bpo = boost::program_options; @@ -166,7 +166,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Cfg::Configuratio if (dataDirs.empty()) { - dataDirs.push_back(cfgMgr.getLocalDataPath()); + dataDirs.push_back(cfgMgr.getDataPath("local:data?")); } engine.setDataDirs(dataDirs); @@ -220,7 +220,7 @@ int main(int argc, char**argv) try { - Cfg::ConfigurationManager cfgMgr; + Files::ConfigurationManager cfgMgr; OMW::Engine engine(cfgMgr); if (parseOptions(argc, argv, engine, cfgMgr)) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 75b8aff8c7..3f7d6d49af 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -6,10 +6,6 @@ add_component_dir (bsa bsa_archive bsa_file ) -add_component_dir (cfg - configurationmanager - ) - add_component_dir (nif controlled effect nif_types record controller extra node record_ptr data nif_file property ) @@ -47,7 +43,7 @@ add_component_dir (misc ) add_component_dir (files - linuxpath windowspath macospath path multidircollection collections fileops + linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager ) add_component_dir (compiler diff --git a/components/cfg/configurationmanager.cpp b/components/cfg/configurationmanager.cpp deleted file mode 100644 index 0998debeeb..0000000000 --- a/components/cfg/configurationmanager.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "configurationmanager.hpp" - -#include -#include -#include - -namespace Cfg -{ - -static const char* const openmwCfgFile = "openmw.cfg"; -static const char* const ogreCfgFile = "ogre.cfg"; -static const char* const pluginsCfgFile = "plugins.cfg"; - - -ConfigurationManager::ConfigurationManager() - : mPath("openmw") -{ - /** - * According to task #168 plugins.cfg file shall be located in global - * configuration path or in runtime configuration path. - */ - mPluginsCfgPath = mPath.getGlobalConfigPath() / pluginsCfgFile; - if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) - { - mPluginsCfgPath = mPath.getRuntimeConfigPath() / pluginsCfgFile; - if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) - { - std::cerr << "Failed to find " << pluginsCfgFile << " file!" << std::endl; - mPluginsCfgPath.clear(); - } - } - - /** - * According to task #168 ogre.cfg file shall be located only - * in user configuration path. - */ - mOgreCfgPath = mPath.getLocalConfigPath() / ogreCfgFile; - - mLogPath = mPath.getLocalConfigPath(); -} - -ConfigurationManager::~ConfigurationManager() -{ -} - -void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, - boost::program_options::options_description& description) -{ - loadConfig(mPath.getLocalConfigPath(), variables, description); - boost::program_options::notify(variables); - loadConfig(mPath.getRuntimeConfigPath(), variables, description); - boost::program_options::notify(variables); - loadConfig(mPath.getGlobalConfigPath(), variables, description); - boost::program_options::notify(variables); -} - -void ConfigurationManager::loadConfig(const boost::filesystem::path& path, - boost::program_options::variables_map& variables, - boost::program_options::options_description& description) -{ - boost::filesystem::path cfgFile(path); - cfgFile /= std::string(openmwCfgFile); - if (boost::filesystem::is_regular_file(cfgFile)) - { - std::cout << "Loading config file: " << cfgFile.string() << "... "; - - std::ifstream configFileStream(cfgFile.string().c_str()); - if (configFileStream.is_open()) - { - boost::program_options::store(boost::program_options::parse_config_file( - configFileStream, description), variables); - - std::cout << "done." << std::endl; - } - else - { - std::cout << "failed." << std::endl; - } - } -} - -const boost::filesystem::path& ConfigurationManager::getGlobalConfigPath() const -{ - return mPath.getGlobalConfigPath(); -} - -void ConfigurationManager::setGlobalConfigPath(const boost::filesystem::path& newPath) -{ - mPath.setGlobalConfigPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getLocalConfigPath() const -{ - return mPath.getLocalConfigPath(); -} - -void ConfigurationManager::setLocalConfigPath(const boost::filesystem::path& newPath) -{ - mPath.setLocalConfigPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getRuntimeConfigPath() const -{ - return mPath.getRuntimeConfigPath(); -} - -void ConfigurationManager::setRuntimeConfigPath(const boost::filesystem::path& newPath) -{ - mPath.setRuntimeConfigPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const -{ - return mPath.getGlobalDataPath(); -} - -void ConfigurationManager::setGlobalDataPath(const boost::filesystem::path& newPath) -{ - mPath.setGlobalDataPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getLocalDataPath() const -{ - return mPath.getLocalDataPath(); -} - -void ConfigurationManager::setLocalDataPath(const boost::filesystem::path& newPath) -{ - mPath.setLocalDataPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getRuntimeDataPath() const -{ - return mPath.getRuntimeDataPath(); -} - -void ConfigurationManager::setRuntimeDataPath(const boost::filesystem::path& newPath) -{ - mPath.setRuntimeDataPath(newPath); -} - -const boost::filesystem::path& ConfigurationManager::getOgreConfigPath() const -{ - return mOgreCfgPath; -} - -const boost::filesystem::path& ConfigurationManager::getPluginsConfigPath() const -{ - return mPluginsCfgPath; -} - -const boost::filesystem::path& ConfigurationManager::getLogPath() const -{ - return mLogPath; -} - -} /* namespace Cfg */ diff --git a/components/cfg/configurationmanager.hpp b/components/cfg/configurationmanager.hpp deleted file mode 100644 index 7f13d0914c..0000000000 --- a/components/cfg/configurationmanager.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef COMPONENTS_CFG_CONFIGURATIONMANAGER_HPP -#define COMPONENTS_CFG_CONFIGURATIONMANAGER_HPP - -#include -#include - -#include - -/** - * \namespace Cfg - */ -namespace Cfg -{ - -/** - * \struct ConfigurationManager - */ -struct ConfigurationManager -{ - ConfigurationManager(); - virtual ~ConfigurationManager(); - - void readConfiguration(boost::program_options::variables_map& variables, - boost::program_options::options_description& description); - - const boost::filesystem::path& getGlobalConfigPath() const; - void setGlobalConfigPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getLocalConfigPath() const; - void setLocalConfigPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getRuntimeConfigPath() const; - void setRuntimeConfigPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getGlobalDataPath() const; - void setGlobalDataPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getLocalDataPath() const; - void setLocalDataPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getRuntimeDataPath() const; - void setRuntimeDataPath(const boost::filesystem::path& newPath); - - const boost::filesystem::path& getOgreConfigPath() const; - const boost::filesystem::path& getPluginsConfigPath() const; - const boost::filesystem::path& getLogPath() const; - - private: - void loadConfig(const boost::filesystem::path& path, - boost::program_options::variables_map& variables, - boost::program_options::options_description& description); - - Files::Path<> mPath; - - boost::filesystem::path mOgreCfgPath; - boost::filesystem::path mPluginsCfgPath; - boost::filesystem::path mLogPath; -}; - -} /* namespace Cfg */ - -#endif /* COMPONENTS_CFG_CONFIGURATIONMANAGER_HPP */ diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp new file mode 100644 index 0000000000..224fd26492 --- /dev/null +++ b/components/files/configurationmanager.cpp @@ -0,0 +1,177 @@ +#include "configurationmanager.hpp" + +#include +#include +#include +#include + +namespace Files +{ + +static const char* const openmwCfgFile = "openmw.cfg"; +static const char* const ogreCfgFile = "ogre.cfg"; +static const char* const pluginsCfgFile = "plugins.cfg"; + +static const char* const mwDataToken = "?mw:data?"; +static const char* const localDataToken = "?local:data?"; +static const char* const userDataToken = "?user:data?"; +static const char* const globalDataToken = "?global:data?"; + +ConfigurationManager::ConfigurationManager() + : mFixedPath("openmw") +{ + setupTokensMapping(); + + /** + * According to task #168 plugins.cfg file shall be located in global + * configuration path or in runtime configuration path. + */ + mPluginsCfgPath = mFixedPath.getGlobalPath() / pluginsCfgFile; + if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) + { + mPluginsCfgPath = mFixedPath.getLocalPath() / pluginsCfgFile; + if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) + { + std::cerr << "Failed to find " << pluginsCfgFile << " file!" << std::endl; + mPluginsCfgPath.clear(); + } + } + + /** + * According to task #168 ogre.cfg file shall be located only + * in user configuration path. + */ + mOgreCfgPath = mFixedPath.getUserPath() / ogreCfgFile; + + /** + * FIXME: Logs shoudn't be stored in the same dir where configuration is placed. + */ + mLogPath = mFixedPath.getUserPath(); +} + +ConfigurationManager::~ConfigurationManager() +{ +} + +void ConfigurationManager::setupTokensMapping() +{ + mTokensMapping.insert(std::make_pair(mwDataToken, &ConfigurationManager::getInstallPath)); + mTokensMapping.insert(std::make_pair(localDataToken, &ConfigurationManager::getLocalDataPath)); + mTokensMapping.insert(std::make_pair(userDataToken, &ConfigurationManager::getUserDataPath)); + mTokensMapping.insert(std::make_pair(globalDataToken, &ConfigurationManager::getGlobalDataPath)); +} + +void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, + boost::program_options::options_description& description) +{ + loadConfig(mFixedPath.getUserPath(), variables, description); + boost::program_options::notify(variables); + loadConfig(mFixedPath.getLocalPath(), variables, description); + boost::program_options::notify(variables); + loadConfig(mFixedPath.getGlobalPath(), variables, description); + boost::program_options::notify(variables); +} + +void ConfigurationManager::loadConfig(const boost::filesystem::path& path, + boost::program_options::variables_map& variables, + boost::program_options::options_description& description) +{ + boost::filesystem::path cfgFile(path); + cfgFile /= std::string(openmwCfgFile); + if (boost::filesystem::is_regular_file(cfgFile)) + { + std::cout << "Loading config file: " << cfgFile.string() << "... "; + + std::ifstream configFileStream(cfgFile.string().c_str()); + if (configFileStream.is_open()) + { + boost::program_options::store(boost::program_options::parse_config_file( + configFileStream, description), variables); + + std::cout << "done." << std::endl; + } + else + { + std::cout << "failed." << std::endl; + } + } +} + +const boost::filesystem::path& ConfigurationManager::getGlobalPath() const +{ + return mFixedPath.getGlobalPath(); +} + +const boost::filesystem::path& ConfigurationManager::getUserPath() const +{ + return mFixedPath.getUserPath(); +} + +const boost::filesystem::path& ConfigurationManager::getLocalPath() const +{ + return mFixedPath.getLocalPath(); +} + +const boost::filesystem::path& ConfigurationManager::getDataPath(const std::string& type) const +{ + TokensMappingContainer::const_iterator it = mTokensMapping.find(type); + if (it != mTokensMapping.end()) + { + return ((this)->*(it->second))(); + } + + return mFixedPath.getLocalDataPath(); +} + +const boost::filesystem::path& ConfigurationManager::getInstallPath() const +{ +// TODO: It will be corrected later. + static boost::filesystem::path p("./"); + return p; + + //return mFixedPath.getInstallPath(); +} + +const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const +{ +// TODO: It will be corrected later. + static boost::filesystem::path p("./"); + return p; + + //return mFixedPath.getGlobalDataPath(); +} + +const boost::filesystem::path& ConfigurationManager::getUserDataPath() const +{ +// TODO: It will be corrected later. + static boost::filesystem::path p("./"); + return p; + + //return mFixedPath.getUserDataPath(); +} + +const boost::filesystem::path& ConfigurationManager::getLocalDataPath() const +{ +// TODO: It will be corrected later. + static boost::filesystem::path p("./"); + return p; + //return mFixedPath.getLocalDataPath(); +} + + +const boost::filesystem::path& ConfigurationManager::getOgreConfigPath() const +{ + return mOgreCfgPath; +} + +const boost::filesystem::path& ConfigurationManager::getPluginsConfigPath() const +{ + return mPluginsCfgPath; +} + +const boost::filesystem::path& ConfigurationManager::getLogPath() const +{ + return mLogPath; +} + +} /* namespace Cfg */ diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp new file mode 100644 index 0000000000..3004b62819 --- /dev/null +++ b/components/files/configurationmanager.hpp @@ -0,0 +1,65 @@ +#ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP +#define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP + +#include + +#include +#include + +#include + +/** + * \namespace Files + */ +namespace Files +{ + +/** + * \struct ConfigurationManager + */ +struct ConfigurationManager +{ + ConfigurationManager(); + virtual ~ConfigurationManager(); + + void readConfiguration(boost::program_options::variables_map& variables, + boost::program_options::options_description& description); + + /**< Fixed paths */ + const boost::filesystem::path& getGlobalPath() const; + const boost::filesystem::path& getUserPath() const; + const boost::filesystem::path& getLocalPath() const ; + + const boost::filesystem::path& getDataPath(const std::string& type) const; + + const boost::filesystem::path& getOgreConfigPath() const; + const boost::filesystem::path& getPluginsConfigPath() const; + const boost::filesystem::path& getLogPath() const; + + private: + typedef const boost::filesystem::path& (ConfigurationManager::*path_type_f)() const; + typedef std::tr1::unordered_map TokensMappingContainer; + + void loadConfig(const boost::filesystem::path& path, + boost::program_options::variables_map& variables, + boost::program_options::options_description& description); + + void setupTokensMapping(); + + const boost::filesystem::path& getInstallPath() const; + const boost::filesystem::path& getGlobalDataPath() const; + const boost::filesystem::path& getUserDataPath() const; + const boost::filesystem::path& getLocalDataPath() const; + + Files::FixedPath<> mFixedPath; + + boost::filesystem::path mOgreCfgPath; + boost::filesystem::path mPluginsCfgPath; + boost::filesystem::path mLogPath; + + TokensMappingContainer mTokensMapping; +}; + +} /* namespace Cfg */ + +#endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */ diff --git a/components/files/path.hpp b/components/files/fixedpath.hpp similarity index 68% rename from components/files/path.hpp rename to components/files/fixedpath.hpp index 78de9c585b..4e2b20e3cd 100644 --- a/components/files/path.hpp +++ b/components/files/fixedpath.hpp @@ -18,10 +18,10 @@ * along with this program. If not, see . */ -/** \file components/files/path.hpp */ +/** \file components/files/fixedpath.hpp */ -#ifndef COMPONENTS_FILES_PATH_HPP -#define COMPONENTS_FILES_PATH_HPP +#ifndef COMPONENTS_FILES_FIXEDPATH_HPP +#define COMPONENTS_FILES_FIXEDPATH_HPP #include #include @@ -59,7 +59,7 @@ template < class P = TargetPathType > -struct Path +struct FixedPath { typedef P PathType; @@ -68,21 +68,21 @@ struct Path * * \param [in] application_name - Name of the application */ - Path(const std::string& application_name) + FixedPath(const std::string& application_name) : mPath() - , mLocalConfigPath(mPath.getLocalConfigPath()) - , mGlobalConfigPath(mPath.getGlobalConfigPath()) - , mRuntimeConfigPath(mPath.getRuntimeConfigPath()) - , mLocalDataPath(mPath.getLocalDataPath()) - , mGlobalDataPath(mPath.getGlobalDataPath()) - , mRuntimeDataPath(mPath.getRuntimeDataPath()) + , mUserPath(mPath.getUserPath()) + , mGlobalPath(mPath.getGlobalPath()) + , mLocalPath(mPath.getLocalPath()) + , mLocalDataPath() + , mGlobalDataPath() + , mRuntimeDataPath() { if (!application_name.empty()) { boost::filesystem::path suffix(application_name + std::string("/")); - mLocalConfigPath /= suffix; - mGlobalConfigPath /= suffix; + mUserPath /= suffix; + mGlobalPath /= suffix; mLocalDataPath /= suffix; mGlobalDataPath /= suffix; @@ -94,19 +94,9 @@ struct Path * * \return boost::filesystem::path */ - const boost::filesystem::path& getLocalConfigPath() const + const boost::filesystem::path& getUserPath() const { - return mLocalConfigPath; - } - - /** - * \brief Sets new local configuration path. - * - * \param [in] path - New path - */ - void setLocalConfigPath(const boost::filesystem::path& path) - { - mLocalConfigPath = path; + return mUserPath; } /** @@ -114,19 +104,9 @@ struct Path * * \return boost::filesystem::path */ - const boost::filesystem::path& getGlobalConfigPath() const + const boost::filesystem::path& getGlobalPath() const { - return mGlobalConfigPath; - } - - /** - * \brief Sets new global configuration path. - * - * \param [in] path - New path - */ - void setGlobalConfigPath(const boost::filesystem::path& path) - { - mGlobalConfigPath = path; + return mGlobalPath; } /** @@ -134,19 +114,9 @@ struct Path * * \return boost::filesystem::path */ - const boost::filesystem::path& getRuntimeConfigPath() const + const boost::filesystem::path& getLocalPath() const { - return mRuntimeConfigPath; - } - - /** - * \brief Sets new runtime configuration path. - * - * \param [in] path - New path - */ - void setRuntimeConfigPath(const boost::filesystem::path& path) - { - mRuntimeConfigPath = path; + return mLocalPath; } /** @@ -212,11 +182,9 @@ struct Path private: PathType mPath; - boost::filesystem::path mLocalConfigPath; /**< User local path to the configuration files */ - boost::filesystem::path mGlobalConfigPath; /**< Global path to the configuration files */ - boost::filesystem::path mRuntimeConfigPath; /**< Runtime path to the configuration files. - By default it is the same directory where - application was run */ + boost::filesystem::path mUserPath; /**< User path */ + boost::filesystem::path mGlobalPath; /**< Global path */ + boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ boost::filesystem::path mLocalDataPath; /**< User local application data path (user plugins / mods / etc.) */ boost::filesystem::path mGlobalDataPath; /**< Global application data path */ @@ -229,4 +197,4 @@ struct Path } /* namespace Files */ -#endif /* COMPONENTS_FILES_PATH_HPP */ +#endif /* COMPONENTS_FILES_FIXEDPATH_HPP */ diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 27581a1e2f..11ddc31048 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -35,9 +35,9 @@ namespace Files { -boost::filesystem::path LinuxPath::getLocalConfigPath() const +boost::filesystem::path LinuxPath::getUserPath() const { - boost::filesystem::path localConfigPath("."); + boost::filesystem::path userPath("."); boost::filesystem::path suffix("/"); const char* theDir = getenv("OPENMW_CONFIG"); @@ -63,17 +63,17 @@ boost::filesystem::path LinuxPath::getLocalConfigPath() const } if (theDir != NULL) { - localConfigPath = boost::filesystem::path(theDir); + userPath = boost::filesystem::path(theDir); } - localConfigPath /= suffix; + userPath /= suffix; - return localConfigPath; + return userPath; } -boost::filesystem::path LinuxPath::getGlobalConfigPath() const +boost::filesystem::path LinuxPath::getGlobalPath() const { - boost::filesystem::path globalConfigPath("/etc/xdg/"); + boost::filesystem::path globalPath("/etc/xdg/"); char* theDir = getenv("XDG_CONFIG_DIRS"); if (theDir != NULL) @@ -82,79 +82,19 @@ boost::filesystem::path LinuxPath::getGlobalConfigPath() const char* ptr = strtok(theDir, ":"); if (ptr != NULL) { - globalConfigPath = boost::filesystem::path(ptr); - globalConfigPath /= boost::filesystem::path("/"); + globalPath = boost::filesystem::path(ptr); + globalPath /= boost::filesystem::path("/"); } } - return globalConfigPath; + return globalPath; } -boost::filesystem::path LinuxPath::getRuntimeConfigPath() const +boost::filesystem::path LinuxPath::getLocalPath() const { return boost::filesystem::path("./"); } -boost::filesystem::path LinuxPath::getLocalDataPath() const -{ - boost::filesystem::path localDataPath("."); - boost::filesystem::path suffix("/"); - - const char* theDir = getenv("OPENMW_DATA"); - if (theDir == NULL) - { - theDir = getenv("XDG_DATA_HOME"); - if (theDir == NULL) - { - theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - suffix = boost::filesystem::path("/.local/share/"); - } - } - } - - if (theDir != NULL) { - localDataPath = boost::filesystem::path(theDir); - } - - localDataPath /= suffix; - return localDataPath; -} - -boost::filesystem::path LinuxPath::getGlobalDataPath() const -{ - boost::filesystem::path globalDataPath("/usr/local/share/"); - - char* theDir = getenv("XDG_DATA_DIRS"); - if (theDir != NULL) - { - // We take only first path from list - char* ptr = strtok(theDir, ":"); - if (ptr != NULL) - { - globalDataPath = boost::filesystem::path(ptr); - globalDataPath /= boost::filesystem::path("/"); - } - } - - return globalDataPath; -} - -boost::filesystem::path LinuxPath::getRuntimeDataPath() const -{ - return boost::filesystem::path("./data/"); -} - - } /* namespace Files */ #endif /* defined(__linux__) || defined(__FreeBSD__) */ diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 53f7a73b43..af92326bed 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -39,18 +39,18 @@ namespace Files struct LinuxPath { /** - * \brief Return path to the local configuration directory. + * \brief Return path to the user directory. * * \return boost::filesystem::path */ - boost::filesystem::path getLocalConfigPath() const; + boost::filesystem::path getUserPath() const; /** * \brief Return path to the global (system) configuration directory. * * \return boost::filesystem::path */ - boost::filesystem::path getGlobalConfigPath() const; + boost::filesystem::path getGlobalPath() const; /** * \brief Return path to the runtime configuration directory which is the @@ -58,29 +58,7 @@ struct LinuxPath * * \return boost::filesystem::path */ - boost::filesystem::path getRuntimeConfigPath() const; - - /** - * \brief Return path to the local data directory. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getLocalDataPath() const; - - /** - * \brief Return path to the global (system) data directory. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getGlobalDataPath() const; - - /** - * \brief Return runtime data path which is a location where - * an application was started with 'data' suffix. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getRuntimeDataPath() const; + boost::filesystem::path getLocalPath() const; }; } /* namespace Files */ diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 46e775030f..1ffb443991 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -34,9 +34,9 @@ namespace Files { -boost::filesystem::path MacOsPath::getLocalConfigPath() const +boost::filesystem::path MacOsPath::getUserPath() const { - boost::filesystem::path localConfigPath("."); + boost::filesystem::path userPath("."); boost::filesystem::path suffix("/"); const char* theDir = getenv("HOME"); @@ -50,69 +50,25 @@ boost::filesystem::path MacOsPath::getLocalConfigPath() const } if (theDir != NULL) { - localConfigPath = boost::filesystem::path(theDir) / "Library/Preferences/"; + userPath = boost::filesystem::path(theDir) / "Library/Preferences/"; } - localConfigPath /= suffix; + userPath /= suffix; - return localConfigPath; + return userPath; } -boost::filesystem::path MacOsPath::getGlobalConfigPath() const +boost::filesystem::path MacOsPath::getGlobalPath() const { - boost::filesystem::path globalConfigPath("/Library/Preferences/"); - return globalConfigPath; + boost::filesystem::path globalPath("/Library/Preferences/"); + return globalPath; } -boost::filesystem::path MacOsPath::getRuntimeConfigPath() const +boost::filesystem::path MacOsPath::getLocalPath() const { return boost::filesystem::path("./"); } -boost::filesystem::path MacOsPath::getLocalDataPath() const -{ - boost::filesystem::path localDataPath("."); - boost::filesystem::path suffix("/"); - - const char* theDir = getenv("OPENMW_DATA"); - if (theDir == NULL) - { - theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - suffix = boost::filesystem::path("/Library/Application Support/"); - } - } - - if (theDir != NULL) - { - localDataPath = boost::filesystem::path(theDir); - } - - localDataPath /= suffix; - return localDataPath; -} - -boost::filesystem::path MacOsPath::getGlobalDataPath() const -{ - boost::filesystem::path globalDataPath("/Library/Application Support/"); - return globalDataPath; -} - -boost::filesystem::path MacOsPath::getRuntimeDataPath() const -{ - return boost::filesystem::path("./data/"); -} - - } /* namespace Files */ #endif /* defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) */ diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 26f2c907f7..2087a21c70 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -43,14 +43,14 @@ struct MacOsPath * * \return boost::filesystem::path */ - boost::filesystem::path getLocalConfigPath() const; + boost::filesystem::path getUserPath() const; /** * \brief Return path to the global (system) configuration directory. * * \return boost::filesystem::path */ - boost::filesystem::path getGlobalConfigPath() const; + boost::filesystem::path getGlobalPath() const; /** * \brief Return path to the runtime configuration directory which is the @@ -58,29 +58,7 @@ struct MacOsPath * * \return boost::filesystem::path */ - boost::filesystem::path getRuntimeConfigPath() const; - - /** - * \brief Return path to the local data directory. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getLocalDataPath() const; - - /** - * \brief Return path to the global (system) data directory. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getGlobalDataPath() const; - - /** - * \brief Return runtime data path which is a location where - * an application was started with 'data' suffix. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getRuntimeDataPath() const; + boost::filesystem::path getLocalPath() const; }; } /* namespace Files */ diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index f42f149c14..7a4bab1dfb 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -10,9 +10,9 @@ namespace Files { -boost::filesystem::path WindowsPath::getLocalConfigPath() const +boost::filesystem::path WindowsPath::getUserPath() const { - boost::filesystem::path localConfigPath("."); + boost::filesystem::path userPath("."); boost::filesystem::path suffix("/"); TCHAR path[MAX_PATH]; @@ -21,17 +21,17 @@ boost::filesystem::path WindowsPath::getLocalConfigPath() const if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, path))) { PathAppend(path, TEXT("My Games")); - localConfigPath = boost::filesystem::path(path); + userPath = boost::filesystem::path(path); } - localConfigPath /= suffix; + userPath /= suffix; - return localConfigPath; + return userPath; } -boost::filesystem::path WindowsPath::getGlobalConfigPath() const +boost::filesystem::path WindowsPath::getGlobalPath() const { - boost::filesystem::path globalConfigPath("."); + boost::filesystem::path globalPath("."); boost::filesystem::path suffix("/"); TCHAR path[MAX_PATH]; @@ -39,34 +39,19 @@ boost::filesystem::path WindowsPath::getGlobalConfigPath() const if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, NULL, 0, path))) { - globalConfigPath = boost::filesystem::path(path); + globalPath = boost::filesystem::path(path); } - globalConfigPath /= suffix; + globalPath /= suffix; - return globalConfigPath; + return globalPath; } -boost::filesystem::path WindowsPath::getRuntimeConfigPath() const +boost::filesystem::path WindowsPath::getLocalPath() const { return boost::filesystem::path("./"); } -boost::filesystem::path WindowsPath::getLocalDataPath() const -{ - return getLocalConfigPath(); -} - -boost::filesystem::path WindowsPath::getGlobalDataPath() const -{ - return getGlobalConfigPath(); -} - -boost::filesystem::path WindowsPath::getRuntimeDataPath() const -{ - return boost::filesystem::path("./data/"); -} - } /* namespace Files */ #endif /* defined(_WIN32) || defined(__WINDOWS__) */ diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 47dfc08d8e..78b3981bb0 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -43,44 +43,22 @@ struct WindowsPath * * \return boost::filesystem::path */ - boost::filesystem::path getLocalConfigPath() const; + boost::filesystem::path getUserPath() const; /** * \brief Returns "X:\Program Files\" * * \return boost::filesystem::path */ - boost::filesystem::path getGlobalConfigPath() const; + boost::filesystem::path getGlobalPath() const; /** - * \brief Return runtime configuration path which is a location where + * \brief Return local path which is a location where * an application was started * * \return boost::filesystem::path */ - boost::filesystem::path getRuntimeConfigPath() const; - - /** - * \brief Return same path like getLocalConfigPath - * - * \return boost::filesystem::path - */ - boost::filesystem::path getLocalDataPath() const; - - /** - * \brief Return same path like getGlobalConfigPath - * - * \return boost::filesystem::path - */ - boost::filesystem::path getGlobalDataPath() const; - - /** - * \brief Return runtime data path which is a location where - * an application was started with 'data' suffix. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getRuntimeDataPath() const; + boost::filesystem::path getLocalPath() const; }; } /* namespace Files */ From 406897aa646b387fd29a65c6911ae1e958d24cc5 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 21 Jan 2012 17:58:49 +0100 Subject: [PATCH 168/269] Issue #168 - Configuration cleanup - WIP Sources update. Signed-off-by: Lukasz Gromanowski --- apps/openmw/main.cpp | 2 +- components/files/configurationmanager.cpp | 54 +++-------------- components/files/configurationmanager.hpp | 17 +++--- components/files/fixedpath.hpp | 70 ++++++----------------- components/files/linuxpath.cpp | 65 +++++++++++++++++++++ components/files/linuxpath.hpp | 23 ++++++++ components/files/macospath.cpp | 48 ++++++++++++++++ components/files/macospath.hpp | 2 + components/files/windowspath.cpp | 29 ++++++++++ components/files/windowspath.hpp | 24 ++++++++ 10 files changed, 229 insertions(+), 105 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 0f66925d38..02fa1d7651 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -166,7 +166,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat if (dataDirs.empty()) { - dataDirs.push_back(cfgMgr.getDataPath("local:data?")); + dataDirs.push_back(cfgMgr.getDataPath(Files::localDataToken)); } engine.setDataDirs(dataDirs); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 224fd26492..aaa26977c5 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -12,10 +12,10 @@ static const char* const openmwCfgFile = "openmw.cfg"; static const char* const ogreCfgFile = "ogre.cfg"; static const char* const pluginsCfgFile = "plugins.cfg"; -static const char* const mwDataToken = "?mw:data?"; -static const char* const localDataToken = "?local:data?"; -static const char* const userDataToken = "?user:data?"; -static const char* const globalDataToken = "?global:data?"; +const char* const mwDataToken = "?mw:data?"; +const char* const localDataToken = "?local:data?"; +const char* const userDataToken = "?user:data?"; +const char* const globalDataToken = "?global:data?"; ConfigurationManager::ConfigurationManager() : mFixedPath("openmw") @@ -55,10 +55,10 @@ ConfigurationManager::~ConfigurationManager() void ConfigurationManager::setupTokensMapping() { - mTokensMapping.insert(std::make_pair(mwDataToken, &ConfigurationManager::getInstallPath)); - mTokensMapping.insert(std::make_pair(localDataToken, &ConfigurationManager::getLocalDataPath)); - mTokensMapping.insert(std::make_pair(userDataToken, &ConfigurationManager::getUserDataPath)); - mTokensMapping.insert(std::make_pair(globalDataToken, &ConfigurationManager::getGlobalDataPath)); + mTokensMapping.insert(std::make_pair(mwDataToken, &FixedPath<>::getInstallPath)); + mTokensMapping.insert(std::make_pair(localDataToken, &FixedPath<>::getLocalDataPath)); + mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); + mTokensMapping.insert(std::make_pair(globalDataToken, &FixedPath<>::getGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, @@ -117,48 +117,12 @@ const boost::filesystem::path& ConfigurationManager::getDataPath(const std::stri TokensMappingContainer::const_iterator it = mTokensMapping.find(type); if (it != mTokensMapping.end()) { - return ((this)->*(it->second))(); + return ((mFixedPath).*(it->second))(); } return mFixedPath.getLocalDataPath(); } -const boost::filesystem::path& ConfigurationManager::getInstallPath() const -{ -// TODO: It will be corrected later. - static boost::filesystem::path p("./"); - return p; - - //return mFixedPath.getInstallPath(); -} - -const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const -{ -// TODO: It will be corrected later. - static boost::filesystem::path p("./"); - return p; - - //return mFixedPath.getGlobalDataPath(); -} - -const boost::filesystem::path& ConfigurationManager::getUserDataPath() const -{ -// TODO: It will be corrected later. - static boost::filesystem::path p("./"); - return p; - - //return mFixedPath.getUserDataPath(); -} - -const boost::filesystem::path& ConfigurationManager::getLocalDataPath() const -{ -// TODO: It will be corrected later. - static boost::filesystem::path p("./"); - return p; - //return mFixedPath.getLocalDataPath(); -} - - const boost::filesystem::path& ConfigurationManager::getOgreConfigPath() const { return mOgreCfgPath; diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 3004b62819..d1c9999793 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -14,6 +14,12 @@ namespace Files { +extern const char* const mwDataToken; +extern const char* const localDataToken; +extern const char* const userDataToken; +extern const char* const globalDataToken; + + /** * \struct ConfigurationManager */ @@ -37,7 +43,9 @@ struct ConfigurationManager const boost::filesystem::path& getLogPath() const; private: - typedef const boost::filesystem::path& (ConfigurationManager::*path_type_f)() const; + typedef Files::FixedPath<> FixedPathType; + + typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const; typedef std::tr1::unordered_map TokensMappingContainer; void loadConfig(const boost::filesystem::path& path, @@ -46,12 +54,7 @@ struct ConfigurationManager void setupTokensMapping(); - const boost::filesystem::path& getInstallPath() const; - const boost::filesystem::path& getGlobalDataPath() const; - const boost::filesystem::path& getUserDataPath() const; - const boost::filesystem::path& getLocalDataPath() const; - - Files::FixedPath<> mFixedPath; + FixedPathType mFixedPath; boost::filesystem::path mOgreCfgPath; boost::filesystem::path mPluginsCfgPath; diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 4e2b20e3cd..3a27bd21d4 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -73,9 +73,10 @@ struct FixedPath , mUserPath(mPath.getUserPath()) , mGlobalPath(mPath.getGlobalPath()) , mLocalPath(mPath.getLocalPath()) - , mLocalDataPath() - , mGlobalDataPath() - , mRuntimeDataPath() + , mUserDataPath(mPath.getUserDataPath()) + , mGlobalDataPath(mPath.getGlobalDataPath()) + , mLocalDataPath(mPath.getLocalDataPath()) + , mInstallPath(mPath.getInstallPath()) { if (!application_name.empty()) { @@ -119,64 +120,28 @@ struct FixedPath return mLocalPath; } - /** - * \brief Return path pointing to the user local data directory. - * - * \return boost::filesystem::path - */ - const boost::filesystem::path& getLocalDataPath() const + const boost::filesystem::path& getInstallPath() const { - return mLocalDataPath; + // TODO: It will be corrected later. + static boost::filesystem::path p("./"); + return p; + + //return mFixedPath.getInstallPath(); } - /** - * \brief Sets new local data path. - * - * \param [in] path - New path - */ - void setLocalDataPath(const boost::filesystem::path& path) - { - mLocalDataPath = path; - } - - /** - * \brief Return path pointing to the global (system) data directory. - * - * \return boost::filesystem::path - */ const boost::filesystem::path& getGlobalDataPath() const { return mGlobalDataPath; } - /** - * \brief Sets new global (system) data directory. - * - * \param [in] path - New path - */ - void setGlobalDataPath(const boost::filesystem::path& path) + const boost::filesystem::path& getUserDataPath() const { - mGlobalDataPath = path; + return mUserDataPath; } - /** - * \brief Return path pointing to the directory where application was started. - * - * \return boost::filesystem::path - */ - const boost::filesystem::path& getRuntimeDataPath() const + const boost::filesystem::path& getLocalDataPath() const { - return mRuntimeDataPath; - } - - /** - * \brief Sets new runtime data directory. - * - * \param [in] path - New path - */ - void setRuntimeDataPath(const boost::filesystem::path& path) - { - mRuntimeDataPath = path; + return mLocalDataPath; } private: @@ -186,11 +151,12 @@ struct FixedPath boost::filesystem::path mGlobalPath; /**< Global path */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ - boost::filesystem::path mLocalDataPath; /**< User local application data path (user plugins / mods / etc.) */ - boost::filesystem::path mGlobalDataPath; /**< Global application data path */ - boost::filesystem::path mRuntimeDataPath; /**< Runtime path to the configuration files. + boost::filesystem::path mUserDataPath; /**< User data path */ + boost::filesystem::path mGlobalDataPath; /**< Global application data path */ + boost::filesystem::path mLocalDataPath; /**< Local path to the configuration files. By default it is a 'data' directory in same directory where application was run */ + boost::filesystem::path mInstallPath; }; diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 11ddc31048..8466f0222e 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -95,6 +95,71 @@ boost::filesystem::path LinuxPath::getLocalPath() const return boost::filesystem::path("./"); } +boost::filesystem::path LinuxPath::getUserDataPath() const +{ + boost::filesystem::path localDataPath("."); + boost::filesystem::path suffix("/"); + + const char* theDir = getenv("OPENMW_DATA"); + if (theDir == NULL) + { + theDir = getenv("XDG_DATA_HOME"); + if (theDir == NULL) + { + theDir = getenv("HOME"); + if (theDir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + theDir = pwd->pw_dir; + } + } + if (theDir != NULL) + { + suffix = boost::filesystem::path("/.local/share/"); + } + } + } + + if (theDir != NULL) { + localDataPath = boost::filesystem::path(theDir); + } + + localDataPath /= suffix; + return localDataPath; +} + +boost::filesystem::path LinuxPath::getGlobalDataPath() const +{ + boost::filesystem::path globalDataPath("/usr/local/share/"); + + char* theDir = getenv("XDG_DATA_DIRS"); + if (theDir != NULL) + { + // We take only first path from list + char* ptr = strtok(theDir, ":"); + if (ptr != NULL) + { + globalDataPath = boost::filesystem::path(ptr); + globalDataPath /= boost::filesystem::path("/"); + } + } + + return globalDataPath; +} + +boost::filesystem::path LinuxPath::getLocalDataPath() const +{ + return boost::filesystem::path("./data/"); +} + + +boost::filesystem::path LinuxPath::getInstallPath() const +{ + return boost::filesystem::path("./"); +} + } /* namespace Files */ #endif /* defined(__linux__) || defined(__FreeBSD__) */ diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index af92326bed..51be6242cc 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -59,6 +59,29 @@ struct LinuxPath * \return boost::filesystem::path */ boost::filesystem::path getLocalPath() const; + + /** + * \brief + * + * \return boost::filesystem::path + */ + boost::filesystem::path getUserDataPath() const; + + /** + * \brief + * + * \return boost::filesystem::path + */ + boost::filesystem::path getGlobalDataPath() const; + + /** + * \brief + * + * \return boost::filesystem::path + */ + boost::filesystem::path getLocalDataPath() const; + + boost::filesystem::path getInstallPath() const; }; } /* namespace Files */ diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 1ffb443991..87ac422020 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -69,6 +69,54 @@ boost::filesystem::path MacOsPath::getLocalPath() const return boost::filesystem::path("./"); } +boost::filesystem::path MacOsPath::getUserDataPath() const +{ + boost::filesystem::path localDataPath("."); + boost::filesystem::path suffix("/"); + + const char* theDir = getenv("OPENMW_DATA"); + if (theDir == NULL) + { + theDir = getenv("HOME"); + if (theDir == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + theDir = pwd->pw_dir; + } + } + if (theDir != NULL) + { + suffix = boost::filesystem::path("/Library/Application Support/"); + } + } + + if (theDir != NULL) + { + localDataPath = boost::filesystem::path(theDir); + } + + localDataPath /= suffix; + return localDataPath; +} + +boost::filesystem::path MacOsPath::getGlobalDataPath() const +{ + boost::filesystem::path globalDataPath("/Library/Application Support/"); + return globalDataPath; +} + +boost::filesystem::path MacOsPath::getLocalDataPath() const +{ + return boost::filesystem::path("./data/"); +} + +boost::filesystem::path MacOsPath::getInstallPath() const; +{ + return boost::filesystem::path("./"); +} + } /* namespace Files */ #endif /* defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) */ diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 2087a21c70..2194dbb94a 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -59,6 +59,8 @@ struct MacOsPath * \return boost::filesystem::path */ boost::filesystem::path getLocalPath() const; + + boost::filesystem::path getInstallPath() const; }; } /* namespace Files */ diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 7a4bab1dfb..a54f800d40 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -52,6 +52,35 @@ boost::filesystem::path WindowsPath::getLocalPath() const return boost::filesystem::path("./"); } +/** + * FIXME: Someone with Windows system should check this and correct if necessary + */ +boost::filesystem::path WindowsPath::getUserDataPath() const +{ + return getUserConfigPath(); +} + +/** + * FIXME: Someone with Windows system should check this and correct if necessary + */ +boost::filesystem::path WindowsPath::getGlobalDataPath() const +{ + return getGlobalConfigPath(); +} + +/** + * FIXME: Someone with Windows system should check this and correct if necessary + */ +boost::filesystem::path WindowsPath::getLocalDataPath() const +{ + return boost::filesystem::path("./data/"); +} + +boost::filesystem::path WindowsPath::getInstallPath() const; +{ + return boost::filesystem::path("./"); +} + } /* namespace Files */ #endif /* defined(_WIN32) || defined(__WINDOWS__) */ diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 78b3981bb0..95af3ea902 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -59,6 +59,30 @@ struct WindowsPath * \return boost::filesystem::path */ boost::filesystem::path getLocalPath() const; + + /** + * \brief Return same path like getUserConfigPath + * + * \return boost::filesystem::path + */ + boost::filesystem::path getUserDataPath() const; + + /** + * \brief Return same path like getGlobalConfigPath + * + * \return boost::filesystem::path + */ + boost::filesystem::path getGlobalDataPath() const; + + /** + * \brief Return runtime data path which is a location where + * an application was started with 'data' suffix. + * + * \return boost::filesystem::path + */ + boost::filesystem::path getLocalDataPath() const; + + boost::filesystem::path getInstallPath() const; }; } /* namespace Files */ From 0a55fe0219b60b549c1d8c5e29e37d1f49f92917 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 23 Jan 2012 21:49:51 +0100 Subject: [PATCH 169/269] Fixed NPC activation. Might require some more work when NPC physic will be used. Note: some NPC seems to be smaller than other, but for now, the capsule shae size is the same for every NPC --- bullet/physic.cpp | 16 +++++++++------- bullet/physic.hpp | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 8cf7b8eb64..8fa54a9e61 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -34,8 +34,8 @@ namespace Physic externalGhostObject = new PairCachingGhostObject(name); externalGhostObject->setWorldTransform( transform ); - btScalar externalCapsuleHeight = 130; - btScalar externalCapsuleWidth = 16; + btScalar externalCapsuleHeight = 120; + btScalar externalCapsuleWidth = 19; externalCollisionShape = new btCapsuleShapeZ( externalCapsuleWidth, externalCapsuleHeight ); externalCollisionShape->setMargin( 0.1 ); @@ -47,8 +47,8 @@ namespace Physic internalGhostObject = new PairCachingGhostObject(name); internalGhostObject->setWorldTransform( transform ); //internalGhostObject->getBroadphaseHandle()->s - btScalar internalCapsuleHeight = 120; - btScalar internalCapsuleWidth = 15; + btScalar internalCapsuleHeight = 110; + btScalar internalCapsuleWidth = 17; internalCollisionShape = new btCapsuleShapeZ( internalCapsuleWidth, internalCapsuleHeight ); internalCollisionShape->setMargin( 0.1 ); @@ -62,6 +62,8 @@ namespace Physic mCharacter->mCollision = false; setGravity(0); + + mTranslation = btVector3(0,0,70); } PhysicActor::~PhysicActor() @@ -113,7 +115,7 @@ namespace Physic btVector3 PhysicActor::getPosition(void) { - return internalGhostObject->getWorldTransform().getOrigin(); + return internalGhostObject->getWorldTransform().getOrigin() -mTranslation; } btQuaternion PhysicActor::getRotation(void) @@ -123,8 +125,8 @@ namespace Physic void PhysicActor::setPosition(const btVector3& pos) { - internalGhostObject->getWorldTransform().setOrigin(pos); - externalGhostObject->getWorldTransform().setOrigin(pos); + internalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); + externalGhostObject->getWorldTransform().setOrigin(pos+mTranslation); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/bullet/physic.hpp b/bullet/physic.hpp index d4dfde4673..9f554dd0e7 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -89,6 +89,8 @@ namespace Physic btCollisionShape* externalCollisionShape; std::string mName; + + btVector3 mTranslation; }; /** From 6c15aec5e4c791c01030ea3b6d3e8352a8577a61 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 23 Jan 2012 22:16:08 +0100 Subject: [PATCH 170/269] added some comments --- bullet/physic.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bullet/physic.hpp b/bullet/physic.hpp index 9f554dd0e7..7507619653 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -90,6 +90,10 @@ namespace Physic std::string mName; + /** + *NPC scenenode is located on there feet, and you can't simply translate a btShape, so this vector is used + *each time get/setposition is called. + */ btVector3 mTranslation; }; From 1d96b99532f069bd50dd6cc7d1ef3a552e4c3e7f Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 25 Jan 2012 23:55:43 +0100 Subject: [PATCH 171/269] Issue #168 - Configuration cleanup Added tokens processing, modified getInstallPath for linux so we could use ~/.wine/dosdevices symlinks. Signed-off-by: Lukasz Gromanowski --- apps/openmw/main.cpp | 3 +- components/files/configurationmanager.cpp | 77 ++++++++++++--- components/files/configurationmanager.hpp | 17 ++-- components/files/fixedpath.hpp | 24 ++++- components/files/linuxpath.cpp | 112 +++++++++++----------- components/files/macospath.cpp | 76 ++++++++++++++- 6 files changed, 226 insertions(+), 83 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 02fa1d7651..9f70fac15e 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -157,6 +157,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.enableFSStrict(variables["fs-strict"].as()); Files::PathContainer dataDirs(variables["data"].as()); + cfgMgr.processPaths(dataDirs); std::string local(variables["data-local"].as()); if (!local.empty()) @@ -166,7 +167,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat if (dataDirs.empty()) { - dataDirs.push_back(cfgMgr.getDataPath(Files::localDataToken)); + dataDirs.push_back(cfgMgr.getLocalDataPath()); } engine.setDataDirs(dataDirs); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index aaa26977c5..e1bf48a6fc 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include namespace Files { @@ -24,7 +24,7 @@ ConfigurationManager::ConfigurationManager() /** * According to task #168 plugins.cfg file shall be located in global - * configuration path or in runtime configuration path. + * configuration path or in local configuration path. */ mPluginsCfgPath = mFixedPath.getGlobalPath() / pluginsCfgFile; if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) @@ -55,10 +55,10 @@ ConfigurationManager::~ConfigurationManager() void ConfigurationManager::setupTokensMapping() { - mTokensMapping.insert(std::make_pair(mwDataToken, &FixedPath<>::getInstallPath)); - mTokensMapping.insert(std::make_pair(localDataToken, &FixedPath<>::getLocalDataPath)); - mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); - mTokensMapping.insert(std::make_pair(globalDataToken, &FixedPath<>::getGlobalDataPath)); + mTokensMapping.insert(std::make_pair(mwDataToken, &FixedPath<>::setInstallPath)); + mTokensMapping.insert(std::make_pair(localDataToken, &FixedPath<>::setLocalDataPath)); + mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::setUserDataPath)); + mTokensMapping.insert(std::make_pair(globalDataToken, &FixedPath<>::setGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, @@ -66,10 +66,54 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m { loadConfig(mFixedPath.getUserPath(), variables, description); boost::program_options::notify(variables); + loadConfig(mFixedPath.getLocalPath(), variables, description); boost::program_options::notify(variables); loadConfig(mFixedPath.getGlobalPath(), variables, description); boost::program_options::notify(variables); + +} + +struct EmptyPath : public std::unary_function +{ + bool operator()(const boost::filesystem::path& path) const + { + return path.empty(); + } +}; + +void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) +{ + for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + { + const std::string& path = it->string(); + if (!path.empty() && path[0] == '?') + { + std::string::size_type pos = path.find('?', 1); + if (pos != std::string::npos) + { + ++pos; + TokensMappingContainer::iterator tokenIt = mTokensMapping.find(path.substr(0, pos)); + + if (tokenIt != mTokensMapping.end()) + { + boost::filesystem::path tempPath(path.substr(pos, path.length() - pos)); + + if (boost::filesystem::is_directory(tempPath)) + { + ((mFixedPath).*(tokenIt->second))(tempPath); + (*it) = tempPath; + } + else + { + (*it).clear(); + } + } + } + } + } + + dataDirs.erase(std::remove_if(dataDirs.begin(), dataDirs.end(), EmptyPath()), dataDirs.end()); } void ConfigurationManager::loadConfig(const boost::filesystem::path& path, @@ -112,17 +156,26 @@ const boost::filesystem::path& ConfigurationManager::getLocalPath() const return mFixedPath.getLocalPath(); } -const boost::filesystem::path& ConfigurationManager::getDataPath(const std::string& type) const +const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const { - TokensMappingContainer::const_iterator it = mTokensMapping.find(type); - if (it != mTokensMapping.end()) - { - return ((mFixedPath).*(it->second))(); - } + return mFixedPath.getGlobalDataPath(); +} +const boost::filesystem::path& ConfigurationManager::getUserDataPath() const +{ + return mFixedPath.getUserDataPath(); +} + +const boost::filesystem::path& ConfigurationManager::getLocalDataPath() const +{ return mFixedPath.getLocalDataPath(); } +const boost::filesystem::path& ConfigurationManager::getInstallPath() const +{ + return mFixedPath.getInstallPath(); +} + const boost::filesystem::path& ConfigurationManager::getOgreConfigPath() const { return mOgreCfgPath; diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index d1c9999793..dce56ea5d5 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -7,6 +7,7 @@ #include #include +#include /** * \namespace Files @@ -14,12 +15,6 @@ namespace Files { -extern const char* const mwDataToken; -extern const char* const localDataToken; -extern const char* const userDataToken; -extern const char* const globalDataToken; - - /** * \struct ConfigurationManager */ @@ -30,13 +25,17 @@ struct ConfigurationManager void readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description); + void processPaths(Files::PathContainer& dataDirs); /**< Fixed paths */ const boost::filesystem::path& getGlobalPath() const; const boost::filesystem::path& getUserPath() const; - const boost::filesystem::path& getLocalPath() const ; + const boost::filesystem::path& getLocalPath() const; - const boost::filesystem::path& getDataPath(const std::string& type) const; + const boost::filesystem::path& getGlobalDataPath() const; + const boost::filesystem::path& getUserDataPath() const; + const boost::filesystem::path& getLocalDataPath() const; + const boost::filesystem::path& getInstallPath() const; const boost::filesystem::path& getOgreConfigPath() const; const boost::filesystem::path& getPluginsConfigPath() const; @@ -45,7 +44,7 @@ struct ConfigurationManager private: typedef Files::FixedPath<> FixedPathType; - typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const; + typedef void (FixedPathType::*path_type_f)(const boost::filesystem::path&); typedef std::tr1::unordered_map TokensMappingContainer; void loadConfig(const boost::filesystem::path& path, diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 3a27bd21d4..3db409b2c2 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -122,11 +122,12 @@ struct FixedPath const boost::filesystem::path& getInstallPath() const { - // TODO: It will be corrected later. - static boost::filesystem::path p("./"); - return p; + return mInstallPath; + } - //return mFixedPath.getInstallPath(); + void setInstallPath(const boost::filesystem::path& path) + { + mInstallPath = path; } const boost::filesystem::path& getGlobalDataPath() const @@ -134,16 +135,31 @@ struct FixedPath return mGlobalDataPath; } + void setGlobalDataPath(const boost::filesystem::path& path) + { + mGlobalDataPath = path; + } + const boost::filesystem::path& getUserDataPath() const { return mUserDataPath; } + void setUserDataPath(const boost::filesystem::path& path) + { + mUserDataPath = path; + } + const boost::filesystem::path& getLocalDataPath() const { return mLocalDataPath; } + void setLocalDataPath(const boost::filesystem::path& path) + { + mLocalDataPath = path; + } + private: PathType mPath; diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index a1d1168d9a..41891661ef 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -157,73 +157,75 @@ boost::filesystem::path LinuxPath::getLocalDataPath() const boost::filesystem::path LinuxPath::getInstallPath() const { - char *homePath = getenv("HOME"); - if(!homePath) - { - return boost::filesystem::path(""); - } - - boost::filesystem::path wineDefaultRegistry(homePath); - wineDefaultRegistry /= ".wine/system.reg"; - - boost::filesystem::path wineDriveC(homePath); - wineDriveC /= ".wine/drive_c"; + boost::filesystem::path installPath; - boost::filesystem::file_status fileStatus = boost::filesystem::status(wineDefaultRegistry); - boost::filesystem::file_status dirStatus = boost::filesystem::status(wineDriveC); - if(!boost::filesystem::is_regular_file(fileStatus) || !boost::filesystem::is_directory(dirStatus)) + char *homePath = getenv("HOME"); + if (homePath == NULL) { - return boost::filesystem::path(""); - } - - - boost::filesystem::ifstream file(wineDefaultRegistry); - bool isRegEntry = false; - std::string line; - - while (std::getline(file, line)) - { - if(!line.empty() && line[0] == '[') // we found an entry + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) { - std::string regkey = line.substr(1, line.find(']')-1); - if( regkey.compare("SOFTWARE\\\\Wow6432Node\\\\Bethesda Softworks\\\\Morrowind") == 0 - || regkey.compare("SOFTWARE\\\\Bethesda Softworks\\\\Morrowind") == 0 ) - { - isRegEntry = true; - } + homePath = pwd->pw_dir; } - else if(isRegEntry) + } + + if (homePath != NULL) + { + boost::filesystem::path wineDefaultRegistry(homePath); + wineDefaultRegistry /= ".wine/system.reg"; + + if (boost::filesystem::is_regular_file(wineDefaultRegistry)) { - if(line.empty() || line[0] != '"') // empty line means new registry key + boost::filesystem::ifstream file(wineDefaultRegistry); + bool isRegEntry = false; + std::string line; + std::string mwpath; + + while (std::getline(file, line) && !line.empty()) { - break; - } - std::string key = line.substr(1, line.find('"', 1)-1); - if(key.compare("Installed Path") == 0) { - std::string::size_type pos, startPos; - - startPos = line.find('=')+2; - std::string installPath = line.substr(startPos, line.find('"', startPos+1)-startPos); - installPath.replace(0, 2, wineDriveC.string()); - - pos = -1; - do + if (line[0] == '[') // we found an entry { - pos = installPath.find("\\\\", pos+1); - if(pos == std::string::npos) + isRegEntry = (line.find("Softworks\\Morrowind]") != std::string::npos); + } + else if (isRegEntry) + { + if (line[0] == '"') // empty line means new registry key { - break; + std::string key = line.substr(1, line.find('"', 1) - 1); + if (strcasecmp(key.c_str(), "Installed Path") == 0) + { + std::string::size_type valuePos = line.find('=') + 2; + mwpath = line.substr(valuePos, line.rfind('"') - valuePos); + + std::string::size_type pos = mwpath.find("\\"); + while (pos != std::string::npos) + { + mwpath.replace(pos, 2, "/"); + pos = mwpath.find("\\", pos + 1); + } + break; + } } - - installPath.replace(pos, 2, "/"); - } while(true); - - return boost::filesystem::path(installPath); + } + } + + if (!mwpath.empty()) + { + // Change drive letter to lowercase, so we could use ~/.wine/dosdevice symlinks + mwpath[0] = tolower(mwpath[0]); + installPath /= homePath; + installPath /= ".wine/dosdevices/"; + installPath /= mwpath; + + if (!boost::filesystem::is_directory(installPath)) + { + installPath.clear(); + } } } } - - return boost::filesystem::path(""); + + return installPath; } } /* namespace Files */ diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 87ac422020..789c877099 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -112,11 +112,83 @@ boost::filesystem::path MacOsPath::getLocalDataPath() const return boost::filesystem::path("./data/"); } -boost::filesystem::path MacOsPath::getInstallPath() const; +/** + * FIXME: This should be verified on MacOS system! + */ +boost::filesystem::path MacOsPath::getInstallPath() const { - return boost::filesystem::path("./"); + boost::filesystem::path installPath; + + char *homePath = getenv("HOME"); + if (homePath == NULL) + { + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) + { + homePath = pwd->pw_dir; + } + } + + if (homePath != NULL) + { + boost::filesystem::path wineDefaultRegistry(homePath); + wineDefaultRegistry /= ".wine/system.reg"; + + if (boost::filesystem::is_regular_file(wineDefaultRegistry)) + { + boost::filesystem::ifstream file(wineDefaultRegistry); + bool isRegEntry = false; + std::string line; + std::string mwpath; + + while (std::getline(file, line) && !line.empty()) + { + if (line[0] == '[') // we found an entry + { + isRegEntry = (line.find("Softworks\\Morrowind]") != std::string::npos); + } + else if (isRegEntry) + { + if (line[0] == '"') // empty line means new registry key + { + std::string key = line.substr(1, line.find('"', 1) - 1); + if (strcasecmp(key.c_str(), "Installed Path") == 0) + { + std::string::size_type valuePos = line.find('=') + 2; + mwpath = line.substr(valuePos, line.rfind('"') - valuePos); + + std::string::size_type pos = mwpath.find("\\"); + while (pos != std::string::npos) + { + mwpath.replace(pos, 2, "/"); + pos = mwpath.find("\\", pos + 1); + } + break; + } + } + } + } + + if (!mwpath.empty()) + { + // Change drive letter to lowercase, so we could use ~/.wine/dosdevice symlinks + mwpath[0] = tolower(mwpath[0]); + installPath /= homePath; + installPath /= ".wine/dosdevices/"; + installPath /= mwpath; + + if (!boost::filesystem::is_directory(installPath)) + { + installPath.clear(); + } + } + } + } + + return installPath; } + } /* namespace Files */ #endif /* defined(macintosh) || defined(Macintosh) || defined(__APPLE__) || defined(__MACH__) */ From f4b6caac5961e869b531a63d1113e2a00d89080c Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 26 Jan 2012 00:14:03 +0100 Subject: [PATCH 172/269] Revert "Issue #178 - workaround for compilation problems with ogre 1.8.0." This reverts commit 37e272b3b5e18d1a93a1d69804a96845f8762ba1. It shouldn't be commited and pushed to config branch. --- components/bsa/bsa_archive.cpp | 6 ------ extern/caelum/src/CaelumPlugin.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 87b46b34c4..2178be318e 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -221,12 +221,6 @@ class BSAArchive : public Archive { BSAFile arc; - FileInfoListPtr findFileInfo(const String&, bool, bool) const - { - static FileInfoListPtr filp(new FileInfoList()); - return filp; - } - public: BSAArchive(const String& name) : Archive(name, "BSA") diff --git a/extern/caelum/src/CaelumPlugin.cpp b/extern/caelum/src/CaelumPlugin.cpp index 340a26559a..288ad9220a 100644 --- a/extern/caelum/src/CaelumPlugin.cpp +++ b/extern/caelum/src/CaelumPlugin.cpp @@ -21,17 +21,17 @@ along with Caelum. If not, see . #include "CaelumPrecompiled.h" #include "CaelumPlugin.h" -template<> Caelum::CaelumPlugin* Ogre::Singleton::msSingleton = 0; +template<> Caelum::CaelumPlugin* Ogre::Singleton::ms_Singleton = 0; namespace Caelum { CaelumPlugin* CaelumPlugin::getSingletonPtr () { - return msSingleton; + return ms_Singleton; } CaelumPlugin& CaelumPlugin::getSingleton () { - assert (msSingleton); - return *msSingleton; + assert (ms_Singleton); + return *ms_Singleton; } extern "C" void CAELUM_EXPORT dllStartPlugin () { From c5dee2c4fb792ea31f24c0aad13dbe23df66941a Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 28 Jan 2012 09:49:09 +0100 Subject: [PATCH 173/269] Issue #168 - Configuration cleanup Corrected tokens processing. If directory exist then tokens shall be replaced by correct path, otherwise they are silently removed from path container. Signed-off-by: Lukasz Gromanowski --- components/files/configurationmanager.cpp | 37 ++++++++++------------- components/files/configurationmanager.hpp | 2 +- components/files/fixedpath.hpp | 20 ------------ 3 files changed, 17 insertions(+), 42 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index e1bf48a6fc..91e2790517 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -55,10 +55,10 @@ ConfigurationManager::~ConfigurationManager() void ConfigurationManager::setupTokensMapping() { - mTokensMapping.insert(std::make_pair(mwDataToken, &FixedPath<>::setInstallPath)); - mTokensMapping.insert(std::make_pair(localDataToken, &FixedPath<>::setLocalDataPath)); - mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::setUserDataPath)); - mTokensMapping.insert(std::make_pair(globalDataToken, &FixedPath<>::setGlobalDataPath)); + mTokensMapping.insert(std::make_pair(mwDataToken, &FixedPath<>::getInstallPath)); + mTokensMapping.insert(std::make_pair(localDataToken, &FixedPath<>::getLocalDataPath)); + mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); + mTokensMapping.insert(std::make_pair(globalDataToken, &FixedPath<>::getGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, @@ -87,27 +87,22 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { const std::string& path = it->string(); - if (!path.empty() && path[0] == '?') + + // Check if path contains a token + if (!path.empty() && *path.begin() == '?' && *path.rbegin() == '?') { - std::string::size_type pos = path.find('?', 1); - if (pos != std::string::npos) + TokensMappingContainer::iterator tokenIt = mTokensMapping.find(path); + if (tokenIt != mTokensMapping.end()) { - ++pos; - TokensMappingContainer::iterator tokenIt = mTokensMapping.find(path.substr(0, pos)); + boost::filesystem::path tempPath(((mFixedPath).*(tokenIt->second))()); - if (tokenIt != mTokensMapping.end()) + if (boost::filesystem::is_directory(tempPath)) { - boost::filesystem::path tempPath(path.substr(pos, path.length() - pos)); - - if (boost::filesystem::is_directory(tempPath)) - { - ((mFixedPath).*(tokenIt->second))(tempPath); - (*it) = tempPath; - } - else - { - (*it).clear(); - } + (*it) = tempPath; + } + else + { + (*it).clear(); } } } diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index dce56ea5d5..7d77df9c00 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -44,7 +44,7 @@ struct ConfigurationManager private: typedef Files::FixedPath<> FixedPathType; - typedef void (FixedPathType::*path_type_f)(const boost::filesystem::path&); + typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const; typedef std::tr1::unordered_map TokensMappingContainer; void loadConfig(const boost::filesystem::path& path, diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 3db409b2c2..1bf582ab9c 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -125,41 +125,21 @@ struct FixedPath return mInstallPath; } - void setInstallPath(const boost::filesystem::path& path) - { - mInstallPath = path; - } - const boost::filesystem::path& getGlobalDataPath() const { return mGlobalDataPath; } - void setGlobalDataPath(const boost::filesystem::path& path) - { - mGlobalDataPath = path; - } - const boost::filesystem::path& getUserDataPath() const { return mUserDataPath; } - void setUserDataPath(const boost::filesystem::path& path) - { - mUserDataPath = path; - } - const boost::filesystem::path& getLocalDataPath() const { return mLocalDataPath; } - void setLocalDataPath(const boost::filesystem::path& path) - { - mLocalDataPath = path; - } - private: PathType mPath; From 86f88bedae4a7bca9ae3e8645825388eefb579fc Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 28 Jan 2012 11:59:08 +0100 Subject: [PATCH 174/269] Issue #168 - Configuration cleanup Removed 'data' part from token names, added token cleaning when invalid or unknown token is passed as commandline parameter. Signed-off-by: Lukasz Gromanowski --- components/files/configurationmanager.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 91e2790517..28d49a50c2 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -12,10 +12,10 @@ static const char* const openmwCfgFile = "openmw.cfg"; static const char* const ogreCfgFile = "ogre.cfg"; static const char* const pluginsCfgFile = "plugins.cfg"; -const char* const mwDataToken = "?mw:data?"; -const char* const localDataToken = "?local:data?"; -const char* const userDataToken = "?user:data?"; -const char* const globalDataToken = "?global:data?"; +const char* const mwToken = "?mw?"; +const char* const localToken = "?local?"; +const char* const userToken = "?user?"; +const char* const globalToken = "?global?"; ConfigurationManager::ConfigurationManager() : mFixedPath("openmw") @@ -55,10 +55,10 @@ ConfigurationManager::~ConfigurationManager() void ConfigurationManager::setupTokensMapping() { - mTokensMapping.insert(std::make_pair(mwDataToken, &FixedPath<>::getInstallPath)); - mTokensMapping.insert(std::make_pair(localDataToken, &FixedPath<>::getLocalDataPath)); - mTokensMapping.insert(std::make_pair(userDataToken, &FixedPath<>::getUserDataPath)); - mTokensMapping.insert(std::make_pair(globalDataToken, &FixedPath<>::getGlobalDataPath)); + mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); + mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalDataPath)); + mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserDataPath)); + mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, @@ -105,6 +105,11 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) (*it).clear(); } } + else + { + // Clean invalid / unknown token, it will be removed outside the loop + (*it).clear(); + } } } From b004e2479c335a40d61ea944f69403cf0176bff8 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Jan 2012 20:27:03 +0100 Subject: [PATCH 175/269] Issue #133 Handle resources across multiple data directories - WIP Work In Progress - added support for multiple paths in SoundManager. Signed-off-by: Lukasz Gromanowski --- apps/openmw/engine.cpp | 12 +- apps/openmw/engine.hpp | 2 +- apps/openmw/mwsound/soundmanager.cpp | 169 +++++++++++++----------- apps/openmw/mwsound/soundmanager.hpp | 11 +- components/file_finder/file_finder.hpp | 89 ++++++++++++- components/file_finder/search.cpp | 28 ++-- components/files/multidircollection.hpp | 2 +- 7 files changed, 200 insertions(+), 113 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bbc68b8e23..31c9470548 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -200,12 +200,12 @@ void OMW::Engine::loadBSA() for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter) { - std::cout << "Adding " << iter->second.string() << std::endl; - Bsa::addBSA (iter->second.string()); + std::cout << "Adding " << iter->second.string() << std::endl; + Bsa::addBSA (iter->second.string()); } - std::cout << "Data dir " << mDataDir.string() << std::endl; - Bsa::addDir(mDataDir.string(), mFSStrict); + //std::cout << "Data dir " << mDataDir.string() << std::endl; + //Bsa::addDir(mDataDir.string(), mFSStrict); } // add resources directory @@ -228,7 +228,7 @@ void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs) { /// \todo remove mDataDir, once resources system can handle multiple directories assert (!dataDirs.empty()); - mDataDir = dataDirs.back(); + mDataDirs = dataDirs; mFileCollections = Files::Collections (dataDirs, !mFSStrict); } @@ -339,7 +339,7 @@ void OMW::Engine::go() mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(), mOgre->getCamera(), mEnvironment.mWorld->getStore(), - (mDataDir), + mDataDirs, mUseSound, mFSStrict, mEnvironment); // Create script system diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 97079f5a5e..40bf73c6d2 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -64,7 +64,7 @@ namespace OMW class Engine : private Ogre::FrameListener { std::string mEncoding; - boost::filesystem::path mDataDir; + Files::PathContainer mDataDirs; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; OEngine::Physic::PhysicEngine* mPhysicEngine; diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 7390e4c5ca..76ef23bc2a 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -4,8 +4,6 @@ #include #include -using namespace std; - #include #include @@ -15,6 +13,7 @@ using namespace std; #include #include + #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" #include "../mwworld/player.hpp" @@ -90,24 +89,28 @@ namespace MWSound // relative to the sound dir, and translates them into full paths // of existing files in the filesystem, if they exist. bool FSstrict; - FileFinder::FileFinder files; - FileFinder::FileFinderStrict strict; - FileFinder::FileFinder musicpath; - FileFinder::FileFinderStrict musicpathStrict; + FileFinder::LessTreeFileFinder files; + FileFinder::StrictTreeFileFinder strict; + FileFinder::LessTreeFileFinder musicpath; + FileFinder::StrictTreeFileFinder musicpathStrict; - SoundImpl(Ogre::Root *root, Ogre::Camera *camera, - const ESMS::ESMStore &str, - const std::string &soundDir, const std::string &musicDir, bool fsstrict) + SoundImpl(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &str, + const Files::PathContainer& soundDir, + const Files::PathContainer& musicDir, + bool fsstrict) : mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) , updater(mgr) , cameraTracker(mgr) , store(str) - , files(soundDir), strict(soundDir) - ,musicpath(musicDir), musicpathStrict(musicDir) + , FSstrict(fsstrict) + , files(soundDir) + , strict(soundDir) + , musicpath(musicDir) + , musicpathStrict(musicDir) { - FSstrict = fsstrict; - cout << "Sound output: " << SOUND_OUT << endl; - cout << "Sound decoder: " << SOUND_IN << endl; + + std::cout << "Sound output: " << SOUND_OUT << std::endl; + std::cout << "Sound decoder: " << SOUND_IN << std::endl; // Attach the camera to the camera tracker cameraTracker.followCamera(camera); @@ -136,36 +139,49 @@ namespace MWSound bool hasFile(const std::string &str, bool music = false) { - if(FSstrict == false) + bool found = false; + if(!FSstrict) { if(music) { - if(musicpath.has(str)) return true; - + found = musicpath.has(str); // Not found? Try with .mp3 - return musicpath.has(toMp3(str)); + if (!found) + { + found = musicpath.has(toMp3(str)); + } } else { - if(files.has(str)) return true; - return files.has(toMp3(str)); + found = files.has(str); + if (!found) + { + found = files.has(toMp3(str)); + } } } else { if(music) { - if(musicpathStrict.has(str)) return true; - + found = musicpathStrict.has(str); // Not found? Try with .mp3 - return musicpathStrict.has(toMp3(str)); + if (!found) + { + found = musicpathStrict.has(toMp3(str)); + } } else { - if(strict.has(str)) return true; - return strict.has(toMp3(str)); + found = strict.has(str); + if (!found) + { + found = strict.has(toMp3(str)); + } } } + + return found; } // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path @@ -258,13 +274,13 @@ namespace MWSound } catch(...) { - cout << "Error loading " << file << ", skipping.\n"; + std::cout << "Error loading " << file << ", skipping.\n"; } } // Clears all the sub-elements of a given iterator, and then // removes it from 'sounds'. - void clearAll(PtrMap::iterator it) + void clearAll(PtrMap::iterator& it) { IDMap::iterator sit = it->second.begin(); @@ -362,9 +378,9 @@ namespace MWSound } } } - }; + }; /* SoundImpl */ - void SoundManager::streamMusicFull (const std::string& filename) + void SoundManager::streamMusicFull(const std::string& filename) { if(!mData) return; @@ -381,20 +397,24 @@ namespace MWSound } SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, - const ESMS::ESMStore &store, - boost::filesystem::path dataDir, - bool useSound, bool fsstrict, MWWorld::Environment& environment) - : mData(NULL), fsStrict (fsstrict), mEnvironment (environment) + const ESMS::ESMStore &store, const Files::PathContainer& dataDirs, + bool useSound, bool fsstrict, MWWorld::Environment& environment) + : mData(NULL) + , fsStrict(fsstrict) + , mEnvironment(environment) { - MP3Lookup(dataDir / "Music/Explore/"); - if(useSound) - mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string(), (dataDir / "Music").string(), fsstrict); + for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + { + MP3Lookup((*it) / "Music/Explore/"); + } + if(useSound) + { + mData = new SoundImpl(root, camera, store, dataDirs /* Sound */, dataDirs /* Music */, fsstrict); + } test.name = ""; total = 0; - - } SoundManager::~SoundManager() @@ -407,14 +427,12 @@ namespace MWSound { if(mData->hasFile(filename, true)) { - std::string fullpath = mData->convertPath(filename, true); - streamMusicFull(fullpath); + streamMusicFull(mData->convertPath(filename, true)); } } - - void SoundManager::MP3Lookup(boost::filesystem::path dir) -{ + void SoundManager::MP3Lookup(const boost::filesystem::path& dir) + { boost::filesystem::directory_iterator dir_iter(dir), dir_end; std::string mp3extension = ".mp3"; @@ -425,35 +443,30 @@ namespace MWSound files.push_back(*dir_iter); } } -} + } void SoundManager::startRandomTitle() -{ - std::vector::iterator fileIter; - - if(files.size() > 0) + { + if(!files.empty()) { - fileIter = files.begin(); - srand ( time(NULL) ); + Files::PathContainer::iterator fileIter = files.begin(); + srand( time(NULL) ); int r = rand() % files.size() + 1; //old random code - for(int i = 1; i < r; i++) - { - fileIter++; - } + std::advance(fileIter, r - 1); std::string music = fileIter->string(); + std::cout << "Playing " << music << "\n"; + try { - std::cout << "Playing " << music << "\n"; streamMusicFull(music); } - catch(std::exception &e) + catch (std::exception &e) { std::cout << " Music Error: " << e.what() << "\n"; } } -} - + } bool SoundManager::isMusicPlaying() { @@ -465,14 +478,12 @@ namespace MWSound return test; } - SoundManager::SoundImpl SoundManager::getMData() + SoundManager::SoundImpl SoundManager::getMData() { // bool test = mData->music->isPlaying(); return *mData; } - - void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename) { // The range values are not tested @@ -480,7 +491,7 @@ namespace MWSound if(mData->hasFile(filename)) mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 20000, false); else - cout << "Sound file " << filename << " not found, skipping.\n"; + std::cout << "Sound file " << filename << " not found, skipping.\n"; } bool SoundManager::sayDone (MWWorld::Ptr ptr) const @@ -490,20 +501,20 @@ namespace MWSound } - void SoundManager::playSound (const std::string& soundId, float volume, float pitch) + void SoundManager::playSound(const std::string& soundId, float volume, float pitch) { if(!mData) return; // Play and forget float min, max; const std::string &file = mData->lookup(soundId, volume, min, max); - if(file != "") - { + if (file != "") + { SoundPtr snd = mData->mgr->load(file); snd->setVolume(volume); snd->setRange(min,max); snd->setPitch(pitch); snd->play(); - } + } } void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId, @@ -514,7 +525,7 @@ namespace MWSound // Look up the sound in the ESM data float min, max; const std::string &file = mData->lookup(soundId, volume, min, max); - if(file != "") + if (file != "") mData->add(file, ptr, soundId, volume, pitch, min, max, loop); } @@ -541,18 +552,19 @@ namespace MWSound void SoundManager::updateObject(MWWorld::Ptr ptr) { - if(!mData) return; - mData->updatePositions(ptr); + if (mData != NULL) + { + mData->updatePositions(ptr); + } } void SoundManager::update (float duration) { - std::string effect; - MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell(); //If the region has changed - if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){ + if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10) + { timer.restart(); if (test.name != current->cell->region) { @@ -564,11 +576,12 @@ namespace MWSound { std::vector::iterator soundIter = test.soundList.begin(); //mEnvironment.mSoundManager - if(total == 0){ - while (!(soundIter == test.soundList.end())) + if(total == 0) + { + while (soundIter != test.soundList.end()) { - ESM::NAME32 go = soundIter->sound; int chance = (int) soundIter->chance; + //ESM::NAME32 go = soundIter->sound; //std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; soundIter++; total += chance; @@ -578,7 +591,7 @@ namespace MWSound int r = rand() % total; //old random code int pos = 0; soundIter = test.soundList.begin(); - while (!(soundIter == test.soundList.end())) + while (soundIter != test.soundList.end()) { const ESM::NAME32 go = soundIter->sound; int chance = (int) soundIter->chance; @@ -586,13 +599,11 @@ namespace MWSound soundIter++; if( r - pos < chance) { - effect = go.name; //play sound std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; - mEnvironment.mSoundManager->playSound(effect, 20.0, 1.0); + mEnvironment.mSoundManager->playSound(go.name, 20.0, 1.0); break; - } pos += chance; } diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 7dff16c761..5c3f473f2e 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -2,14 +2,15 @@ #define GAME_SOUND_SOUNDMANAGER_H #include -#include #include +#include + #include "../mwworld/ptr.hpp" #include +#include -#include namespace Ogre { @@ -37,7 +38,7 @@ namespace MWSound struct SoundImpl; SoundImpl *mData; - std::vector files; + Files::PathContainer files; bool fsStrict; MWWorld::Environment& mEnvironment; @@ -52,7 +53,7 @@ namespace MWSound public: SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, - boost::filesystem::path dataDir, bool useSound, bool fsstrict, + const Files::PathContainer& dataDir, bool useSound, bool fsstrict, MWWorld::Environment& environment); ~SoundManager(); @@ -61,7 +62,7 @@ namespace MWSound /// \param filename name of a sound file in "Music/" in the data directory. void startRandomTitle(); - void MP3Lookup(boost::filesystem::path dir); + void MP3Lookup(const boost::filesystem::path& dir); bool isMusicPlaying(); diff --git a/components/file_finder/file_finder.hpp b/components/file_finder/file_finder.hpp index 0e1e072263..8a15af73af 100644 --- a/components/file_finder/file_finder.hpp +++ b/components/file_finder/file_finder.hpp @@ -1,9 +1,11 @@ #ifndef FILE_FINDER_MAIN_H #define FILE_FINDER_MAIN_H +#include + #include "search.hpp" #include "filename_less.hpp" -#include +#include namespace FileFinder { @@ -11,7 +13,8 @@ namespace FileFinder template class FileFinderT { - std::map table; + typedef std::map TableContainer; + TableContainer table; struct Inserter : ReturnPath { @@ -35,12 +38,12 @@ public: // Remember the original path length, so we can cut it away from // the relative paths used as keys - std::string pstring = path.string(); + const std::string& pstring = path.string(); inserter.cut = pstring.size(); // If the path does not end in a slash, then boost will add one // later, which means one more character we have to remove. - char last = pstring[pstring.size()-1]; + char last = *pstring.rbegin(); if(last != '\\' && last != '/') inserter.cut++; @@ -56,12 +59,84 @@ public: // Find the full path from a relative path. const std::string &lookup(const std::string& file) const { - return table.find(file)->second; + static std::string empty; + typename TableContainer::const_iterator it = table.find(file); + return (it != table.end()) ? it->second : empty; } }; +template +< + class LESS +> +struct TreeFileFinder +{ + typedef TreeFileFinder finder_t; + + TreeFileFinder(const Files::PathContainer& paths, bool recurse = true) + { + struct : ReturnPath + { + finder_t *owner; + int cut; + + void add(const boost::filesystem::path &pth) + { + std::string file = pth.string(); + std::string key = file.substr(cut); + owner->mTable[key] = file; + } + } inserter; + + inserter.owner = this; + + for (Files::PathContainer::const_iterator it = paths.begin(); it != paths.end(); ++it) + { + + // Remember the original path length, so we can cut it away from + // the relative paths used as keys + const std::string& pstring = it->string(); + inserter.cut = pstring.size(); + + // If the path does not end in a slash, then boost will add one + // later, which means one more character we have to remove. + char last = *pstring.rbegin(); + if (last != '\\' && last != '/') + { + inserter.cut++; + } + + // Fill the map + find(*it, inserter, recurse); + } + } + + bool has(const std::string& file) const + { + return mTable.find(file) != mTable.end(); + } + + const std::string& lookup(const std::string& file) const + { + static std::string empty; + typename TableContainer::const_iterator it = mTable.find(file); + return (it != mTable.end()) ? it->second : empty; + } + + private: + typedef std::map TableContainer; + TableContainer mTable; + +// Inserter inserter; +}; + + // The default is to use path_less for equality checks typedef FileFinderT FileFinder; typedef FileFinderT FileFinderStrict; -} -#endif + +typedef TreeFileFinder LessTreeFileFinder; +typedef TreeFileFinder StrictTreeFileFinder; + +} /* namespace FileFinder */ +#endif /* FILE_FINDER_MAIN_H */ diff --git a/components/file_finder/search.cpp b/components/file_finder/search.cpp index b05b30e835..eb4392abc5 100644 --- a/components/file_finder/search.cpp +++ b/components/file_finder/search.cpp @@ -2,27 +2,27 @@ #include -using namespace std; -using namespace boost::filesystem; - -void FileFinder::find(const path & dir_path, ReturnPath &ret, bool recurse) +void FileFinder::find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse) { - if ( !exists( dir_path ) ) + if ( !boost::filesystem::exists( dir_path ) ) { - cout << "Path " << dir_path << " not found\n"; + std::cout << "Path " << dir_path << " not found" << std::endl; return; } - directory_iterator end_itr; // default construction yields past-the-end - for ( directory_iterator itr(dir_path); - itr != end_itr; - ++itr ) + boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end + for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) { - if ( is_directory( *itr ) ) + if (boost::filesystem::is_directory( *itr )) { - if(recurse) find(*itr, ret); + if (recurse) + { + find(*itr, ret); + } + } + else + { + ret.add(*itr); } - else - ret.add(*itr); } } diff --git a/components/files/multidircollection.hpp b/components/files/multidircollection.hpp index 391f8b6a4e..e8abeb45dc 100644 --- a/components/files/multidircollection.hpp +++ b/components/files/multidircollection.hpp @@ -68,7 +68,7 @@ namespace Files /// \param foldCase Ignore filename case boost::filesystem::path getPath (const std::string& file) const; - ///< Return full path (including filename) of \æ file. + ///< Return full path (including filename) of \a file. /// /// If the file does not exist, an exception is thrown. \a file must include /// the extension. From 8affa30ff1e0357ac04566bd64a40849a44545b6 Mon Sep 17 00:00:00 2001 From: gugus Date: Mon, 6 Feb 2012 23:15:19 +0100 Subject: [PATCH 176/269] magic commit that fixe the crash when activating collision after changing cell. To be faire, this commit should break the collision system... there must be something I don't understand with bullet ghost objects :p --- bullet/physic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 8fa54a9e61..294b379786 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -157,7 +157,7 @@ namespace Physic //TODO: memory leak? btOverlappingPairCache* pairCache = new btSortedOverlappingPairCache(); - pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); + //pairCache->setInternalGhostPairCallback( new btGhostPairCallback() ); broadphase = new btDbvtBroadphase(pairCache); From eea8773be1c752d92e53ea3e07dd07828b4b5210 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Fri, 10 Feb 2012 21:45:46 +0100 Subject: [PATCH 177/269] Changed the order of preference for the configuration files --- apps/launcher/maindialog.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 3bef0b6f9b..dd9f0d653c 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -164,15 +164,15 @@ void MainDialog::createPages() dataDirs = readConfig(file.fileName()); } - // User location - file.setFileName(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); + // Local location + file.setFileName("./openmw.cfg"); + if (file.exists()) { dataDirs = readConfig(file.fileName()); } - // Local location - file.setFileName("./openmw.cfg"); - + // User location + file.setFileName(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); if (file.exists()) { dataDirs = readConfig(file.fileName()); } From bcc4d7a7c98cf03231de39c6d5f185f8c90887a5 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 12 Feb 2012 13:31:19 +0100 Subject: [PATCH 178/269] Issue #133 Handle resources across multiple data directories Signed-off-by: Lukasz Gromanowski --- apps/openmw/engine.cpp | 13 +++++++------ components/file_finder/search.cpp | 32 +++++++++++++++++++------------ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 31c9470548..2a0c23e51a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -197,15 +197,16 @@ OMW::Engine::~Engine() void OMW::Engine::loadBSA() { const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa"); - - for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter) + std::string dataDirectory; + for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter) { std::cout << "Adding " << iter->second.string() << std::endl; - Bsa::addBSA (iter->second.string()); - } + Bsa::addBSA(iter->second.string()); - //std::cout << "Data dir " << mDataDir.string() << std::endl; - //Bsa::addDir(mDataDir.string(), mFSStrict); + dataDirectory = iter->second.parent_path().string(); + std::cout << "Data dir " << dataDirectory << std::endl; + Bsa::addDir(dataDirectory, mFSStrict); + } } // add resources directory diff --git a/components/file_finder/search.cpp b/components/file_finder/search.cpp index eb4392abc5..06deaf83a6 100644 --- a/components/file_finder/search.cpp +++ b/components/file_finder/search.cpp @@ -4,25 +4,33 @@ void FileFinder::find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse) { - if ( !boost::filesystem::exists( dir_path ) ) + if (boost::filesystem::exists(dir_path)) { - std::cout << "Path " << dir_path << " not found" << std::endl; - return; - } - - boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end - for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) - { - if (boost::filesystem::is_directory( *itr )) + if (!recurse) { - if (recurse) + boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end + for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) { - find(*itr, ret); + if (!boost::filesystem::is_directory( *itr )) + { + ret.add(*itr); + } } } else { - ret.add(*itr); + boost::filesystem::recursive_directory_iterator end_itr; // default construction yields past-the-end + for (boost::filesystem::recursive_directory_iterator itr(dir_path); itr != end_itr; ++itr) + { + if (!boost::filesystem::is_directory(*itr)) + { + ret.add(*itr); + } + } } } + else + { + std::cout << "Path " << dir_path << " not found" << std::endl; + } } From 77201d05bc64a052b1a52cd1d92d802c73f0018c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 13 Feb 2012 16:42:01 +0100 Subject: [PATCH 179/269] post-merge fix --- apps/openmw/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 892e7a1881..9e04d1ad00 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -470,7 +470,7 @@ void OMW::Engine::screenshot() // Count screenshots. int shotCount = 0; - const std::string screenshotPath = mCfgMgr.getLocalConfigPath().string(); + const std::string screenshotPath = mCfgMgr.getLocalPath().string(); // Find the first unused filename with a do-while std::ostringstream stream; From 9af08ff18de5403289f053d5da572309d2e5ef03 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 13 Feb 2012 18:34:21 +0100 Subject: [PATCH 180/269] store screenshots in user directory instead of local directory --- apps/openmw/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 9e04d1ad00..2e4beb65f3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -470,7 +470,7 @@ void OMW::Engine::screenshot() // Count screenshots. int shotCount = 0; - const std::string screenshotPath = mCfgMgr.getLocalPath().string(); + const std::string screenshotPath = mCfgMgr.getUserPath().string(); // Find the first unused filename with a do-while std::ostringstream stream; From e1defd359548b071c92fe6ad9b518d7a186d0eac Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 14 Feb 2012 09:25:12 +0100 Subject: [PATCH 181/269] adjusted the globally installed openmw.cfg file --- files/openmw.cfg | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/files/openmw.cfg b/files/openmw.cfg index f5322ae8c8..f76524b33f 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,3 +1,5 @@ -data=${MORROWIND_DATA_FILES} +data=?mw?Data Files +data=?global?data +data=?local?data +data-local=?user?data resources=${MORROWIND_RESOURCE_FILES} - From 8277df04b124cbb8d61e13978f24fe796bd2eeec Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 14 Feb 2012 09:26:06 +0100 Subject: [PATCH 182/269] bumping version number --- CMakeLists.txt | 8 ++++---- readme.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d556ae147c..75d26645ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,8 +18,8 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 11) -set (OPENMW_VERSION_RELEASE 1) +set (OPENMW_VERSION_MINOR 12) +set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -451,7 +451,7 @@ if (APPLE) # some library already has be "fixed up", i.e. its id name contains @executable_path, # but library is not embedded in bundle. For example, it's Ogre.framework from Ogre SDK. # Current implementation of GetPrerequsities/BundleUtilities doesn't handle that case. - # + # # Current limitations: # 1. Handles only frameworks, not simple libs INSTALL(CODE " @@ -492,4 +492,4 @@ include(CPack) set(CMAKE_EXE_LINKER_FLAGS "-arch i386") set(CMAKE_CXX_FLAGS "-arch i386") -endif (APPLE) \ No newline at end of file +endif (APPLE) diff --git a/readme.txt b/readme.txt index cfddd9a1fa..a02374b9b1 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.11.1 +Version: 0.12.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 7b457c05bc13b93e96e04b37796d712d6fe2f4c7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 14 Feb 2012 09:39:58 +0100 Subject: [PATCH 183/269] updated readme --- readme.txt | 109 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 10 deletions(-) diff --git a/readme.txt b/readme.txt index a02374b9b1..47dd6d3fd4 100644 --- a/readme.txt +++ b/readme.txt @@ -14,7 +14,7 @@ THIS IS A WORK IN PROGRESS INSTALLATION Windows: -Just unpack to a location of your choice. Currently there is no installer. +Run the installer. Linux: Ubuntu (and most others) @@ -35,25 +35,87 @@ TODO add description here THE DATA PATH -After the installation OpenMW needs to be told where to find the Morrowind data directory. Create a text file named openmw.cfg (location depends on platform) and enter the following line: +The data path tells OpenMW where to find your Morrowind files. From 0.12.0 on OpenMW should be able to +pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly +(installing Morrowind under WINE is considered a proper install). + +If that does not work for you, please check if you have any leftover openmw.cfg files from versions earlier than 0.12.0. These can interfere with the configuration process, so try to remove then. + +If you are running OpenMW without installing it, you still need to manually adjust the data path. Create a text file named openmw.cfg in the location of the binary and enter the following line: data=path to your data directory (where you replace "path to your data directory" with the actual location of your data directory) -On Windows a suitable location for the cfg file is alongside the binary. Currently the binary release comes with such a file pre-generated, but you still need to adjust the data setting. - -On Linux and Mac the default location will be ~/.config/openmw/openmw.cfg. - COMMAND LINE OPTIONS -TODO add description of command line options + +Syntax: openmw +Allowed options: + --help print help message + --version print version information and quit + --data arg (=data) set data directories (later directories have + higher priority) + --data-local arg set local data directory (highest priority) + --resources arg (=resources) set resources directory + --start arg (=Beshara) set initial cell + --master arg master file(s) + --plugin arg plugin file(s) + --fps [=arg(=1)] (=0) fps counter detail (0 = off, 1 = fps counter + , 2 = full detail) + --anim-verbose [=arg(=1)] (=0) output animation indices files + --debug [=arg(=1)] (=0) debug mode + --nosound [=arg(=1)] (=0) disable all sounds + --script-verbose [=arg(=1)] (=0) verbose script output + --new-game [=arg(=1)] (=0) activate char gen/new game mechanics + --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scri + pts) at startup + --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding + ) + --encoding arg (=win1252) Character encoding used in OpenMW game messa + ges: + + win1250 - Central and Eastern European such + as Polish, Czech, Slovak, Hungarian, Slovene + , Bosnian, Croatian, Serbian (Latin script), + Romanian and Albanian languages + + win1251 - Cyrillic alphabet such as Russian, + Bulgarian, Serbian Cyrillic and other langua + ges + + win1252 - Western European (Latin) alphabet, + used by default + --report-focus [=arg(=1)] (=0) write name of focussed object to cout CREDITS -Developers: -TODO add list of developers +Current Developers: +Alexander “Ace” Olofsson +athile +Cris “Mirceam” Mihalache +gugus / gus +Jacob “Yacoby” Essex +Jason “jhooks” Hooks +Lukasz “lgro” Gromanowski +Marc “Zini” Zinnschlag +Nikolay “corristo” Kasyanov +Pieter “pvdk” van der Kloet +Sebastian “swick” Wick + +Retired Developers: +Ardekantur +Armin Preiml +Diggory Hardy +Jan Borsodi +Jan-Peter “peppe” Nilsson +Josua Grawitter +Karl-Felix “k1ll” Glatzer +Nicolay Korslund +sergoz +Star-Demon +Yuri Krupenin OpenMW: Thanks to DokterDume for kindly providing us with the Moon and Star logo used as the application icon and project logo. @@ -64,6 +126,33 @@ Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Fil CHANGELOG +0.12.0 + +Bug #154: FPS Drop +Bug #169: Local scripts continue running if associated object is deleted +Bug #174: OpenMW fails to start if the config directory doesn't exist +Bug #187: Missing lighting +Bug #188: Lights without a mesh are not rendered +Bug #191: Taking screenshot causes crash when running installed +Feature #28: Sort out the cell load problem +Feature #31: Allow the player to move away from pre-defined cells +Feature #35: Use alternate storage location for modified object position +Feature #45: NPC animations +Feature #46: Creature Animation +Feature #89: Basic Journal Window +Feature #110: Automatically pick up the path of existing MW-installations +Feature #133: Handle resources across multiple data directories +Feature #134: Generate a suitable default-value for --data-local +Feature #183: More FPS display settings +Task #19: Refactor engine class +Task #109/Feature #162: Automate Packaging +Task #112: Catch exceptions thrown in input handling functions +Task #128/#168: Cleanup Configuration File Handling +Task #131: NPC Activation doesn't work properly +Task #144: MWRender cleanup +Task #155: cmake cleanup + + 0.11.1 Bug #2: Resources loading doesn't work outside of bsa files @@ -90,4 +179,4 @@ Task #14: Replace tabs with 4 spaces Task #18: Move components from global namespace into their own namespace Task #123: refactor header files in components/esm -TODO add old changelog (take pre 0.11.0 changelog from wiki) +TODO add old changelog (take pre 0.11.1 changelog from wiki) From 57a4b198806ba305822e3404cad9bd387918b654 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Wed, 15 Feb 2012 11:27:51 +0400 Subject: [PATCH 184/269] Feature #162 - Need to create app bundle using CMake, not by hand. Almost complete --- CMakeLists.txt | 14 +++++--------- apps/launcher/CMakeLists.txt | 32 +++++++++++++++++--------------- apps/launcher/main.cpp | 9 +++++++++ 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef31dae076..fb58b01248 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -384,9 +384,6 @@ endif() if (APPLE) set(INSTALL_SUBDIR OpenMW) - #install(FILES ${MISC_FILES} DESTINATION ../MacOS) - #install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Plugins" DESTINATION ..) - #install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Resources/resources" DESTINATION ../Resources) install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) @@ -394,9 +391,6 @@ if (APPLE) install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") - # set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/files/mac/Info.plist") - # set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw.icns") - # set(CPACK_BUNDLE_NAME "OpenMW") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) @@ -406,11 +400,13 @@ if (APPLE) set(PLUGINS "") # Scan Plugins dir for *.dylibs - file(GLOB ALL_PLUGINS "${APP_BUNDLE_DIR}/Contents/Plugins/*.dylib") + set(PLUGIN_SEARCH_ROOT "${APP_BUNDLE_DIR}/Contents/Plugins") + file(GLOB_RECURSE ALL_PLUGINS "${PLUGIN_SEARCH_ROOT}/*.dylib") + set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins") foreach(PLUGIN ${ALL_PLUGINS}) - get_filename_component(PLUGIN_FILENAME ${PLUGIN} NAME) - set(PLUGINS ${PLUGINS} "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins/${PLUGIN_FILENAME}") + string(REPLACE "${PLUGIN_SEARCH_ROOT}/" "" PLUGIN_RELATIVE "${PLUGIN}") + set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") endforeach() #For now, search unresolved dependencies only in default system paths, so if you put unresolveable (i.e. with @executable_path in id name) lib or framework somewhere else, it would fail diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index e46a062055..2879539a75 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -41,8 +41,10 @@ source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC find_package(Qt4 REQUIRED) set(QT_USE_QTGUI 1) -#find_package(PNG REQUIRED) -#include_directories(${PNG_INCLUDE_DIR}) +if (NOT APPLE) # this dependency can be completely removed, but now it only tested on OS X + find_package(PNG REQUIRED) + include_directories(${PNG_INCLUDE_DIR}) +endif() QT4_ADD_RESOURCES(RCC_SRCS resources.qrc) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) @@ -51,14 +53,14 @@ include(${QT_USE_FILE}) # list here plugins that can't be detected statically, but loaded in runtime # it needed for packaging automatisation -#set(USED_QT_PLUGINS imageformats/libqgif -# imageformats/libqico -# imageformats/libqjpeg -# imageformats/libqmng -# imageformats/libqsvg -# imageformats/libqtga -# imageformats/libqtiff) -# It seems that launcher works without this plugins, but it loads them into memory if they exists +set(USED_QT_PLUGINS imageformats/libqgif + imageformats/libqico + imageformats/libqjpeg + imageformats/libqmng + imageformats/libqsvg + imageformats/libqtga + imageformats/libqtiff) +# It seems that launcher works without this plugins, but it loads them if they exists # Main executable add_executable(omwlauncher @@ -71,7 +73,7 @@ target_link_libraries(omwlauncher ${Boost_LIBRARIES} ${OGRE_LIBRARIES} ${QT_LIBRARIES} -# ${PNG_LIBRARY} + ${PNG_LIBRARY} components ) @@ -86,10 +88,10 @@ if (APPLE) "${APP_BUNDLE_DIR}/../launcher.cfg") # copy used QT plugins into ${APP_BUNDLE_DIR}/Contents/Plugins - #foreach(PLUGIN ${USED_QT_PLUGINS}) - # get_filename_component(PLUGIN_FILENAME ${PLUGIN} NAME) - # configure_file("${QT_PLUGINS_DIR}/${PLUGIN}.dylib" "${APP_BUNDLE_DIR}/Contents/Plugins/${PLUGIN_FILENAME}.dylib" COPYONLY) - #endforeach() + foreach(PLUGIN ${USED_QT_PLUGINS}) + get_filename_component(PLUGIN_FILENAME ${PLUGIN} NAME) + configure_file("${QT_PLUGINS_DIR}/${PLUGIN}.dylib" "${APP_BUNDLE_DIR}/Contents/Plugins/${PLUGIN}.dylib" COPYONLY) + endforeach() else() configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 4e438a4db1..93d23371e1 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "maindialog.hpp" @@ -17,6 +18,14 @@ int main(int argc, char *argv[]) dir.cdUp(); dir.cdUp(); } + + QDir pluginsPath(QCoreApplication::applicationDirPath()); + pluginsPath.cdUp(); + pluginsPath.cd("Plugins"); + + QStringList libraryPaths; + libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); + app.setLibraryPaths(libraryPaths); #endif QDir::setCurrent(dir.absolutePath()); From 2020d916ec482ae592b195fec18db4f6c0745589 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Wed, 15 Feb 2012 11:30:35 +0400 Subject: [PATCH 185/269] added missing comment --- apps/launcher/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index 93d23371e1..f108882a3f 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -19,6 +19,7 @@ int main(int argc, char *argv[]) dir.cdUp(); } + // force Qt to load only LOCAL plugins, don't touch system Qt installation QDir pluginsPath(QCoreApplication::applicationDirPath()); pluginsPath.cdUp(); pluginsPath.cd("Plugins"); From 80008ed09f516aed4f68efbf253ab6f716c13820 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 15 Feb 2012 22:34:51 +0100 Subject: [PATCH 186/269] Issue #168 - Configuration cleanup Fixed bug with configuration tokens parsing - when something appear after token it should be appended to replaced path. Signed-off-by: Lukasz Gromanowski --- components/files/configurationmanager.cpp | 34 +++++++++++++++-------- components/files/fixedpath.hpp | 1 + 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 28d49a50c2..46cd0e053b 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -89,27 +89,37 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) const std::string& path = it->string(); // Check if path contains a token - if (!path.empty() && *path.begin() == '?' && *path.rbegin() == '?') + if (!path.empty() && *path.begin() == '?') { - TokensMappingContainer::iterator tokenIt = mTokensMapping.find(path); - if (tokenIt != mTokensMapping.end()) + std::string::size_type pos = path.find('?', 1); + if (pos != std::string::npos && pos != 0) { - boost::filesystem::path tempPath(((mFixedPath).*(tokenIt->second))()); - - if (boost::filesystem::is_directory(tempPath)) + TokensMappingContainer::iterator tokenIt = mTokensMapping.find(path.substr(0, pos + 1)); + if (tokenIt != mTokensMapping.end()) { - (*it) = tempPath; + boost::filesystem::path tempPath(((mFixedPath).*(tokenIt->second))()); + if (pos < path.length() - 1) + { + // There is something after the token, so we should + // append it to the path + tempPath /= path.substr(pos + 1, path.length() - pos); + } + + if (boost::filesystem::is_directory(tempPath)) + { + (*it) = tempPath; + } + else + { + (*it).clear(); + } } else { + // Clean invalid / unknown token, it will be removed outside the loop (*it).clear(); } } - else - { - // Clean invalid / unknown token, it will be removed outside the loop - (*it).clear(); - } } } diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 1bf582ab9c..0e052fd248 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -86,6 +86,7 @@ struct FixedPath mGlobalPath /= suffix; mLocalDataPath /= suffix; + mUserDataPath /= suffix; mGlobalDataPath /= suffix; } } From 8244ed58605fc91ac920f1436f3e6c77b340c91e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Feb 2012 09:26:27 +0100 Subject: [PATCH 187/269] workaround for space in paths problem --- files/openmw.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/files/openmw.cfg b/files/openmw.cfg index f76524b33f..950332cc1c 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,5 +1,5 @@ -data=?mw?Data Files -data=?global?data -data=?local?data -data-local=?user?data +data="?mw?Data Files" +data="?global?data" +data="?local?data" +data-"local=?user?data" resources=${MORROWIND_RESOURCE_FILES} From 850501e9221daf1a55d31b72c4389da158b338e6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Feb 2012 13:14:16 +0100 Subject: [PATCH 188/269] workaround for the configuration problems --- components/files/configurationmanager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 46cd0e053b..c6e5534180 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -56,9 +56,9 @@ ConfigurationManager::~ConfigurationManager() void ConfigurationManager::setupTokensMapping() { mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); - mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalDataPath)); - mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserDataPath)); - mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); + mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); + mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserPath)); + mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, From 8186445de9e2bcb6966c5ef86583493c4225a3b6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 16 Feb 2012 21:06:25 +0100 Subject: [PATCH 189/269] fix for a very silly bug --- files/openmw.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/openmw.cfg b/files/openmw.cfg index 950332cc1c..585b735f30 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,5 +1,5 @@ data="?mw?Data Files" data="?global?data" data="?local?data" -data-"local=?user?data" +data-local="?user?data" resources=${MORROWIND_RESOURCE_FILES} From b77f5c06cc3fd818a5efbbb42d6c2a079fa91143 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 18 Feb 2012 18:25:02 +0100 Subject: [PATCH 190/269] added facilities for fading the screen in/out --- ogre/fader.cpp | 104 ++++++++++++++++++++++++++++++++++++++++++++++ ogre/fader.hpp | 54 ++++++++++++++++++++++++ ogre/renderer.cpp | 10 +++++ ogre/renderer.hpp | 7 ++++ 4 files changed, 175 insertions(+) create mode 100644 ogre/fader.cpp create mode 100644 ogre/fader.hpp diff --git a/ogre/fader.cpp b/ogre/fader.cpp new file mode 100644 index 0000000000..6e15d91b71 --- /dev/null +++ b/ogre/fader.cpp @@ -0,0 +1,104 @@ +#include "fader.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FADE_OVERLAY_NAME "FadeInOutOverlay" +#define FADE_OVERLAY_PANEL_NAME "FadeInOutOverlayPanel" +#define FADE_MATERIAL_NAME "FadeInOutMaterial" + +using namespace Ogre; +using namespace OEngine::Render; + +Fader::Fader() : + mMode(FadingMode_None), + mRemainingTime(0.f), + mTargetTime(0.f), + mTargetAlpha(0.f) +{ + + // Create the fading material + MaterialPtr material = MaterialManager::getSingleton().create( FADE_MATERIAL_NAME, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME ); + Pass* pass = material->getTechnique(0)->getPass(0); + pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); + mFadeTextureUnit = pass->createTextureUnitState(); + mFadeTextureUnit->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, ColourValue(0.f, 0.f, 0.f)); // always black colour + + // Create the overlay + OverlayManager& ovm = OverlayManager::getSingleton(); + + mOverlay = ovm.create( FADE_OVERLAY_NAME ); + + OverlayContainer* overlay_panel; + overlay_panel = (OverlayContainer*)ovm.createOverlayElement("Panel", FADE_OVERLAY_PANEL_NAME); + + // position it over the whole screen + overlay_panel->_setPosition(0, 0); + overlay_panel->_setDimensions(1, 1); + + overlay_panel->setMaterialName( FADE_MATERIAL_NAME ); + overlay_panel->show(); + mOverlay->add2D(overlay_panel); + mOverlay->hide(); +} + +void Fader::update(float dt) +{ + if (mMode == FadingMode_None) return; + + if (mRemainingTime > 0) + { + mOverlay->show(); + float alpha; + if (mMode == FadingMode_In) + alpha = (mRemainingTime/mTargetTime) * 1.f; + else if (mMode == FadingMode_Out) + alpha = (1-(mRemainingTime/mTargetTime)) * 1.f; + else if (mMode == FadingMode_To) + alpha = (1-(mRemainingTime/mTargetTime)) * mTargetAlpha; + + mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, alpha); + + mRemainingTime -= dt; + } + else + { + mMode = FadingMode_None; + mOverlay->hide(); + } +} + +void Fader::fadeIn(float time) +{ + if (time<=0) return; + + mMode = FadingMode_In; + mTargetTime = time; + mRemainingTime = time; +} + +void Fader::fadeOut(const float time) +{ + if (time<=0) return; + + mMode = FadingMode_Out; + mTargetTime = time; + mRemainingTime = time; +} + +void Fader::fadeTo(const int percent, const float time) +{ + if (time<=0) return; + + mMode = FadingMode_To; + mTargetTime = time; + mRemainingTime = time; + mTargetAlpha = percent/100.f; +} diff --git a/ogre/fader.hpp b/ogre/fader.hpp new file mode 100644 index 0000000000..0bc96acfab --- /dev/null +++ b/ogre/fader.hpp @@ -0,0 +1,54 @@ +#ifndef OENGINE_OGRE_FADE_H +#define OENGINE_OGRE_FADE_H + +/* + A class that handles fading in the screen from black or fading it out to black. + + To achieve this, it uses a full-screen Ogre::Overlay + + inspired by http://www.ogre3d.org/tikiwiki/FadeEffectOverlay (heavily adjusted) + */ + +#include + +namespace Ogre +{ + class TextureUnitState; + class Overlay; +} + +namespace OEngine { +namespace Render +{ + class Fader + { + public: + Fader(); + + void update(float dt); + + void fadeIn(const float time); + void fadeOut(const float time); + void fadeTo(const int percent, const float time); + + private: + enum FadingMode + { + FadingMode_In, + FadingMode_Out, + FadingMode_To, + FadingMode_None // no fading + }; + + Ogre::TextureUnitState* mFadeTextureUnit; + Ogre::Overlay* mOverlay; + + FadingMode mMode; + float mRemainingTime; + float mTargetTime; + float mTargetAlpha; + + protected: + }; +}} +#endif diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index ca263d8152..1ce1d26096 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -1,4 +1,5 @@ #include "renderer.hpp" +#include "fader.hpp" #include "OgreRoot.h" #include "OgreRenderWindow.h" @@ -12,6 +13,8 @@ using namespace OEngine::Render; void OgreRenderer::cleanup() { + delete mFader; + if(mRoot) delete mRoot; mRoot = NULL; @@ -22,6 +25,11 @@ void OgreRenderer::start() mRoot->startRendering(); } +void OgreRenderer::update(float dt) +{ + mFader->update(dt); +} + void OgreRenderer::screenshot(const std::string &file) { mWindow->writeContentsToFile(file); @@ -98,4 +106,6 @@ void OgreRenderer::createScene(const std::string camName, float fov, float nearC // Alter the camera aspect ratio to match the viewport mCamera->setAspectRatio(Real(mView->getActualWidth()) / Real(mView->getActualHeight())); + + mFader = new Fader(); } diff --git a/ogre/renderer.hpp b/ogre/renderer.hpp index 982534e6b7..0c2f0d3d17 100644 --- a/ogre/renderer.hpp +++ b/ogre/renderer.hpp @@ -19,6 +19,7 @@ namespace Ogre namespace OEngine { namespace Render { + class Fader; class OgreRenderer { Ogre::Root *mRoot; @@ -26,6 +27,7 @@ namespace Render Ogre::SceneManager *mScene; Ogre::Camera *mCamera; Ogre::Viewport *mView; + Fader* mFader; bool logging; public: @@ -66,6 +68,8 @@ namespace Render /// Start the main rendering loop void start(); + + void update(float dt); /// Write a screenshot to file void screenshot(const std::string &file); @@ -80,6 +84,9 @@ namespace Render /// Get the scene manager Ogre::SceneManager *getScene() { return mScene; } + + /// Get the screen colour fader + Fader *getFader() { return mFader; } /// Camera Ogre::Camera *getCamera() { return mCamera; } From eb61ba59e64c332931c12251cac40de5836ff9cd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 19 Feb 2012 17:45:57 +0100 Subject: [PATCH 191/269] updated the fading calculation (the alpha value stays now, even after the specified time - this is how it is in morrowind) --- ogre/fader.cpp | 82 +++++++++++++++++++++++++++++++++++--------------- ogre/fader.hpp | 11 ++++--- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/ogre/fader.cpp b/ogre/fader.cpp index 6e15d91b71..062559e000 100644 --- a/ogre/fader.cpp +++ b/ogre/fader.cpp @@ -18,10 +18,12 @@ using namespace Ogre; using namespace OEngine::Render; Fader::Fader() : - mMode(FadingMode_None), + mMode(FadingMode_In), mRemainingTime(0.f), mTargetTime(0.f), - mTargetAlpha(0.f) + mTargetAlpha(0.f), + mCurrentAlpha(0.f), + mStartAlpha(0.f) { // Create the fading material @@ -50,35 +52,46 @@ Fader::Fader() : } void Fader::update(float dt) -{ - if (mMode == FadingMode_None) return; - +{ if (mRemainingTime > 0) { - mOverlay->show(); - float alpha; if (mMode == FadingMode_In) - alpha = (mRemainingTime/mTargetTime) * 1.f; + { + mCurrentAlpha -= dt/mTargetTime * (mStartAlpha-mTargetAlpha); + if (mCurrentAlpha < mTargetAlpha) mCurrentAlpha = mTargetAlpha; + } else if (mMode == FadingMode_Out) - alpha = (1-(mRemainingTime/mTargetTime)) * 1.f; - else if (mMode == FadingMode_To) - alpha = (1-(mRemainingTime/mTargetTime)) * mTargetAlpha; - - mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, alpha); + { + mCurrentAlpha += dt/mTargetTime * (mTargetAlpha-mStartAlpha); + if (mCurrentAlpha > mTargetAlpha) mCurrentAlpha = mTargetAlpha; + } + + applyAlpha(); mRemainingTime -= dt; } - else - { - mMode = FadingMode_None; - mOverlay->hide(); - } + + if (mCurrentAlpha == 0.f) mOverlay->hide(); +} + +void Fader::applyAlpha() +{ + mOverlay->show(); + mFadeTextureUnit->setAlphaOperation(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, mCurrentAlpha); } void Fader::fadeIn(float time) { - if (time<=0) return; - + if (time<0.f) return; + if (time==0.f) + { + mCurrentAlpha = 0.f; + applyAlpha(); + return; + } + + mStartAlpha = mCurrentAlpha; + mTargetAlpha = 0.f; mMode = FadingMode_In; mTargetTime = time; mRemainingTime = time; @@ -86,8 +99,16 @@ void Fader::fadeIn(float time) void Fader::fadeOut(const float time) { - if (time<=0) return; - + if (time<0.f) return; + if (time==0.f) + { + mCurrentAlpha = 1.f; + applyAlpha(); + return; + } + + mStartAlpha = mCurrentAlpha; + mTargetAlpha = 1.f; mMode = FadingMode_Out; mTargetTime = time; mRemainingTime = time; @@ -95,10 +116,21 @@ void Fader::fadeOut(const float time) void Fader::fadeTo(const int percent, const float time) { - if (time<=0) return; + if (time<0.f) return; + if (time==0.f) + { + mCurrentAlpha = percent/100.f; + applyAlpha(); + return; + } + + mStartAlpha = mCurrentAlpha; + mTargetAlpha = percent/100.f; + + if (mTargetAlpha == mStartAlpha) return; + else if (mTargetAlpha > mStartAlpha) mMode = FadingMode_Out; + else mMode = FadingMode_In; - mMode = FadingMode_To; mTargetTime = time; mRemainingTime = time; - mTargetAlpha = percent/100.f; } diff --git a/ogre/fader.hpp b/ogre/fader.hpp index 0bc96acfab..f76ac51ef9 100644 --- a/ogre/fader.hpp +++ b/ogre/fader.hpp @@ -30,23 +30,26 @@ namespace Render void fadeIn(const float time); void fadeOut(const float time); void fadeTo(const int percent, const float time); - + private: enum FadingMode { FadingMode_In, - FadingMode_Out, - FadingMode_To, - FadingMode_None // no fading + FadingMode_Out }; + + void applyAlpha(); Ogre::TextureUnitState* mFadeTextureUnit; Ogre::Overlay* mOverlay; FadingMode mMode; + float mRemainingTime; float mTargetTime; float mTargetAlpha; + float mCurrentAlpha; + float mStartAlpha; protected: }; From 0c0b5940908c2ea385a9564fd02431d27792bd85 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 19 Feb 2012 23:39:37 +0100 Subject: [PATCH 192/269] Issue #168 - Configuration cleanup Removed unnecessary path methods - according to forum disscusion: http://openmw.org/forum/viewtopic.php?f=6&t=448 Signed-off-by: Lukasz Gromanowski --- apps/launcher/datafilespage.cpp | 2 + apps/openmw/main.cpp | 2 +- components/files/configurationmanager.cpp | 56 ++++--------- components/files/fixedpath.hpp | 20 +---- components/files/linuxpath.cpp | 97 +++-------------------- components/files/linuxpath.hpp | 14 ---- components/files/macospath.cpp | 44 +--------- components/files/macospath.hpp | 7 ++ components/files/windowspath.cpp | 26 ++---- components/files/windowspath.hpp | 18 +---- 10 files changed, 50 insertions(+), 236 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8b59f1b819..6eb4590373 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -135,6 +135,8 @@ void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) dataDirs.push_back(boost::filesystem::path(currentPath.toStdString())); } + mCfgMgr.processPaths(dataDirs); + // Create a file collection for the dataDirs Files::Collections mFileCollections(dataDirs, strict); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 9f70fac15e..6bdfdd91f0 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -167,7 +167,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat if (dataDirs.empty()) { - dataDirs.push_back(cfgMgr.getLocalDataPath()); + dataDirs.push_back(cfgMgr.getLocalPath()); } engine.setDataDirs(dataDirs); diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 46cd0e053b..c5561d6527 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -5,6 +5,11 @@ #include #include +#include + +/** + * \namespace Files + */ namespace Files { @@ -22,10 +27,6 @@ ConfigurationManager::ConfigurationManager() { setupTokensMapping(); - /** - * According to task #168 plugins.cfg file shall be located in global - * configuration path or in local configuration path. - */ mPluginsCfgPath = mFixedPath.getGlobalPath() / pluginsCfgFile; if (!boost::filesystem::is_regular_file(mPluginsCfgPath)) { @@ -37,15 +38,7 @@ ConfigurationManager::ConfigurationManager() } } - /** - * According to task #168 ogre.cfg file shall be located only - * in user configuration path. - */ mOgreCfgPath = mFixedPath.getUserPath() / ogreCfgFile; - - /** - * FIXME: Logs shoudn't be stored in the same dir where configuration is placed. - */ mLogPath = mFixedPath.getUserPath(); } @@ -56,8 +49,8 @@ ConfigurationManager::~ConfigurationManager() void ConfigurationManager::setupTokensMapping() { mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); - mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalDataPath)); - mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserDataPath)); + mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); + mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserPath)); mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } @@ -74,14 +67,6 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m } -struct EmptyPath : public std::unary_function -{ - bool operator()(const boost::filesystem::path& path) const - { - return path.empty(); - } -}; - void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) { for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) @@ -105,14 +90,7 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) tempPath /= path.substr(pos + 1, path.length() - pos); } - if (boost::filesystem::is_directory(tempPath)) - { - (*it) = tempPath; - } - else - { - (*it).clear(); - } + *it = tempPath; } else { @@ -121,9 +99,15 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) } } } + + if (!boost::filesystem::is_directory(*it)) + { + (*it).clear(); + } } - dataDirs.erase(std::remove_if(dataDirs.begin(), dataDirs.end(), EmptyPath()), dataDirs.end()); + dataDirs.erase(std::remove_if(dataDirs.begin(), dataDirs.end(), + boost::bind(&boost::filesystem::path::empty, _1)), dataDirs.end()); } void ConfigurationManager::loadConfig(const boost::filesystem::path& path, @@ -171,16 +155,6 @@ const boost::filesystem::path& ConfigurationManager::getGlobalDataPath() const return mFixedPath.getGlobalDataPath(); } -const boost::filesystem::path& ConfigurationManager::getUserDataPath() const -{ - return mFixedPath.getUserDataPath(); -} - -const boost::filesystem::path& ConfigurationManager::getLocalDataPath() const -{ - return mFixedPath.getLocalDataPath(); -} - const boost::filesystem::path& ConfigurationManager::getInstallPath() const { return mFixedPath.getInstallPath(); diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 0e052fd248..4e054b17ff 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -73,9 +73,7 @@ struct FixedPath , mUserPath(mPath.getUserPath()) , mGlobalPath(mPath.getGlobalPath()) , mLocalPath(mPath.getLocalPath()) - , mUserDataPath(mPath.getUserDataPath()) , mGlobalDataPath(mPath.getGlobalDataPath()) - , mLocalDataPath(mPath.getLocalDataPath()) , mInstallPath(mPath.getInstallPath()) { if (!application_name.empty()) @@ -84,9 +82,6 @@ struct FixedPath mUserPath /= suffix; mGlobalPath /= suffix; - - mLocalDataPath /= suffix; - mUserDataPath /= suffix; mGlobalDataPath /= suffix; } } @@ -131,16 +126,6 @@ struct FixedPath return mGlobalDataPath; } - const boost::filesystem::path& getUserDataPath() const - { - return mUserDataPath; - } - - const boost::filesystem::path& getLocalDataPath() const - { - return mLocalDataPath; - } - private: PathType mPath; @@ -148,11 +133,8 @@ struct FixedPath boost::filesystem::path mGlobalPath; /**< Global path */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ - boost::filesystem::path mUserDataPath; /**< User data path */ boost::filesystem::path mGlobalDataPath; /**< Global application data path */ - boost::filesystem::path mLocalDataPath; /**< Local path to the configuration files. - By default it is a 'data' directory in same - directory where application was run */ + boost::filesystem::path mInstallPath; }; diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 41891661ef..0b315ccfb2 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -41,29 +41,19 @@ boost::filesystem::path LinuxPath::getUserPath() const boost::filesystem::path userPath("."); boost::filesystem::path suffix("/"); - const char* theDir = getenv("OPENMW_CONFIG"); + const char* theDir = getenv("HOME"); if (theDir == NULL) { - theDir = getenv("XDG_CONFIG_HOME"); - if (theDir == NULL) + struct passwd* pwd = getpwuid(getuid()); + if (pwd != NULL) { - theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - suffix = boost::filesystem::path("/.config/"); - } + theDir = pwd->pw_dir; } } - if (theDir != NULL) { + if (theDir != NULL) + { + suffix = boost::filesystem::path("/.config/"); userPath = boost::filesystem::path(theDir); } @@ -74,20 +64,7 @@ boost::filesystem::path LinuxPath::getUserPath() const boost::filesystem::path LinuxPath::getGlobalPath() const { - boost::filesystem::path globalPath("/etc/xdg/"); - - char* theDir = getenv("XDG_CONFIG_DIRS"); - if (theDir != NULL) - { - // We take only first path from list - char* ptr = strtok(theDir, ":"); - if (ptr != NULL) - { - globalPath = boost::filesystem::path(ptr); - globalPath /= boost::filesystem::path("/"); - } - } - + boost::filesystem::path globalPath("/etc/"); return globalPath; } @@ -96,65 +73,12 @@ boost::filesystem::path LinuxPath::getLocalPath() const return boost::filesystem::path("./"); } -boost::filesystem::path LinuxPath::getUserDataPath() const -{ - boost::filesystem::path localDataPath("."); - boost::filesystem::path suffix("/"); - - const char* theDir = getenv("OPENMW_DATA"); - if (theDir == NULL) - { - theDir = getenv("XDG_DATA_HOME"); - if (theDir == NULL) - { - theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - suffix = boost::filesystem::path("/.local/share/"); - } - } - } - - if (theDir != NULL) { - localDataPath = boost::filesystem::path(theDir); - } - - localDataPath /= suffix; - return localDataPath; -} - boost::filesystem::path LinuxPath::getGlobalDataPath() const { - boost::filesystem::path globalDataPath("/usr/local/share/"); - - char* theDir = getenv("XDG_DATA_DIRS"); - if (theDir != NULL) - { - // We take only first path from list - char* ptr = strtok(theDir, ":"); - if (ptr != NULL) - { - globalDataPath = boost::filesystem::path(ptr); - globalDataPath /= boost::filesystem::path("/"); - } - } - + boost::filesystem::path globalDataPath("/usr/share/games/"); return globalDataPath; } -boost::filesystem::path LinuxPath::getLocalDataPath() const -{ - return boost::filesystem::path("./data/"); -} - boost::filesystem::path LinuxPath::getInstallPath() const { boost::filesystem::path installPath; @@ -211,7 +135,8 @@ boost::filesystem::path LinuxPath::getInstallPath() const if (!mwpath.empty()) { - // Change drive letter to lowercase, so we could use ~/.wine/dosdevice symlinks + // Change drive letter to lowercase, so we could use + // ~/.wine/dosdevices symlinks mwpath[0] = tolower(mwpath[0]); installPath /= homePath; installPath /= ".wine/dosdevices/"; diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 71f45ae32b..873f8801de 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -60,13 +60,6 @@ struct LinuxPath */ boost::filesystem::path getLocalPath() const; - /** - * \brief - * - * \return boost::filesystem::path - */ - boost::filesystem::path getUserDataPath() const; - /** * \brief * @@ -74,13 +67,6 @@ struct LinuxPath */ boost::filesystem::path getGlobalDataPath() const; - /** - * \brief - * - * \return boost::filesystem::path - */ - boost::filesystem::path getLocalDataPath() const; - /** * \brief Gets the path of the installed Morrowind version if there is one. * diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 789c877099..6225fc01f4 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -28,6 +28,10 @@ #include #include +/** + * FIXME: Someone with MacOS system should check this and correct if necessary + */ + /** * \namespace Files */ @@ -69,52 +73,12 @@ boost::filesystem::path MacOsPath::getLocalPath() const return boost::filesystem::path("./"); } -boost::filesystem::path MacOsPath::getUserDataPath() const -{ - boost::filesystem::path localDataPath("."); - boost::filesystem::path suffix("/"); - - const char* theDir = getenv("OPENMW_DATA"); - if (theDir == NULL) - { - theDir = getenv("HOME"); - if (theDir == NULL) - { - struct passwd* pwd = getpwuid(getuid()); - if (pwd != NULL) - { - theDir = pwd->pw_dir; - } - } - if (theDir != NULL) - { - suffix = boost::filesystem::path("/Library/Application Support/"); - } - } - - if (theDir != NULL) - { - localDataPath = boost::filesystem::path(theDir); - } - - localDataPath /= suffix; - return localDataPath; -} - boost::filesystem::path MacOsPath::getGlobalDataPath() const { boost::filesystem::path globalDataPath("/Library/Application Support/"); return globalDataPath; } -boost::filesystem::path MacOsPath::getLocalDataPath() const -{ - return boost::filesystem::path("./data/"); -} - -/** - * FIXME: This should be verified on MacOS system! - */ boost::filesystem::path MacOsPath::getInstallPath() const { boost::filesystem::path installPath; diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 2194dbb94a..7656538c19 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -60,6 +60,13 @@ struct MacOsPath */ boost::filesystem::path getLocalPath() const; + /** + * \brief + * + * \return boost::filesystem::path + */ + boost::filesystem::path getGlobalDataPath() const; + boost::filesystem::path getInstallPath() const; }; diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index 9bb66a3cb3..e810412720 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -10,6 +10,13 @@ #pragma comment(lib, "Shlwapi.lib") +/** + * FIXME: Someone with Windows system should check this and correct if necessary + */ + +/** + * \namespace Files + */ namespace Files { @@ -55,30 +62,11 @@ boost::filesystem::path WindowsPath::getLocalPath() const return boost::filesystem::path("./"); } -/** - * FIXME: Someone with Windows system should check this and correct if necessary - */ -boost::filesystem::path WindowsPath::getUserDataPath() const -{ - return getUserConfigPath(); -} - -/** - * FIXME: Someone with Windows system should check this and correct if necessary - */ boost::filesystem::path WindowsPath::getGlobalDataPath() const { return getGlobalConfigPath(); } -/** - * FIXME: Someone with Windows system should check this and correct if necessary - */ -boost::filesystem::path WindowsPath::getLocalDataPath() const -{ - return boost::filesystem::path("./data/"); -} - boost::filesystem::path WindowsPath::getInstallPath() const { boost::filesystem::path installPath(""); diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 0240de257f..919d087f11 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -39,7 +39,8 @@ namespace Files struct WindowsPath { /** - * \brief Returns "X:\Documents And Settings\\My Documents\My Games\" + * \brief Returns user path i.e.: + * "X:\Documents And Settings\\My Documents\My Games\" * * \return boost::filesystem::path */ @@ -60,13 +61,6 @@ struct WindowsPath */ boost::filesystem::path getLocalPath() const; - /** - * \brief Return same path like getUserConfigPath - * - * \return boost::filesystem::path - */ - boost::filesystem::path getUserDataPath() const; - /** * \brief Return same path like getGlobalConfigPath * @@ -74,14 +68,6 @@ struct WindowsPath */ boost::filesystem::path getGlobalDataPath() const; - /** - * \brief Return runtime data path which is a location where - * an application was started with 'data' suffix. - * - * \return boost::filesystem::path - */ - boost::filesystem::path getLocalDataPath() const; - /** * \brief Gets the path of the installed Morrowind version if there is one. * From 21b8456453242e132c85f92047cf9bce535c1b22 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Feb 2012 21:09:08 +0100 Subject: [PATCH 193/269] fixed a double-free that can happen if OpenMW exits prematurely (when OgreRenderer is deleted before CreateScene() was called) --- ogre/renderer.cpp | 3 ++- ogre/renderer.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index ccbc4e1914..399d50d426 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -13,7 +13,8 @@ using namespace OEngine::Render; void OgreRenderer::cleanup() { - delete mFader; + if (mFader) + delete mFader; if(mRoot) delete mRoot; diff --git a/ogre/renderer.hpp b/ogre/renderer.hpp index 0c2f0d3d17..f48383cbc6 100644 --- a/ogre/renderer.hpp +++ b/ogre/renderer.hpp @@ -32,7 +32,7 @@ namespace Render public: OgreRenderer() - : mRoot(NULL), mWindow(NULL), mScene(NULL) {} + : mRoot(NULL), mWindow(NULL), mScene(NULL), mFader(NULL) {} ~OgreRenderer() { cleanup(); } /** Configure the renderer. This will load configuration files and From 971ae94fbf0dc5d4ebab96a2e25b5f731c2f5ca8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 21 Feb 2012 11:50:04 +0100 Subject: [PATCH 194/269] Revert "workaround for the configuration problems" This reverts commit 850501e9221daf1a55d31b72c4389da158b338e6. --- components/files/configurationmanager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index c6e5534180..46cd0e053b 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -56,9 +56,9 @@ ConfigurationManager::~ConfigurationManager() void ConfigurationManager::setupTokensMapping() { mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); - mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); - mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserPath)); - mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalPath)); + mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalDataPath)); + mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserDataPath)); + mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); } void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, From 280babc7194bf01dfe5f9c79b4b3d45fecab51df Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Wed, 22 Feb 2012 08:34:47 +0100 Subject: [PATCH 195/269] Fixed stylesheet and configuration problems with the launcher and code cleanup --- CMakeLists.txt | 2 +- apps/launcher/datafilespage.cpp | 195 ++++++++++++++------- apps/launcher/datafilespage.hpp | 18 +- apps/launcher/graphicspage.cpp | 2 +- apps/launcher/graphicspage.hpp | 9 +- apps/launcher/main.cpp | 7 - apps/launcher/maindialog.cpp | 197 +++------------------- apps/launcher/maindialog.hpp | 7 - components/files/configurationmanager.cpp | 2 +- 9 files changed, 169 insertions(+), 270 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a83ca1c0b..41da79a055 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -329,7 +329,7 @@ if(WIN32) FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*") INSTALL(FILES ${files} DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") - INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.cfg" "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION ".") + INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION ".") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") SET(CPACK_GENERATOR "NSIS") diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 6eb4590373..fb8631f731 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -26,7 +26,7 @@ bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2) return index1.row() <= index2.row(); } -DataFilesPage::DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent) +DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent) : QWidget(parent) , mCfgMgr(cfg) { @@ -123,25 +123,89 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent) setupConfig(); + setupDataFiles(); createActions(); } -void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) +void DataFilesPage::setupConfig() { - // Put the paths in a boost::filesystem vector to use with Files::Collections - Files::PathContainer dataDirs; + QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.cfg").string()); + QFile file(config); - foreach (const QString ¤tPath, paths) { - dataDirs.push_back(boost::filesystem::path(currentPath.toStdString())); + if (!file.exists()) { + config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string()); } + // Open our config file + mLauncherConfig = new QSettings(config, QSettings::IniFormat); + mLauncherConfig->sync(); + + + // Make sure we have no groups open + while (!mLauncherConfig->group().isEmpty()) { + mLauncherConfig->endGroup(); + } + + mLauncherConfig->beginGroup("Profiles"); + QStringList profiles = mLauncherConfig->childGroups(); + + if (profiles.isEmpty()) { + // Add a default profile + profiles.append("Default"); + } + + mProfilesComboBox->addItems(profiles); + + QString currentProfile = mLauncherConfig->value("CurrentProfile").toString(); + + if (currentProfile.isEmpty()) { + // No current profile selected + currentProfile = "Default"; + } + mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile)); + + mLauncherConfig->endGroup(); + + // Now we connect the combobox to do something if the profile changes + // This prevents strange behaviour while reading and appending the profiles + connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); +} + + +void DataFilesPage::setupDataFiles() +{ + // We use the Configuration Manager to retrieve the configuration values + boost::program_options::variables_map variables; + boost::program_options::options_description desc; + + desc.add_options() + ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) + ("data-local", boost::program_options::value()->default_value("")) + ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) + ("encoding", boost::program_options::value()->default_value("win1252")); + + mCfgMgr.readConfiguration(variables, desc); + + // Put the paths in a boost::filesystem vector to use with Files::Collections + Files::PathContainer dataDirs(variables["data"].as()); mCfgMgr.processPaths(dataDirs); + std::string local(variables["data-local"].as()); + if (!local.empty()) + { + dataDirs.push_back(Files::PathContainer::value_type(local)); + } + + if (dataDirs.empty()) + { + dataDirs.push_back(mCfgMgr.getLocalPath()); + } + // Create a file collection for the dataDirs - Files::Collections mFileCollections(dataDirs, strict); + Files::Collections fileCollections(dataDirs, !variables["fs-strict"].as()); // First we add all the master files - const Files::MultiDirCollection &esm = mFileCollections.getCollection(".esm"); + const Files::MultiDirCollection &esm = fileCollections.getCollection(".esm"); unsigned int i = 0; // Row number for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) @@ -161,14 +225,14 @@ void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) } // Now on to the plugins - const Files::MultiDirCollection &esp = mFileCollections.getCollection(".esp"); + const Files::MultiDirCollection &esp = fileCollections.getCollection(".esp"); for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) { ESMReader fileReader; QStringList availableMasters; // Will contain all found masters - fileReader.setEncoding("win1252"); // FIXME: This should be configurable! + fileReader.setEncoding(variables["encoding"].as()); fileReader.open(iter->second.string()); // First we fill the availableMasters and the mMastersWidget @@ -238,52 +302,6 @@ void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict) readConfig(); } -void DataFilesPage::setupConfig() -{ - QString config = (mCfgMgr.getLocalPath() / "launcher.cfg").string().c_str(); - QFile file(config); - - if (!file.exists()) { - config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string()); - } - - file.setFileName(config); // Just for displaying information - - // Open our config file - mLauncherConfig = new QSettings(config, QSettings::IniFormat); - mLauncherConfig->sync(); - - - // Make sure we have no groups open - while (!mLauncherConfig->group().isEmpty()) { - mLauncherConfig->endGroup(); - } - - mLauncherConfig->beginGroup("Profiles"); - QStringList profiles = mLauncherConfig->childGroups(); - - if (profiles.isEmpty()) { - // Add a default profile - profiles.append("Default"); - } - - mProfilesComboBox->addItems(profiles); - - QString currentProfile = mLauncherConfig->value("CurrentProfile").toString(); - - if (currentProfile.isEmpty()) { - // No current profile selected - currentProfile = "Default"; - } - mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile)); - - mLauncherConfig->endGroup(); - - // Now we connect the combobox to do something if the profile changes - // This prevents strange behaviour while reading and appending the profiles - connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); -} - void DataFilesPage::createActions() { // Refresh the plugins @@ -983,6 +1001,53 @@ void DataFilesPage::writeConfig(QString profile) return; } + // Prepare the OpenMW config + + // Open the config as a QFile + QFile file(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); + + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + // File cannot be opened or created + QMessageBox msgBox; + msgBox.setWindowTitle("Error writing OpenMW configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not open or create %0

\ + Please make sure you have the right permissions and try again.
").arg(file.fileName())); + msgBox.exec(); + + QApplication::exit(1); + } + + QTextStream in(&file); + QByteArray buffer; + + // Remove all previous master/plugin entries from config + while (!in.atEnd()) { + QString line = in.readLine(); + if (!line.contains("master") && !line.contains("plugin")) { + buffer += line += "\n"; + } + } + + file.close(); + + // Now we write back the other config entries + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error writing OpenMW configuration file"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("
Could not write to %0

\ + Please make sure you have the right permissions and try again.
").arg(file.fileName())); + msgBox.exec(); + + QApplication::exit(1); + } + + file.write(buffer); + QTextStream gameConfig(&file); + // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { mLauncherConfig->endGroup(); @@ -995,13 +1060,16 @@ void DataFilesPage::writeConfig(QString profile) mLauncherConfig->beginGroup(profile); mLauncherConfig->remove(""); // Clear the subgroup - // First write the masters to the config - const QStringList masterList = selectedMasters(); + // First write the masters to the configs + const QStringList masters = selectedMasters(); // We don't use foreach because we need i - for (int i = 0; i < masterList.size(); ++i) { - const QString master = masterList.at(i); - mLauncherConfig->setValue(QString("Master%0").arg(i), master); + for (int i = 0; i < masters.size(); ++i) { + const QString currentMaster = masters.at(i); + + mLauncherConfig->setValue(QString("Master%0").arg(i), currentMaster); + gameConfig << "master=" << currentMaster << endl; + } // Now write all checked plugins @@ -1009,9 +1077,12 @@ void DataFilesPage::writeConfig(QString profile) for (int i = 0; i < plugins.size(); ++i) { - mLauncherConfig->setValue(QString("Plugin%1").arg(i), plugins.at(i)); + const QString currentPlugin = plugins.at(i); + mLauncherConfig->setValue(QString("Plugin%1").arg(i), currentPlugin); + gameConfig << "plugin=" << currentPlugin << endl; } + file.close(); mLauncherConfig->endGroup(); mLauncherConfig->endGroup(); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index db1068abdc..a454fa871b 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -29,16 +29,9 @@ public: DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent = 0); ComboBox *mProfilesComboBox; - QSettings *mLauncherConfig; - const QStringList checkedPlugins(); - const QStringList selectedMasters(); - void setupConfig(); - void readConfig(); void writeConfig(QString profile = QString()); - void setupDataFiles(const QStringList &paths, bool strict); - public slots: void masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void setCheckState(QModelIndex index); @@ -83,13 +76,20 @@ private: QAction *mCheckAction; QAction *mUncheckAction; - Files::ConfigurationManager& mCfgMgr; + Files::ConfigurationManager &mCfgMgr; + + QSettings *mLauncherConfig; + + const QStringList checkedPlugins(); + const QStringList selectedMasters(); void addPlugins(const QModelIndex &index); void removePlugins(const QModelIndex &index); void uncheckPlugins(); void createActions(); - + void setupDataFiles(); + void setupConfig(); + void readConfig(); void scrollToSelection(); }; diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index d41a333563..c9dca18793 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -3,7 +3,7 @@ #include "graphicspage.hpp" #include -GraphicsPage::GraphicsPage(Files::ConfigurationManager& cfg, QWidget *parent) +GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent) : QWidget(parent) , mCfgMgr(cfg) { diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index ffd7a41b8c..bdfd4f038d 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -20,9 +20,7 @@ class GraphicsPage : public QWidget Q_OBJECT public: - GraphicsPage(Files::ConfigurationManager& cfg, QWidget *parent = 0); - - QSettings *mOgreConfig; + GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent = 0); void writeConfig(); @@ -30,7 +28,6 @@ public slots: void rendererChanged(const QString &renderer); private: - Files::ConfigurationManager& mCfgMgr; Ogre::Root *mOgre; Ogre::RenderSystem *mSelectedRenderSystem; Ogre::RenderSystem *mOpenGLRenderSystem; @@ -60,6 +57,10 @@ private: QCheckBox *mD3DVSyncCheckBox; QCheckBox *mD3DFullScreenCheckBox; + QSettings *mOgreConfig; + + Files::ConfigurationManager &mCfgMgr; + QString getConfigValue(const QString &key, Ogre::RenderSystem *renderer); QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index f108882a3f..bd29e2bcac 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -31,13 +31,6 @@ int main(int argc, char *argv[]) QDir::setCurrent(dir.absolutePath()); - // Load the stylesheet - QFile file("./launcher.qss"); - - file.open(QFile::ReadOnly); - QString styleSheet = QLatin1String(file.readAll()); - app.setStyleSheet(styleSheet); - MainDialog dialog; return dialog.exec(); diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index dd9f0d653c..14f452fa3f 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -45,6 +45,20 @@ MainDialog::MainDialog() setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); setMinimumSize(QSize(575, 575)); + // Load the stylesheet + QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "launcher.qss").string()); + QFile file(config); + + if (!file.exists()) { + file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string())); + } + + file.open(QFile::ReadOnly); + QString styleSheet = QLatin1String(file.readAll()); + qApp->setStyleSheet(styleSheet); + file.close(); + + connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(play())); @@ -85,116 +99,13 @@ void MainDialog::createIcons() } -QStringList MainDialog::readConfig(const QString &fileName) -{ - // We can't use QSettings directly because it - // does not support multiple keys with the same name - QFile file(fileName); - - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error opening OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open %0

\ - Please make sure you have the right permissions and try again.
").arg(file.fileName())); - msgBox.exec(); - - QApplication::exit(); // File cannot be opened or created - } - - QTextStream in(&file); - QStringList dataDirs; - QString dataLocal; - - // Read the config line by line - while (!in.atEnd()) { - QString line = in.readLine(); - - if (line.startsWith("data=")) { - dataDirs.append(line.remove("data=")); - } - - // Read the data-local key, if more than one are found only the last is used - if (line.startsWith("data-local=")) { - dataLocal = line.remove("data-local="); - } - - // Read fs-strict key - if (line.startsWith("fs-strict=")) { - QString value = line.remove("fs-strict="); - - (value.toLower() == QLatin1String("true")) - ? mStrict = true - : mStrict = false; - - } - - } - - // Add the data-local= key to the end of the dataDirs for priority reasons - if (!dataLocal.isEmpty()) { - dataDirs.append(dataLocal); - } - - if (!dataDirs.isEmpty()) - { - // Reset the global datadirs to the newly read entries - // Else return the previous dataDirs because nothing was found in this file; - mDataDirs = dataDirs; - } - - file.close(); - - return mDataDirs; -} - void MainDialog::createPages() { mPlayPage = new PlayPage(this); mGraphicsPage = new GraphicsPage(mCfgMgr, this); mDataFilesPage = new DataFilesPage(mCfgMgr, this); - // Retrieve all data entries from the configs - QStringList dataDirs; - - // Global location - QFile file(QString::fromStdString((mCfgMgr.getGlobalPath()/"openmw.cfg").string())); - if (file.exists()) { - dataDirs = readConfig(file.fileName()); - } - - // Local location - file.setFileName("./openmw.cfg"); - - if (file.exists()) { - dataDirs = readConfig(file.fileName()); - } - - // User location - file.setFileName(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); - if (file.exists()) { - dataDirs = readConfig(file.fileName()); - } - - file.close(); - - if (!dataDirs.isEmpty()) { - // Now pass the datadirs on to the DataFilesPage - mDataFilesPage->setupDataFiles(dataDirs, mStrict); - } else { - QMessageBox msgBox; - msgBox.setWindowTitle("Error reading OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not read the location of the data files

\ - Please make sure OpenMW is correctly configured and try again.
")); - msgBox.exec(); - - QApplication::exit(); // No data or data-local entries in openmw.cfg - } - - // Set the combobox of the play page to imitate the comobox on the datafilespage + // Set the combobox of the play page to imitate the combobox on the datafilespage mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model()); mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex()); @@ -246,14 +157,16 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) void MainDialog::closeEvent(QCloseEvent *event) { // Now write all config files - writeConfig(); + mDataFilesPage->writeConfig(); + mGraphicsPage->writeConfig(); event->accept(); } void MainDialog::play() { // First do a write of all the configs, just to be sure - writeConfig(); + mDataFilesPage->writeConfig(); + mGraphicsPage->writeConfig(); #ifdef Q_WS_WIN QString game = "./openmw.exe"; @@ -313,75 +226,3 @@ void MainDialog::play() close(); } } - -void MainDialog::writeConfig() -{ - // Write the profiles - mDataFilesPage->writeConfig(); - mDataFilesPage->mLauncherConfig->sync(); - - // Write the graphics settings - mGraphicsPage->writeConfig(); - mGraphicsPage->mOgreConfig->sync(); - - QStringList dataFiles = mDataFilesPage->selectedMasters(); - dataFiles.append(mDataFilesPage->checkedPlugins()); - - // Open the config as a QFile - QFile file(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); - - if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { - // File cannot be opened or created - QMessageBox msgBox; - msgBox.setWindowTitle("Error writing OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not open or create %0

\ - Please make sure you have the right permissions and try again.
").arg(file.fileName())); - msgBox.exec(); - - QApplication::exit(1); - } - - QTextStream in(&file); - QByteArray buffer; - - // Remove all previous master/plugin entries from config - while (!in.atEnd()) { - QString line = in.readLine(); - if (!line.contains("master") && !line.contains("plugin")) { - buffer += line += "\n"; - } - } - - file.close(); - - // Now we write back the other config entries - if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error writing OpenMW configuration file"); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
Could not write to %0

\ - Please make sure you have the right permissions and try again.
").arg(file.fileName())); - msgBox.exec(); - - QApplication::exit(1); - } - - file.write(buffer); - - QTextStream out(&file); - - // Write the list of game files to the config - foreach (const QString ¤tFile, dataFiles) { - - if (currentFile.endsWith(QString(".esm"), Qt::CaseInsensitive)) { - out << "master=" << currentFile << endl; - } else if (currentFile.endsWith(QString(".esp"), Qt::CaseInsensitive)) { - out << "plugin=" << currentFile << endl; - } - } - - file.close(); -} diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 718fde4f7f..d6d0e9974e 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -28,15 +28,11 @@ public slots: void play(); void profileChanged(int index); - private: void createIcons(); void createPages(); - void writeConfig(); void closeEvent(QCloseEvent *event); - QStringList readConfig(const QString &fileName); - QListWidget *mIconWidget; QStackedWidget *mPagesWidget; @@ -44,9 +40,6 @@ private: GraphicsPage *mGraphicsPage; DataFilesPage *mDataFilesPage; - QStringList mDataDirs; - bool mStrict; - Files::ConfigurationManager mCfgMgr; }; diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index c5561d6527..5cf2952c74 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -124,7 +124,7 @@ void ConfigurationManager::loadConfig(const boost::filesystem::path& path, if (configFileStream.is_open()) { boost::program_options::store(boost::program_options::parse_config_file( - configFileStream, description), variables); + configFileStream, description, true), variables); std::cout << "done." << std::endl; } From e0206edc44d13dfe8c78826f827f20963e684d3c Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 22 Feb 2012 20:00:17 +0100 Subject: [PATCH 196/269] Fixed stupid bug in getGlobalDataPath. Signed-off-by: Lukasz Gromanowski --- components/files/windowspath.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index e810412720..cf73b37289 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -64,7 +64,7 @@ boost::filesystem::path WindowsPath::getLocalPath() const boost::filesystem::path WindowsPath::getGlobalDataPath() const { - return getGlobalConfigPath(); + return getGlobalPath(); } boost::filesystem::path WindowsPath::getInstallPath() const From d5f1d7eed7517eb4a9df03abea61060ed2c3187b Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Wed, 22 Feb 2012 23:56:07 +0100 Subject: [PATCH 197/269] Fix for processing tokens inside data-local config option. Signed-off-by: Lukasz Gromanowski --- apps/openmw/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index dd7a1e7805..ec1775ac83 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -160,7 +160,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.enableFSStrict(variables["fs-strict"].as()); Files::PathContainer dataDirs(variables["data"].as()); - cfgMgr.processPaths(dataDirs); std::string local(variables["data-local"].as()); if (!local.empty()) @@ -173,6 +172,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat dataDirs.push_back(cfgMgr.getLocalPath()); } + cfgMgr.processPaths(dataDirs); + engine.setDataDirs(dataDirs); engine.setResourceDir(variables["resources"].as()); From f6a80bfc952c1f65af7971e07c0a7f9d0bcf37c1 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Thu, 23 Feb 2012 20:06:06 +0100 Subject: [PATCH 198/269] Small define/include fixes for compiling on windows --- components/files/configurationmanager.hpp | 4 ++++ components/files/fixedpath.hpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 7d77df9c00..7fb3793c64 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -1,7 +1,11 @@ #ifndef COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP #define COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP +#ifdef _WIN32 +#include +#else #include +#endif #include #include diff --git a/components/files/fixedpath.hpp b/components/files/fixedpath.hpp index 4e054b17ff..9e5c4f8f26 100644 --- a/components/files/fixedpath.hpp +++ b/components/files/fixedpath.hpp @@ -30,7 +30,7 @@ #include namespace Files { typedef LinuxPath TargetPathType; } -#elif defined(__WIN32) || defined(__WINDOWS__) +#elif defined(__WIN32) || defined(__WINDOWS__) || defined(_WIN32) #include namespace Files { typedef WindowsPath TargetPathType; } From 3da6af6e380c244a8fa2f44ba3e7d75c66e15521 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 23 Feb 2012 23:01:42 +0100 Subject: [PATCH 199/269] Bug fixes for configuration handling. Added erasing double quotes from paths, corrected retriveing installation path from wine registry. Updated doxygen comments. Signed-off-by: Lukasz Gromanowski --- components/files/configurationmanager.cpp | 5 ++++- components/files/linuxpath.cpp | 9 +++++++-- components/files/macospath.hpp | 6 +++--- components/files/windowspath.hpp | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index c5561d6527..8173c6a044 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -6,6 +6,7 @@ #include #include +#include /** * \namespace Files @@ -69,9 +70,11 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) { + std::string path; for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { - const std::string& path = it->string(); + path = it->string(); + boost::erase_all(path, "\""); // Check if path contains a token if (!path.empty() && *path.begin() == '?') diff --git a/components/files/linuxpath.cpp b/components/files/linuxpath.cpp index 0b315ccfb2..92e4dce33f 100644 --- a/components/files/linuxpath.cpp +++ b/components/files/linuxpath.cpp @@ -105,11 +105,16 @@ boost::filesystem::path LinuxPath::getInstallPath() const std::string line; std::string mwpath; - while (std::getline(file, line) && !line.empty()) + while (std::getline(file, line)) { if (line[0] == '[') // we found an entry { - isRegEntry = (line.find("Softworks\\Morrowind]") != std::string::npos); + if (isRegEntry) + { + break; + } + + isRegEntry = (line.find("Softworks\\\\Morrowind]") != std::string::npos); } else if (isRegEntry) { diff --git a/components/files/macospath.hpp b/components/files/macospath.hpp index 7656538c19..0e9b3402e6 100644 --- a/components/files/macospath.hpp +++ b/components/files/macospath.hpp @@ -39,21 +39,21 @@ namespace Files struct MacOsPath { /** - * \brief Return path to the local configuration directory. + * \brief Return path to the local directory. * * \return boost::filesystem::path */ boost::filesystem::path getUserPath() const; /** - * \brief Return path to the global (system) configuration directory. + * \brief Return path to the global (system) directory. * * \return boost::filesystem::path */ boost::filesystem::path getGlobalPath() const; /** - * \brief Return path to the runtime configuration directory which is the + * \brief Return path to the runtime directory which is the * place where an application was started. * * \return boost::filesystem::path diff --git a/components/files/windowspath.hpp b/components/files/windowspath.hpp index 919d087f11..d4c716c502 100644 --- a/components/files/windowspath.hpp +++ b/components/files/windowspath.hpp @@ -62,7 +62,7 @@ struct WindowsPath boost::filesystem::path getLocalPath() const; /** - * \brief Return same path like getGlobalConfigPath + * \brief Return same path like getGlobalPath * * \return boost::filesystem::path */ From d97854be004a97223be0e3241c0c534df469dceb Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Thu, 23 Feb 2012 23:21:17 +0100 Subject: [PATCH 200/269] Bug fixes for configuration handling. Corrected retrieving installation path from wine registry on MacOS. Updated doxygen comments. Signed-off-by: Lukasz Gromanowski --- components/files/linuxpath.hpp | 2 +- components/files/macospath.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/components/files/linuxpath.hpp b/components/files/linuxpath.hpp index 873f8801de..48fdbb2ff6 100644 --- a/components/files/linuxpath.hpp +++ b/components/files/linuxpath.hpp @@ -46,7 +46,7 @@ struct LinuxPath boost::filesystem::path getUserPath() const; /** - * \brief Return path to the global (system) configuration directory. + * \brief Return path to the global (system) directory where game files could be placed. * * \return boost::filesystem::path */ diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index 6225fc01f4..fc36e2a9cd 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -105,11 +105,16 @@ boost::filesystem::path MacOsPath::getInstallPath() const std::string line; std::string mwpath; - while (std::getline(file, line) && !line.empty()) + while (std::getline(file, line)) { if (line[0] == '[') // we found an entry { - isRegEntry = (line.find("Softworks\\Morrowind]") != std::string::npos); + if (isRegEntry) + { + break; + } + + isRegEntry = (line.find("Softworks\\\\Morrowind]") != std::string::npos); } else if (isRegEntry) { From fb51b281b210f95c6bcb88de263073effad7394d Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Fri, 24 Feb 2012 01:16:30 -0500 Subject: [PATCH 201/269] Slightly better performance on animation --- apps/openmw/mwrender/animation.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 63855f3b81..1318710eea 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -443,31 +443,31 @@ namespace MWRender{ Ogre::Quaternion r; bool bTrans = translist1.size() > 0; - if(bTrans){ - Ogre::Vector3 v1 = translist1[tindexI[slot]]; - Ogre::Vector3 v2 = translist1[tindexJ]; - t = (v1 + (v2 - v1) * x); - - } + bool bQuats = quats.size() > 0; - if(bQuats){ - r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); - } - skel = base->getSkeleton(); + if(skel->hasBone(iter->getBonename())){ Ogre::Bone* bone = skel->getBone(iter->getBonename()); - if(bTrans) + if(bTrans){ + Ogre::Vector3 v1 = translist1[tindexI[slot]]; + Ogre::Vector3 v2 = translist1[tindexJ]; + t = (v1 + (v2 - v1) * x); bone->setPosition(t); - if(bQuats) + + } + if(bQuats){ + r = Ogre::Quaternion::Slerp(x2, quats[rindexI[slot]], quats[rindexJ], true); bone->setOrientation(r); + } - skel->_updateTransforms(); - base->getAllAnimationStates()->_notifyDirty(); + } + skel->_updateTransforms(); + base->getAllAnimationStates()->_notifyDirty(); slot++; } From 08f3ecf9350e06b090de8198a2a6cb3d5c91ee8c Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Fri, 24 Feb 2012 01:30:17 -0500 Subject: [PATCH 202/269] Slightly better performance on animation2 --- apps/openmw/mwrender/animation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 1318710eea..c5b67ffc95 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -466,11 +466,12 @@ namespace MWRender{ } - skel->_updateTransforms(); - base->getAllAnimationStates()->_notifyDirty(); + slot++; } + skel->_updateTransforms(); + base->getAllAnimationStates()->_notifyDirty(); } } From 68da94c8f0efb392696752f1c91f7cabb59d5474 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 24 Feb 2012 20:19:32 +0100 Subject: [PATCH 203/269] workaround for older boost versions --- apps/openmw/main.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index ec1775ac83..b8f711b217 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -35,6 +35,23 @@ #include "config.hpp" +#include +/** + * Workaround for problems with whitespaces in paths in older versions of Boost library + */ +#if (BOOST_VERSION <= 104600) +namespace boost +{ + +template<> +inline boost::filesystem::path lexical_cast(const std::string& arg) +{ + return boost::filesystem::path(arg); +} + +} /* namespace boost */ +#endif /* (BOOST_VERSION <= 104600) */ + using namespace std; /** From 771d50fe6959ec22a1c67c7cf83229f35fab5fff Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 24 Feb 2012 20:20:32 +0100 Subject: [PATCH 204/269] removed a redundant assert and a left-over comment --- apps/openmw/engine.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2e4beb65f3..6535974a9e 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -246,8 +246,6 @@ void OMW::Engine::enableFSStrict(bool fsStrict) void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs) { - /// \todo remove mDataDir, once resources system can handle multiple directories - assert (!dataDirs.empty()); mDataDirs = dataDirs; mFileCollections = Files::Collections (dataDirs, !mFSStrict); } From c9a1789db8b73dca2923afb31cb7c5d48b8a3585 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 25 Feb 2012 10:20:42 +0100 Subject: [PATCH 205/269] Bug fix for processing data paths in OpenMW Launcher. Signed-off-by: Lukasz Gromanowski --- apps/launcher/datafilespage.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index fb8631f731..8100631d7a 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -188,8 +188,7 @@ void DataFilesPage::setupDataFiles() // Put the paths in a boost::filesystem vector to use with Files::Collections Files::PathContainer dataDirs(variables["data"].as()); - mCfgMgr.processPaths(dataDirs); - + std::string local(variables["data-local"].as()); if (!local.empty()) { @@ -201,6 +200,8 @@ void DataFilesPage::setupDataFiles() dataDirs.push_back(mCfgMgr.getLocalPath()); } + mCfgMgr.processPaths(dataDirs); + // Create a file collection for the dataDirs Files::Collections fileCollections(dataDirs, !variables["fs-strict"].as()); From cb780720bba5391cfb1e178851431353aee15d0d Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 25 Feb 2012 17:43:30 +0400 Subject: [PATCH 206/269] fix for string.h: use our own strnlen only on OS X < 10.7 --- libs/platform/string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/platform/string.h b/libs/platform/string.h index d4302ae783..89ac141a89 100644 --- a/libs/platform/string.h +++ b/libs/platform/string.h @@ -3,7 +3,7 @@ #define _STRING_WRAPPER_H #include -#if defined(__APPLE__) || defined(__MINGW32__) +#if (defined(__APPLE__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) || defined(__MINGW32__) // need our own implementation of strnlen static size_t strnlen(const char *s, size_t n) { From 1addef2cc3e3b02ec8dbd808c6f3424d39f531a6 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 25 Feb 2012 17:47:57 +0400 Subject: [PATCH 207/269] added missing include for macospath.cpp --- components/files/macospath.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/files/macospath.cpp b/components/files/macospath.cpp index fc36e2a9cd..94dafe7946 100644 --- a/components/files/macospath.cpp +++ b/components/files/macospath.cpp @@ -27,6 +27,7 @@ #include #include #include +#include /** * FIXME: Someone with MacOS system should check this and correct if necessary From 822f47fbac4433fe9b6f6eb92f759dd5f91b7fd1 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sat, 25 Feb 2012 18:05:29 +0400 Subject: [PATCH 208/269] Slightly changed OIS include dir var used specially for OS X. Should not change anything on other platforms --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41da79a055..a897454686 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,7 +190,7 @@ find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) include_directories("." ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE - ${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} + ${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR} ${CMAKE_HOME_DIRECTORY}/extern/caelum/include ${CMAKE_HOME_DIRECTORY}/extern/mygui_3.0.1/MyGUIEngine/include From 14b2851e72f610ae81dd296598867e6fb0babd2a Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Sat, 25 Feb 2012 19:09:45 +0200 Subject: [PATCH 209/269] Fix for cell change OpenAL music streaming issue --- sound/outputs/openal_out.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/outputs/openal_out.cpp b/sound/outputs/openal_out.cpp index bce08d02f9..f1cf398385 100644 --- a/sound/outputs/openal_out.cpp +++ b/sound/outputs/openal_out.cpp @@ -30,8 +30,11 @@ static char tmp_buffer[BSIZE]; // Number of buffers used (per sound) for streaming sounds. Each // buffer is of size BSIZE. Increasing this will make streaming sounds // more fault tolerant against temporary lapses in call to update(), -// but will also increase memory usage. 4 should be ok. -const int STREAM_BUF_NUM = 4; +// but will also increase memory usage. +// This was changed from 4 to 150 for an estimated 30 seconds tolerance. +// At some point we should replace it with a more multithreading-ish +// solution. +const int STREAM_BUF_NUM = 150; static void fail(const std::string &msg) { throw std::runtime_error("OpenAL exception: " + msg); } @@ -101,7 +104,7 @@ class Mangle::Sound::OpenAL_Sound : public Sound ALuint inst; // Buffers. Only the first is used for non-streaming sounds. - ALuint bufferID[4]; + ALuint bufferID[STREAM_BUF_NUM]; // Number of buffers used int bufNum; From cb5534c6083f8f47b4bf51ea0eba32fd8b315b5a Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 26 Feb 2012 12:12:00 +0100 Subject: [PATCH 210/269] Updated website URL in CMakeLists file. Signed-off-by: Lukasz Gromanowski --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 41da79a055..58f4b938bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,7 +300,7 @@ if(DPKG_PROGRAM) SET(CPACK_GENERATOR "DEB") SET(CPACK_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.com") + SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org") SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}") SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind From be8690e333d1f4757654e91616fdae8c2ba79943 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Sun, 26 Feb 2012 22:45:17 +0100 Subject: [PATCH 211/269] Fix for debug crash on windows --- apps/openmw/mwrender/actors.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index acc6554049..85217b42da 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -99,12 +99,14 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store){ mRend.getScene()->destroySceneNode(base); base = 0; } - for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) + for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); ) { if(iter->first.getCell() == store){ delete iter->second; - mAllActors.erase(iter); + iter = mAllActors.erase(iter); } + else + ++iter; } } From 053a2996d2e487356b100c8b5782ed2a9979421f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 26 Feb 2012 23:31:16 +0100 Subject: [PATCH 212/269] fix for non-standard erase function --- apps/openmw/mwrender/actors.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 85217b42da..d8ca78e3a5 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -99,16 +99,15 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store){ mRend.getScene()->destroySceneNode(base); base = 0; } - for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); ) - { - if(iter->first.getCell() == store){ - delete iter->second; - iter = mAllActors.erase(iter); - } + for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); ) + { + if(iter->first.getCell() == store){ + delete iter->second; + mAllActors.erase(iter++); + } else ++iter; - } - + } } void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number){ From 8d7a5f469b43f0ec1d3e5678867421cd45b78659 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 26 Feb 2012 21:27:54 -0500 Subject: [PATCH 213/269] a few changes --- apps/openmw/mwrender/animation.cpp | 26 +++++++++++++++----------- apps/openmw/mwrender/animation.hpp | 6 ++++-- apps/openmw/mwrender/npcanimation.cpp | 1 + components/nifogre/ogre_nif_loader.cpp | 3 ++- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c5b67ffc95..c30579b0f0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -276,6 +276,7 @@ namespace MWRender{ rotmult = bonePtr->getOrientation(); scale = bonePtr->getScale().x; boneSequenceIter++; + std::cout << "Entering\n"; for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) { if(creaturemodel->getSkeleton()->hasBone(*boneSequenceIter)){ @@ -330,7 +331,7 @@ namespace MWRender{ } } - bool Animation::timeIndex( float time, std::vector times, int & i, int & j, float & x ){ + bool Animation::timeIndex( float time, std::vector& times, int & i, int & j, float & x ){ int count; if ( (count = times.size()) > 0 ) { @@ -388,6 +389,8 @@ namespace MWRender{ } void Animation::handleAnimationTransforms(){ + + Ogre::SkeletonInstance* skel = base->getSkeleton(); @@ -404,10 +407,10 @@ namespace MWRender{ for(unsigned int i = 0; i < entityparts.size(); i++){ //Ogre::SkeletonInstance* skel = entityparts[i]->getSkeleton(); - Ogre::Bone* b = skel->getRootBone(); - b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3));//This is a trick + //Ogre::Bone* b = skel->getRootBone(); + //b->setOrientation(Ogre::Real(.3),Ogre::Real(.3),Ogre::Real(.3), Ogre::Real(.3));//This is a trick - entityparts[i]->getAllAnimationStates()->_notifyDirty(); + //entityparts[i]->getAllAnimationStates()->_notifyDirty(); } @@ -424,18 +427,19 @@ namespace MWRender{ float x; float x2; - std::vector quats = iter->getQuat(); + std::vector& quats = iter->getQuat(); - std::vector ttime = iter->gettTime(); - std::vector::iterator ttimeiter = ttime.begin(); + std::vector& ttime = iter->gettTime(); + - std::vector rtime = iter->getrTime(); - int rindexJ = 0; + std::vector& rtime = iter->getrTime(); + int rindexJ = rindexI[slot]; + timeIndex(time, rtime, rindexI[slot], rindexJ, x2); - int tindexJ = 0; + int tindexJ = tindexI[slot]; - std::vector translist1 = iter->getTranslist1(); + std::vector& translist1 = iter->getTranslist1(); timeIndex(time, ttime, tindexI[slot], tindexJ, x); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index bad7eca15e..63ca3da2d9 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -28,6 +28,8 @@ class Animation{ MWWorld::Environment& mEnvironment; std::map vecRotPos; static std::map mUniqueIDs; + float oldHund; + bool samePlace; std::vector* > shapeparts; //All the NiTriShape data that we need for animating an npc @@ -55,11 +57,11 @@ class Animation{ Ogre::Entity* base; void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); void handleAnimationTransforms(); - bool timeIndex( float time, std::vector times, int & i, int & j, float & x ); + bool timeIndex( float time, std::vector& times, int & i, int & j, float & x ); std::string getUniqueID(std::string mesh); public: - Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend): mRend(_rend), mEnvironment(_env), animate(0){}; + Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend): mRend(_rend), mEnvironment(_env), animate(0), oldHund(0){}; virtual void runAnimation(float timepassed) = 0; void startScript(std::string groupname, int mode, int loops); void stopScript(); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 4c9c0e4665..0ceb0a4c35 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -276,6 +276,7 @@ void NpcAnimation::runAnimation(float timepassed){ shapepartsiter++; entitypartsiter++; } + } } diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 8b55400193..62dbc29dff 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1328,7 +1328,8 @@ void NIFLoader::loadResource(Resource *resource) (*iter)->addBoneAssignment(vba); } - mesh->_notifySkeleton(mSkel); + if(triname == "") + mesh->_notifySkeleton(mSkel); } } From 36e93228309b37ccf2019b530148b401e84ed73a Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sun, 26 Feb 2012 21:43:04 -0500 Subject: [PATCH 214/269] a few changes2 --- apps/openmw/mwrender/animation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index c30579b0f0..999d854141 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -276,7 +276,7 @@ namespace MWRender{ rotmult = bonePtr->getOrientation(); scale = bonePtr->getScale().x; boneSequenceIter++; - std::cout << "Entering\n"; + for(; boneSequenceIter != boneSequence.end(); boneSequenceIter++) { if(creaturemodel->getSkeleton()->hasBone(*boneSequenceIter)){ From d58b408ae11c0004840140dbd5427b9a7156a935 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Mon, 27 Feb 2012 19:00:06 +0400 Subject: [PATCH 215/269] Deploying Qt image format plugins on OS X considered useless --- apps/launcher/CMakeLists.txt | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 2879539a75..d648f4674e 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -51,17 +51,6 @@ QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) include(${QT_USE_FILE}) -# list here plugins that can't be detected statically, but loaded in runtime -# it needed for packaging automatisation -set(USED_QT_PLUGINS imageformats/libqgif - imageformats/libqico - imageformats/libqjpeg - imageformats/libqmng - imageformats/libqsvg - imageformats/libqtga - imageformats/libqtiff) -# It seems that launcher works without this plugins, but it loads them if they exists - # Main executable add_executable(omwlauncher ${LAUNCHER} @@ -86,13 +75,6 @@ if (APPLE) "${APP_BUNDLE_DIR}/../launcher.qss") configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss "${APP_BUNDLE_DIR}/../launcher.cfg") - - # copy used QT plugins into ${APP_BUNDLE_DIR}/Contents/Plugins - foreach(PLUGIN ${USED_QT_PLUGINS}) - get_filename_component(PLUGIN_FILENAME ${PLUGIN} NAME) - configure_file("${QT_PLUGINS_DIR}/${PLUGIN}.dylib" "${APP_BUNDLE_DIR}/Contents/Plugins/${PLUGIN}.dylib" COPYONLY) - endforeach() - else() configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss") From bfb3a4a36b3ca0c15531ef128cf2d56b20fb5e63 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Mon, 27 Feb 2012 22:22:17 +0100 Subject: [PATCH 216/269] Changed FindOGRE a bit to make it easier to find a source build of Ogre. (At least on windows) --- cmake/FindOGRE.cmake | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake index ce3993805b..3311ee2205 100644 --- a/cmake/FindOGRE.cmake +++ b/cmake/FindOGRE.cmake @@ -26,13 +26,15 @@ IF (WIN32) #Windows SET(OGRE_INCLUDE_DIR ${OGRESDK}/include) SET(OGRE_LIB_DIR ${OGRESDK}/lib) SET(OGRE_LIBRARIES debug OgreMain_d optimized OgreMain) - ENDIF (OGRESDK) - IF (OGRESOURCE) + ELSEIF (OGRESOURCE) MESSAGE(STATUS "Using OGRE built from source") SET(OGRE_INCLUDE_DIR $ENV{OGRE_SRC}/OgreMain/include) SET(OGRE_LIB_DIR $ENV{OGRE_SRC}/lib) SET(OGRE_LIBRARIES debug OgreMain_d optimized OgreMain) - ENDIF (OGRESOURCE) + ELSE (OGRESDK) + MESSAGE(STATUS "Using OGRE paths from CMake") + SET(OGRE_LIBRARIES debug OgreMain_d optimized OgreMain) + ENDIF (OGRESDK) ENDIF (WIN32) IF (UNIX AND NOT APPLE) @@ -80,9 +82,12 @@ SET(OGRE_LIB_DIR ${OGRE_LIB_DIR} CACHE PATH "") if(OGRE_LIB_DIR) CMAKE_POLICY(SET CMP0009 NEW) - IF (NOT APPLE) + IF (WIN32) + FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.lib") + ENDIF (WIN32) + IF (NOT APPLE AND NOT WIN32) FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.so") - ENDIF (NOT APPLE) + ENDIF (NOT APPLE AND NOT WIN32) IF (APPLE) FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.dylib") ENDIF (APPLE) From 9a4cd6c2b1debb5e2174e485d4476b1db3d18132 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Mon, 27 Feb 2012 23:56:58 +0100 Subject: [PATCH 217/269] Fixes #200 - Paths with quotes for data-local option. Signed-off-by: Lukasz Gromanowski --- components/files/configurationmanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 887a054ade..ef45b6543e 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -75,6 +75,7 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) { path = it->string(); boost::erase_all(path, "\""); + *it = boost::filesystem::path(path); // Check if path contains a token if (!path.empty() && *path.begin() == '?') From 4c2fffdd614b836069dd9c69e51e162666ca7c19 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Feb 2012 09:27:35 +0100 Subject: [PATCH 218/269] temporarily disabled multi data path support in OpenMW --- apps/openmw/main.cpp | 9 ++++++--- files/openmw.cfg | 3 --- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index b8f711b217..cd1e0e26e4 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -181,12 +181,15 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat std::string local(variables["data-local"].as()); if (!local.empty()) { - dataDirs.push_back(Files::PathContainer::value_type(local)); + std::cout << "Ignoring data-local (currently not supported)" << std::endl; +// dataDirs.push_back(Files::PathContainer::value_type(local)); } - if (dataDirs.empty()) + if (dataDirs.size()>1) { - dataDirs.push_back(cfgMgr.getLocalPath()); + dataDirs.resize (1); + std::cout << "Ignoring all but the first data path (multiple data paths currently not supported)" + << std::endl; } cfgMgr.processPaths(dataDirs); diff --git a/files/openmw.cfg b/files/openmw.cfg index 585b735f30..0af096e6aa 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -1,5 +1,2 @@ data="?mw?Data Files" -data="?global?data" -data="?local?data" -data-local="?user?data" resources=${MORROWIND_RESOURCE_FILES} From 7aaa7f185f530e610bfeaf36f29e5f1d1335bbef Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Feb 2012 09:46:48 +0100 Subject: [PATCH 219/269] same for launcher --- apps/launcher/datafilespage.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8100631d7a..d3968f2bf3 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -180,7 +180,7 @@ void DataFilesPage::setupDataFiles() desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) - ("data-local", boost::program_options::value()->default_value("")) +// ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) ("encoding", boost::program_options::value()->default_value("win1252")); @@ -188,17 +188,15 @@ void DataFilesPage::setupDataFiles() // Put the paths in a boost::filesystem vector to use with Files::Collections Files::PathContainer dataDirs(variables["data"].as()); - - std::string local(variables["data-local"].as()); - if (!local.empty()) - { - dataDirs.push_back(Files::PathContainer::value_type(local)); - } - if (dataDirs.empty()) - { - dataDirs.push_back(mCfgMgr.getLocalPath()); - } +// std::string local(variables["data-local"].as()); +// if (!local.empty()) +// { +// dataDirs.push_back(Files::PathContainer::value_type(local)); +// } + + if (dataDirs.size()>1) + dataDirs.resize (1); mCfgMgr.processPaths(dataDirs); From dc1fe6fb4d957f1b2330674b29c3c470461acb72 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 28 Feb 2012 09:56:12 +0100 Subject: [PATCH 220/269] adjusted readme --- readme.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/readme.txt b/readme.txt index 47dd6d3fd4..d9e6cbfa47 100644 --- a/readme.txt +++ b/readme.txt @@ -141,8 +141,6 @@ Feature #45: NPC animations Feature #46: Creature Animation Feature #89: Basic Journal Window Feature #110: Automatically pick up the path of existing MW-installations -Feature #133: Handle resources across multiple data directories -Feature #134: Generate a suitable default-value for --data-local Feature #183: More FPS display settings Task #19: Refactor engine class Task #109/Feature #162: Automate Packaging From 1512481c075e8063dba59cf64d267d5b1ff22ca1 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 28 Feb 2012 17:18:01 +0100 Subject: [PATCH 221/269] Changed the way whitespace was removed from retrieved Ogre values --- apps/launcher/graphicspage.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index c9dca18793..858f3155d1 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -246,13 +246,7 @@ void GraphicsPage::setupOgre() if (mOpenGLRenderSystem) { mOGLRTTComboBox->addItems(getAvailableOptions(QString("RTT Preferred Mode"), mOpenGLRenderSystem)); mOGLAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mOpenGLRenderSystem)); - - QStringList videoModes = getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem); - // Remove extraneous spaces - videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" ")); - videoModes.replaceInStrings(QRegExp("^\\s"), QString()); - - mOGLResolutionComboBox->addItems(videoModes); + mOGLResolutionComboBox->addItems(getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem)); mOGLFrequencyComboBox->addItems(getAvailableOptions(QString("Display Frequency"), mOpenGLRenderSystem)); } @@ -261,12 +255,7 @@ void GraphicsPage::setupOgre() mD3DRenderDeviceComboBox->addItems(getAvailableOptions(QString("Rendering Device"), mDirect3DRenderSystem)); mD3DAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mDirect3DRenderSystem)); mD3DFloatingPointComboBox->addItems(getAvailableOptions(QString("Floating-point mode"), mDirect3DRenderSystem)); - - QStringList videoModes = getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem); - // Remove extraneous spaces - videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" ")); - videoModes.replaceInStrings(QRegExp("^\\s"), QString()); - mD3DResolutionComboBox->addItems(videoModes); + mD3DResolutionComboBox->addItems(getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem)); } } @@ -481,7 +470,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy { if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) - result << (*opt_it).c_str(); + result << QString::fromStdString((*opt_it).c_str()).simplified(); } } From 5b54a658d89d9cea14a6365278cc6828d63e503d Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 28 Feb 2012 17:19:44 +0100 Subject: [PATCH 222/269] Launcher improvements: ask for data dir and write it to cfg if none is found and prevent removal of the default profile --- CMakeLists.txt | 2 +- apps/launcher/CMakeLists.txt | 6 +- apps/launcher/datafilespage.cpp | 186 ++++++++++++++++++++++---------- apps/launcher/datafilespage.hpp | 5 + 4 files changed, 138 insertions(+), 61 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e62391c741..666522e366 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -428,7 +428,7 @@ if (APPLE) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) - install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index d648f4674e..80e8f61c7b 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -72,13 +72,13 @@ endif() if (APPLE) configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${APP_BUNDLE_DIR}/../launcher.qss") + "${APP_BUNDLE_DIR}/resources/../launcher.qss") configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss "${APP_BUNDLE_DIR}/../launcher.cfg") else() configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss") + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/launcher.qss") configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg") + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg") endif() diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8100631d7a..8c203a771e 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -1,8 +1,6 @@ #include #include -#include -#include #include #include "datafilespage.hpp" @@ -120,11 +118,12 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent) connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); connect(mPluginsTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); + connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); - + createActions(); setupConfig(); setupDataFiles(); - createActions(); + } void DataFilesPage::setupConfig() @@ -138,8 +137,7 @@ void DataFilesPage::setupConfig() // Open our config file mLauncherConfig = new QSettings(config, QSettings::IniFormat); - mLauncherConfig->sync(); - + file.close(); // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { @@ -162,13 +160,14 @@ void DataFilesPage::setupConfig() // No current profile selected currentProfile = "Default"; } - mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile)); + + const int currentIndex = mProfilesComboBox->findText(currentProfile); + if (currentIndex != -1) { + // Profile is found + mProfilesComboBox->setCurrentIndex(currentIndex); + } mLauncherConfig->endGroup(); - - // Now we connect the combobox to do something if the profile changes - // This prevents strange behaviour while reading and appending the profiles - connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); } @@ -188,19 +187,51 @@ void DataFilesPage::setupDataFiles() // Put the paths in a boost::filesystem vector to use with Files::Collections Files::PathContainer dataDirs(variables["data"].as()); - - std::string local(variables["data-local"].as()); - if (!local.empty()) - { + mDataDirs = dataDirs; + + std::string local = variables["data-local"].as(); + if (!local.empty()) { + mDataLocal.push_back(Files::PathContainer::value_type(local)); dataDirs.push_back(Files::PathContainer::value_type(local)); } - if (dataDirs.empty()) - { - dataDirs.push_back(mCfgMgr.getLocalPath()); + mCfgMgr.processPaths(dataDirs); + + while (dataDirs.empty()) { + // No valid data files directory found + + QMessageBox msgBox; + msgBox.setWindowTitle("Error detecting Morrowind installation"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("
Could not find the Data Files location

\ + The directory containing the Data Files was not found.

\ + Press \"Browse...\" to specify the location manually.
")); + + QAbstractButton *dirSelectButton = + msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == dirSelectButton) { + + QString dataDir = QFileDialog::getExistingDirectory( + this, tr("Select Data Files Directory"), + "/home", + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + dataDirs.push_back(Files::PathContainer::value_type(dataDir.toStdString())); + mDataDirs.push_back(Files::PathContainer::value_type(dataDir.toStdString())); + } else { + // Cancel + break; + } } - mCfgMgr.processPaths(dataDirs); + // Check if cancel was clicked because we can't exit from while loop + if (dataDirs.empty()) { + QApplication::exit(1); + } // Create a file collection for the dataDirs Files::Collections fileCollections(dataDirs, !variables["fs-strict"].as()); @@ -209,15 +240,13 @@ void DataFilesPage::setupDataFiles() const Files::MultiDirCollection &esm = fileCollections.getCollection(".esm"); unsigned int i = 0; // Row number - for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) - { + for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) { QString currentMaster = QString::fromStdString( boost::filesystem::path (iter->second.filename()).string()); const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); - if (itemList.isEmpty()) // Master is not yet in the widget - { + if (itemList.isEmpty()) { // Master is not yet in the widget mMastersWidget->insertRow(i); QTableWidgetItem *item = new QTableWidgetItem(currentMaster); mMastersWidget->setItem(i, 0, item); @@ -228,8 +257,7 @@ void DataFilesPage::setupDataFiles() // Now on to the plugins const Files::MultiDirCollection &esp = fileCollections.getCollection(".esp"); - for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) - { + for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) { ESMReader fileReader; QStringList availableMasters; // Will contain all found masters @@ -245,8 +273,7 @@ void DataFilesPage::setupDataFiles() const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); - if (itemList.isEmpty()) // Master is not yet in the widget - { + if (itemList.isEmpty()) { // Master is not yet in the widget mMastersWidget->insertRow(i); QTableWidgetItem *item = new QTableWidgetItem(currentMaster); @@ -435,18 +462,18 @@ void DataFilesPage::deleteProfile() return; } - QMessageBox deleteMessageBox(this); - deleteMessageBox.setWindowTitle(tr("Delete Profile")); - deleteMessageBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); - deleteMessageBox.setIcon(QMessageBox::Warning); + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Delete Profile")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); + QAbstractButton *deleteButton = - deleteMessageBox.addButton(tr("Delete"), QMessageBox::ActionRole); + msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); - deleteMessageBox.addButton(QMessageBox::Cancel); + msgBox.exec(); - deleteMessageBox.exec(); - - if (deleteMessageBox.clickedButton() == deleteButton) { + if (msgBox.clickedButton() == deleteButton) { // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { mLauncherConfig->endGroup(); @@ -503,7 +530,6 @@ void DataFilesPage::moveUp() void DataFilesPage::moveDown() { // Shift the selected plugins down one row - if (!mPluginsTable->selectionModel()->hasSelection()) { return; } @@ -919,9 +945,18 @@ void DataFilesPage::filterChanged(const QString filter) void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) { + // Prevent the deletion of the default profile + if (current == "Default") { + mDeleteProfileAction->setEnabled(false); + } else { + mDeleteProfileAction->setEnabled(true); + } + if (!previous.isEmpty()) { writeConfig(previous); mLauncherConfig->sync(); + } else { + return; } uncheckPlugins(); @@ -989,22 +1024,12 @@ void DataFilesPage::readConfig() void DataFilesPage::writeConfig(QString profile) { - // Don't overwrite the config if no plugins are found - if (mPluginsModel->rowCount() < 1) { + // Don't overwrite the config if no masters are found + if (mMastersWidget->rowCount() < 1) { return; } - if (profile.isEmpty()) { - profile = mProfilesComboBox->currentText(); - } - - if (profile.isEmpty()) { - return; - } - - // Prepare the OpenMW config - - // Open the config as a QFile + // Open the OpenMW config as a QFile QFile file(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { @@ -1023,10 +1048,14 @@ void DataFilesPage::writeConfig(QString profile) QTextStream in(&file); QByteArray buffer; - // Remove all previous master/plugin entries from config + // Remove all previous entries from config while (!in.atEnd()) { QString line = in.readLine(); - if (!line.contains("master") && !line.contains("plugin")) { + if (!line.startsWith("master") && + !line.startsWith("plugin") && + !line.startsWith("data") && + !line.startsWith("data-local")) + { buffer += line += "\n"; } } @@ -1046,9 +1075,52 @@ void DataFilesPage::writeConfig(QString profile) QApplication::exit(1); } - file.write(buffer); + if (!buffer.isEmpty()) { + file.write(buffer); + } + QTextStream gameConfig(&file); + // First write the list of data dirs + mCfgMgr.processPaths(mDataDirs); + mCfgMgr.processPaths(mDataLocal); + + QString path; + + // data= directories + for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) { + path = QString::fromStdString(it->string()); + path.remove(QChar('\"')); + + // Make sure the string is quoted when it contains spaces + if (path.contains(" ")) { + gameConfig << "data=\"" << path << "\"" << endl; + } else { + gameConfig << "data=" << path << endl; + } + } + + // data-local directory + if (!mDataLocal.empty()) { + path = QString::fromStdString(mDataLocal.front().string()); + path.remove(QChar('\"')); + + if (path.contains(" ")) { + gameConfig << "data-local=\"" << path << "\"" << endl; + } else { + gameConfig << "data-local=" << path << endl; + } + } + + + if (profile.isEmpty()) { + profile = mProfilesComboBox->currentText(); + } + + if (profile.isEmpty()) { + return; + } + // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { mLauncherConfig->endGroup(); @@ -1061,7 +1133,7 @@ void DataFilesPage::writeConfig(QString profile) mLauncherConfig->beginGroup(profile); mLauncherConfig->remove(""); // Clear the subgroup - // First write the masters to the configs + // Now write the masters to the configs const QStringList masters = selectedMasters(); // We don't use foreach because we need i @@ -1073,11 +1145,10 @@ void DataFilesPage::writeConfig(QString profile) } - // Now write all checked plugins + // And finally write all checked plugins const QStringList plugins = checkedPlugins(); - for (int i = 0; i < plugins.size(); ++i) - { + for (int i = 0; i < plugins.size(); ++i) { const QString currentPlugin = plugins.at(i); mLauncherConfig->setValue(QString("Plugin%1").arg(i), currentPlugin); gameConfig << "plugin=" << currentPlugin << endl; @@ -1086,5 +1157,6 @@ void DataFilesPage::writeConfig(QString profile) file.close(); mLauncherConfig->endGroup(); mLauncherConfig->endGroup(); + mLauncherConfig->sync(); } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index a454fa871b..ad5e90511c 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -3,6 +3,9 @@ #include #include + +#include + #include "combobox.hpp" class QTableWidget; @@ -77,6 +80,8 @@ private: QAction *mUncheckAction; Files::ConfigurationManager &mCfgMgr; + Files::PathContainer mDataDirs; + Files::PathContainer mDataLocal; QSettings *mLauncherConfig; From b320733e454452251a2c8ac552e07c1e0dabed2d Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 28 Feb 2012 21:00:02 +0100 Subject: [PATCH 223/269] Fixed Qt exits, this time for all occurrences --- apps/launcher/datafilespage.cpp | 6 ++++-- apps/launcher/graphicspage.cpp | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index d3968f2bf3..b0d9779d52 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -1015,7 +1015,8 @@ void DataFilesPage::writeConfig(QString profile) Please make sure you have the right permissions and try again.
").arg(file.fileName())); msgBox.exec(); - QApplication::exit(1); + qApp->exit(1); + return; } QTextStream in(&file); @@ -1041,7 +1042,8 @@ void DataFilesPage::writeConfig(QString profile) Please make sure you have the right permissions and try again.
").arg(file.fileName())); msgBox.exec(); - QApplication::exit(1); + qApp->exit(1); + return; } file.write(buffer); diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index c9dca18793..aaa54dd7fc 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -180,7 +180,7 @@ void GraphicsPage::setupOgre() Make sure you have write access to
%1

")).arg(configDir.path())); msgBox.exec(); - QApplication::exit(1); + qApp->exit(1); return; } @@ -203,7 +203,7 @@ void GraphicsPage::setupOgre() qCritical("Error creating Ogre::Root, the error reported was:\n %s", qPrintable(ogreError)); - QApplication::exit(1); + qApp->exit(1); return; } @@ -237,7 +237,7 @@ void GraphicsPage::setupOgre() Please make sure the plugins.cfg file exists and contains a valid rendering plugin.
")); msgBox.exec(); - QApplication::exit(1); + qApp->exit(1); return; } @@ -423,7 +423,7 @@ void GraphicsPage::writeConfig() qCritical("Error validating configuration"); - QApplication::exit(1); + qApp->exit(1); return; } @@ -449,7 +449,8 @@ void GraphicsPage::writeConfig() qCritical("Error saving Ogre configuration, the error reported was:\n %s", qPrintable(ogreError)); - QApplication::exit(1); + qApp->exit(1); + return; } } From 11ec5cf2e28fd5ab6dd2a155539cd17474f9bf51 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Tue, 28 Feb 2012 22:35:29 +0100 Subject: [PATCH 224/269] Stylesheet location changed in CMakeLists and some minor fixes to the launcher --- CMakeLists.txt | 2 -- apps/launcher/CMakeLists.txt | 8 ++++++-- apps/launcher/datafilespage.cpp | 14 ++++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e62391c741..4c686733b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -329,7 +329,6 @@ if(WIN32) FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*") INSTALL(FILES ${files} DESTINATION ".") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") - INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION ".") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") SET(CPACK_GENERATOR "NSIS") @@ -428,7 +427,6 @@ if (APPLE) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) - install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index d648f4674e..3a5ccdd04b 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -72,10 +72,14 @@ endif() if (APPLE) configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${APP_BUNDLE_DIR}/../launcher.qss") - configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss + "${APP_BUNDLE_DIR}/../resources/launcher.qss") + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg "${APP_BUNDLE_DIR}/../launcher.cfg") else() + configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/launcher.qss") + + # Fallback in case getGlobalDataPath does not point to resources configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss") diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index b0d9779d52..b88664f0cc 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -987,8 +987,8 @@ void DataFilesPage::readConfig() void DataFilesPage::writeConfig(QString profile) { - // Don't overwrite the config if no plugins are found - if (mPluginsModel->rowCount() < 1) { + // Don't overwrite the config if no masters are found + if (mMastersWidget->rowCount() < 1) { return; } @@ -1001,9 +1001,15 @@ void DataFilesPage::writeConfig(QString profile) } // Prepare the OpenMW config + QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "openmw.cfg").string()); + QFile file(config); + + if (!file.exists()) { + config = QString::fromStdString((mCfgMgr.getUserPath() / "openmw.cfg").string()); + } // Open the config as a QFile - QFile file(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); + file.setFileName(config); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { // File cannot be opened or created @@ -1086,5 +1092,5 @@ void DataFilesPage::writeConfig(QString profile) file.close(); mLauncherConfig->endGroup(); mLauncherConfig->endGroup(); - + mLauncherConfig->sync(); } From b6915a609095033fd622eec946d5a4106825103f Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Thu, 1 Mar 2012 01:03:31 +0100 Subject: [PATCH 225/269] Forgot that IsWow64Process would return false on a native 64-bit application --- components/files/windowspath.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/windowspath.cpp b/components/files/windowspath.cpp index cf73b37289..dfa8f20cc3 100644 --- a/components/files/windowspath.cpp +++ b/components/files/windowspath.cpp @@ -75,7 +75,7 @@ boost::filesystem::path WindowsPath::getInstallPath() const BOOL f64 = FALSE; LPCTSTR regkey; - if (IsWow64Process(GetCurrentProcess(), &f64) && f64) + if ((IsWow64Process(GetCurrentProcess(), &f64) && f64) || sizeof(void*) == 8) { regkey = "SOFTWARE\\Wow6432Node\\Bethesda Softworks\\Morrowind"; } From 5ffa3264b2ad3c404f9f13d6f36a05cdfc8276a3 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Thu, 1 Mar 2012 04:38:37 +0100 Subject: [PATCH 226/269] Appended resources/ to the stylesheet path --- apps/launcher/maindialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 14f452fa3f..49c0bd960e 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -46,7 +46,7 @@ MainDialog::MainDialog() setMinimumSize(QSize(575, 575)); // Load the stylesheet - QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "launcher.qss").string()); + QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string()); QFile file(config); if (!file.exists()) { From 2c4ef5c6703ecf7eccb1182f31aa428a5c451110 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Thu, 1 Mar 2012 01:32:02 +0100 Subject: [PATCH 227/269] 64-bit install path on windows was easier to fix than expected. --- CMakeLists.txt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c686733b3..57c582c93a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -347,11 +347,17 @@ if(WIN32) SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe") - SET(VCREDIST "${OpenMW_BINARY_DIR}/vcredist_x86.exe") - if(EXISTS ${VCREDIST}) - INSTALL(FILES ${VCREDIST} DESTINATION "redist") + SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") + if(EXISTS ${VCREDIST32}) + INSTALL(FILES ${VCREDIST32} DESTINATION "redist") SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x86.exe\\\" /q'" ) - endif(EXISTS ${VCREDIST}) + endif(EXISTS ${VCREDIST32}) + + SET(VCREDIST64 "${OpenMW_BINARY_DIR}/vcredist_x64.exe") + if(EXISTS ${VCREDIST64}) + INSTALL(FILES ${VCREDIST64} DESTINATION "redist") + SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\redist\\\\vcredist_x64.exe\\\" /q'" ) + endif(EXISTS ${VCREDIST64}) SET(OALREDIST "${OpenMW_BINARY_DIR}/oalinst.exe") if(EXISTS ${OALREDIST}) @@ -360,6 +366,10 @@ if(WIN32) ExecWait '\\\"$INSTDIR\\\\redist\\\\oalinst.exe\\\" /s'" ) endif(EXISTS ${OALREDIST}) + if(CMAKE_CL_64) + SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") + endif() + include(CPack) endif(WIN32) From a1cbc7fb429e0d482a7beb8c59eef81256c6cdd3 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Thu, 1 Mar 2012 14:00:45 +0200 Subject: [PATCH 228/269] Altered getFver to fix a warning --- components/esm/esm_reader.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/esm_reader.hpp b/components/esm/esm_reader.hpp index e5b230748c..0420f37cd6 100644 --- a/components/esm/esm_reader.hpp +++ b/components/esm/esm_reader.hpp @@ -153,7 +153,7 @@ public: *************************************************************************/ int getVer() { return mCtx.header.version; } - float getFVer() { return *((float*)&mCtx.header.version); } + float getFVer() { if(mCtx.header.version == VER_12) return 1.2; else return 1.3; } int getSpecial() { return mSpf; } const std::string getAuthor() { return mCtx.header.author.toString(); } const std::string getDesc() { return mCtx.header.desc.toString(); } From 185cd634624a2fbe9254044b63b8ebde47b9b915 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Thu, 1 Mar 2012 14:19:27 +0200 Subject: [PATCH 229/269] ESMTool set to use default Latin encoding --- apps/esmtool/esmtool.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index fe067d85d1..4536dd3380 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -29,6 +29,7 @@ int main(int argc, char**argv) } ESMReader esm; + esm.setEncoding("win1252"); // FIXME: This should be configurable const char* filename = info.inputs[0]; cout << "\nFile: " << filename << endl; From b283ad86fbea7ffe9d760bcaf226929d6638f9c2 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 1 Mar 2012 17:15:42 +0400 Subject: [PATCH 230/269] revert to old launcher stylesheet location on OS X --- CMakeLists.txt | 1 + apps/launcher/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c686733b3..76b9595e55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -427,6 +427,7 @@ if (APPLE) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 3a5ccdd04b..319bbbc576 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -72,7 +72,7 @@ endif() if (APPLE) configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${APP_BUNDLE_DIR}/../resources/launcher.qss") + "${APP_BUNDLE_DIR}/../launcher.qss") configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg "${APP_BUNDLE_DIR}/../launcher.cfg") else() From b3159683a497640181a576989043fe6767bc4f50 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Fri, 2 Mar 2012 04:41:29 +0200 Subject: [PATCH 231/269] Replace gengetopt with bullet program options for esmtool's argument handling --- apps/esmtool/CMakeLists.txt | 2 - apps/esmtool/esmtool.cpp | 134 +++- apps/esmtool/esmtool.ggo | 10 - apps/esmtool/esmtool_cmd.c | 1141 ----------------------------------- apps/esmtool/esmtool_cmd.h | 179 ------ 5 files changed, 118 insertions(+), 1348 deletions(-) delete mode 100644 apps/esmtool/esmtool.ggo delete mode 100644 apps/esmtool/esmtool_cmd.c delete mode 100644 apps/esmtool/esmtool_cmd.h diff --git a/apps/esmtool/CMakeLists.txt b/apps/esmtool/CMakeLists.txt index f2ab7bce74..af3dc090e0 100644 --- a/apps/esmtool/CMakeLists.txt +++ b/apps/esmtool/CMakeLists.txt @@ -1,6 +1,4 @@ set(ESMTOOL - esmtool_cmd.c - esmtool_cmd.h esmtool.cpp ) source_group(apps\\esmtool FILES ${ESMTOOL}) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 4536dd3380..f417d5c608 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -1,36 +1,138 @@ +#include + +#include + #include #include -#include "esmtool_cmd.h" - -#include +#define ESMTOOL_VERSION 1.1 using namespace std; using namespace ESM; +// Create a local alias for brevity +namespace bpo = boost::program_options; + void printRaw(ESMReader &esm); void loadCell(Cell &cell, ESMReader &esm, bool quiet); -int main(int argc, char**argv) +// Based on the legacy struct +struct Arguments { - gengetopt_args_info info; + unsigned int raw_given; + unsigned int quiet_given; + unsigned int loadcells_given; + std::string encoding; + std::string filename; +}; - if(cmdline_parser(argc, argv, &info) != 0) - return 1; +bool parseOptions (int argc, char** argv, Arguments &info) +{ + bpo::options_description desc("Inspect and extract from Morrowind ES files (ESM, ESP, ESS)\nSyntax: esmtool [options] file \nAllowed options"); - if(info.inputs_num != 1) + desc.add_options() + ("help,h", "print help message.") + ("version,v", "print version information and quit.") + ("raw,r", "Show an unformattet list of all records and subrecords.") + ("quiet,q", "Supress all record information. Useful for speed tests.") + ("loadcells,C", "Browse through contents of all cells.") + + ( "encoding,e", bpo::value(&(info.encoding))-> + default_value("win1252"), + "Character encoding used in ESMTool:\n" + "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" + "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" + "\n\twin1252 - Western European (Latin) alphabet, used by default") + ; + + std::string finalText = "\nIf no option is given, the default action is to parse all records in the archive\nand display diagnostic information."; + + // input-file is hidden and used as a positional argument + bpo::options_description hidden("Hidden Options"); + + hidden.add_options() + ( "input-file,i", bpo::value< vector >(), "input file") + ; + + bpo::positional_options_description p; + p.add("input-file", -1); + + // there might be a better way to do this + bpo::options_description all; + all.add(desc).add(hidden); + bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) + .options(all).positional(p).run(); + + bpo::variables_map variables; + bpo::store(valid_opts, variables); + bpo::notify(variables); + + if (variables.count ("help")) { - if(info.inputs_num == 0) - cout << "ERROR: missing ES file\n\n"; - else - cout << "ERROR: more than one ES file specified\n\n"; - cmdline_parser_print_help(); - return 1; + std::cout << desc << finalText << std::endl; + return false; + } + if (variables.count ("version")) + { + std::cout << "ESMTool version " << ESMTOOL_VERSION << std::endl; + return false; } + if ( !variables.count("input-file") ) + { + std::cout << "\nERROR: missing ES file\n\n"; + std::cout << desc << finalText << std::endl; + return false; + } + + // handling gracefully the user adding multiple files + if (variables["input-file"].as< vector >().size() > 1) + { + std::cout << "\nERROR: more than one ES file specified\n\n"; + std::cout << desc << finalText << std::endl; + return false; + } + + info.filename = variables["input-file"].as< vector >()[0]; + + info.raw_given = variables.count ("raw"); + info.quiet_given = variables.count ("quiet"); + info.loadcells_given = variables.count ("loadcells"); + + // Font encoding settings + info.encoding = variables["encoding"].as(); + if (info.encoding == "win1250") + { + std::cout << "Using Central and Eastern European font encoding." << std::endl; + } + else if (info.encoding == "win1251") + { + std::cout << "Using Cyrillic font encoding." << std::endl; + } + else + { + if(info.encoding != "win1252") + { + std::cout << info.encoding << " is not a valid encoding option." << std::endl; + info.encoding = "win1252"; + } + std::cout << "Using default (English) font encoding." << std::endl; + } + + return true; +} + + +int main(int argc, char**argv) +{ + Arguments info; + if(!parseOptions (argc, argv, info)) + return 1; + ESMReader esm; - esm.setEncoding("win1252"); // FIXME: This should be configurable - const char* filename = info.inputs[0]; + esm.setEncoding(info.encoding); + + string filename = info.filename; cout << "\nFile: " << filename << endl; try { diff --git a/apps/esmtool/esmtool.ggo b/apps/esmtool/esmtool.ggo deleted file mode 100644 index 9d0f3c1893..0000000000 --- a/apps/esmtool/esmtool.ggo +++ /dev/null @@ -1,10 +0,0 @@ -package "esmtool" -version "1.0" -purpose "Inspect and extract from Morrowind ES files (ESM, ESP, ESS)" -args "--unamed-opts=ES-FILE -F esmtool_cmd -G" - -option "raw" r "Show an unformattet list of all records and subrecords" optional -option "quiet" q "Supress all record information. Useful for speed tests." optional -option "loadcells" C "Browse through contents of all cells." optional - -text "\nIf no option is given, the default action is to parse all records in the archive and display diagnostic information." diff --git a/apps/esmtool/esmtool_cmd.c b/apps/esmtool/esmtool_cmd.c deleted file mode 100644 index 3fce77de2b..0000000000 --- a/apps/esmtool/esmtool_cmd.c +++ /dev/null @@ -1,1141 +0,0 @@ -/* - File autogenerated by gengetopt version 2.22.2 - generated with the following command: - gengetopt --unamed-opts=ES-FILE -F esmtool_cmd -G - - The developers of gengetopt consider the fixed text that goes in all - gengetopt output files to be in the public domain: - we make no copyright claims on it. -*/ - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -#ifndef FIX_UNUSED -#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ -#endif - - -#include "esmtool_cmd.h" - -const char *gengetopt_args_info_purpose = "Inspect and extract from Morrowind ES files (ESM, ESP, ESS)"; - -const char *gengetopt_args_info_usage = "Usage: esmtool [OPTIONS]... [ES-FILE]..."; - -const char *gengetopt_args_info_description = ""; - -const char *gengetopt_args_info_help[] = { - " -h, --help Print help and exit", - " -V, --version Print version and exit", - " -r, --raw Show an unformattet list of all records and subrecords", - " -q, --quiet Supress all record information. Useful for speed tests.", - " -C, --loadcells Browse through contents of all cells.", - "\nIf no option is given, the default action is to parse all records in the \narchive and display diagnostic information.", - 0 -}; - -typedef enum {ARG_NO -} cmdline_parser_arg_type; - -static -void clear_given (struct gengetopt_args_info *args_info); -static -void clear_args (struct gengetopt_args_info *args_info); - -static int -cmdline_parser_internal (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error); - - -static char * -gengetopt_strdup (const char *s); - -static -void clear_given (struct gengetopt_args_info *args_info) -{ - args_info->help_given = 0 ; - args_info->version_given = 0 ; - args_info->raw_given = 0 ; - args_info->quiet_given = 0 ; - args_info->loadcells_given = 0 ; -} - -static -void clear_args (struct gengetopt_args_info *args_info) -{ - FIX_UNUSED (args_info); - -} - -static -void init_args_info(struct gengetopt_args_info *args_info) -{ - - - args_info->help_help = gengetopt_args_info_help[0] ; - args_info->version_help = gengetopt_args_info_help[1] ; - args_info->raw_help = gengetopt_args_info_help[2] ; - args_info->quiet_help = gengetopt_args_info_help[3] ; - args_info->loadcells_help = gengetopt_args_info_help[4] ; - -} - -void -cmdline_parser_print_version (void) -{ - printf ("%s %s\n", - (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), - CMDLINE_PARSER_VERSION); -} - -static void print_help_common(void) { - cmdline_parser_print_version (); - - if (strlen(gengetopt_args_info_purpose) > 0) - printf("\n%s\n", gengetopt_args_info_purpose); - - if (strlen(gengetopt_args_info_usage) > 0) - printf("\n%s\n", gengetopt_args_info_usage); - - printf("\n"); - - if (strlen(gengetopt_args_info_description) > 0) - printf("%s\n\n", gengetopt_args_info_description); -} - -void -cmdline_parser_print_help (void) -{ - int i = 0; - print_help_common(); - while (gengetopt_args_info_help[i]) - printf("%s\n", gengetopt_args_info_help[i++]); -} - -void -cmdline_parser_init (struct gengetopt_args_info *args_info) -{ - clear_given (args_info); - clear_args (args_info); - init_args_info (args_info); - - args_info->inputs = 0; - args_info->inputs_num = 0; -} - -void -cmdline_parser_params_init(struct cmdline_parser_params *params) -{ - if (params) - { - params->override = 0; - params->initialize = 1; - params->check_required = 1; - params->check_ambiguity = 0; - params->print_errors = 1; - } -} - -struct cmdline_parser_params * -cmdline_parser_params_create(void) -{ - struct cmdline_parser_params *params = - (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); - cmdline_parser_params_init(params); - return params; -} - - - -static void -cmdline_parser_release (struct gengetopt_args_info *args_info) -{ - unsigned int i; - - - for (i = 0; i < args_info->inputs_num; ++i) - free (args_info->inputs [i]); - - if (args_info->inputs_num) - free (args_info->inputs); - - clear_given (args_info); -} - - -static void -write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) -{ - FIX_UNUSED (values); - if (arg) { - fprintf(outfile, "%s=\"%s\"\n", opt, arg); - } else { - fprintf(outfile, "%s\n", opt); - } -} - - -int -cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) -{ - int i = 0; - - if (!outfile) - { - fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); - return EXIT_FAILURE; - } - - if (args_info->help_given) - write_into_file(outfile, "help", 0, 0 ); - if (args_info->version_given) - write_into_file(outfile, "version", 0, 0 ); - if (args_info->raw_given) - write_into_file(outfile, "raw", 0, 0 ); - if (args_info->quiet_given) - write_into_file(outfile, "quiet", 0, 0 ); - if (args_info->loadcells_given) - write_into_file(outfile, "loadcells", 0, 0 ); - - - i = EXIT_SUCCESS; - return i; -} - -int -cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) -{ - FILE *outfile; - int i = 0; - - outfile = fopen(filename, "w"); - - if (!outfile) - { - fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); - return EXIT_FAILURE; - } - - i = cmdline_parser_dump(outfile, args_info); - fclose (outfile); - - return i; -} - -void -cmdline_parser_free (struct gengetopt_args_info *args_info) -{ - cmdline_parser_release (args_info); -} - -/** @brief replacement of strdup, which is not standard */ -char * -gengetopt_strdup (const char *s) -{ - char *result = 0; - if (!s) - return result; - - result = (char*)malloc(strlen(s) + 1); - if (result == (char*)0) - return (char*)0; - strcpy(result, s); - return result; -} - -int -cmdline_parser (int argc, char * const *argv, struct gengetopt_args_info *args_info) -{ - return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); -} - -int -cmdline_parser_ext (int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params) -{ - int result; - result = cmdline_parser_internal (argc, argv, args_info, params, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser2 (int argc, char * const *argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) -{ - int result; - struct cmdline_parser_params params; - - params.override = override; - params.initialize = initialize; - params.check_required = check_required; - params.check_ambiguity = 0; - params.print_errors = 1; - - result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); - - if (result == EXIT_FAILURE) - { - cmdline_parser_free (args_info); - exit (EXIT_FAILURE); - } - - return result; -} - -int -cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) -{ - FIX_UNUSED (args_info); - FIX_UNUSED (prog_name); - return EXIT_SUCCESS; -} - -/* - * Extracted from the glibc source tree, version 2.3.6 - * - * Licensed under the GPL as per the whole glibc source tree. - * - * This file was modified so that getopt_long can be called - * many times without risking previous memory to be spoiled. - * - * Modified by Andre Noll and Lorenzo Bettini for use in - * GNU gengetopt generated files. - * - */ - -/* - * we must include anything we need since this file is not thought to be - * inserted in a file already using getopt.h - * - * Lorenzo - */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. -*/ -/* - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `custom_optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -/* Names for the values of the `has_arg' field of `struct option'. */ -#ifndef no_argument -#define no_argument 0 -#endif - -#ifndef required_argument -#define required_argument 1 -#endif - -#ifndef optional_argument -#define optional_argument 2 -#endif - -struct custom_getopt_data { - /* - * These have exactly the same meaning as the corresponding global variables, - * except that they are used for the reentrant versions of getopt. - */ - int custom_optind; - int custom_opterr; - int custom_optopt; - char *custom_optarg; - - /* True if the internal members have been initialized. */ - int initialized; - - /* - * The next char to be scanned in the option-element in which the last option - * character we returned was found. This allows us to pick up the scan where - * we left off. If this is zero, or a null string, it means resume the scan by - * advancing to the next ARGV-element. - */ - char *nextchar; - - /* - * Describe the part of ARGV that contains non-options that have been skipped. - * `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is - * the index after the last of them. - */ - int first_nonopt; - int last_nonopt; -}; - -/* - * the variables optarg, optind, opterr and optopt are renamed with - * the custom_ prefix so that they don't interfere with getopt ones. - * - * Moreover they're static so they are visible only from within the - * file where this very file will be included. - */ - -/* - * For communication from `custom_getopt' to the caller. When `custom_getopt' finds an - * option that takes an argument, the argument value is returned here. - */ -static char *custom_optarg; - -/* - * Index in ARGV of the next element to be scanned. This is used for - * communication to and from the caller and for communication between - * successive calls to `custom_getopt'. - * - * On entry to `custom_getopt', 1 means this is the first call; initialize. - * - * When `custom_getopt' returns -1, this is the index of the first of the non-option - * elements that the caller should itself scan. - * - * Otherwise, `custom_optind' communicates from one call to the next how much of ARGV - * has been scanned so far. - * - * 1003.2 says this must be 1 before any call. - */ -static int custom_optind = 1; - -/* - * Callers store zero here to inhibit the error message for unrecognized - * options. - */ -static int custom_opterr = 1; - -/* - * Set to an option character which was unrecognized. This must be initialized - * on some systems to avoid linking in the system's own getopt implementation. - */ -static int custom_optopt = '?'; - -/* - * Exchange two adjacent subsequences of ARGV. One subsequence is elements - * [first_nonopt,last_nonopt) which contains all the non-options that have been - * skipped so far. The other is elements [last_nonopt,custom_optind), which contains - * all the options processed since those non-options were skipped. - * `first_nonopt' and `last_nonopt' are relocated so that they describe the new - * indices of the non-options in ARGV after they are moved. - */ -static void exchange(char **argv, struct custom_getopt_data *d) -{ - int bottom = d->first_nonopt; - int middle = d->last_nonopt; - int top = d->custom_optind; - char *tem; - - /* - * Exchange the shorter segment with the far end of the longer segment. - * That puts the shorter segment into the right place. It leaves the - * longer segment in the right place overall, but it consists of two - * parts that need to be swapped next. - */ - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { - /* Bottom segment is the short one. */ - int len = middle - bottom; - int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = - argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } else { - /* Top segment is the short one. */ - int len = top - middle; - int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - /* Update records for the slots the non-options now occupy. */ - d->first_nonopt += (d->custom_optind - d->last_nonopt); - d->last_nonopt = d->custom_optind; -} - -/* Initialize the internal data when the first call is made. */ -static void custom_getopt_initialize(struct custom_getopt_data *d) -{ - /* - * Start processing options with ARGV-element 1 (since ARGV-element 0 - * is the program name); the sequence of previously skipped non-option - * ARGV-elements is empty. - */ - d->first_nonopt = d->last_nonopt = d->custom_optind; - d->nextchar = NULL; - d->initialized = 1; -} - -#define NONOPTION_P (argv[d->custom_optind][0] != '-' || argv[d->custom_optind][1] == '\0') - -/* return: zero: continue, nonzero: return given value to user */ -static int shuffle_argv(int argc, char *const *argv,const struct option *longopts, - struct custom_getopt_data *d) -{ - /* - * Give FIRST_NONOPT & LAST_NONOPT rational values if CUSTOM_OPTIND has been - * moved back by the user (who may also have changed the arguments). - */ - if (d->last_nonopt > d->custom_optind) - d->last_nonopt = d->custom_optind; - if (d->first_nonopt > d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * If we have just processed some options following some - * non-options, exchange them so that the options come first. - */ - if (d->first_nonopt != d->last_nonopt && - d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->last_nonopt != d->custom_optind) - d->first_nonopt = d->custom_optind; - /* - * Skip any additional non-options and extend the range of - * non-options previously skipped. - */ - while (d->custom_optind < argc && NONOPTION_P) - d->custom_optind++; - d->last_nonopt = d->custom_optind; - /* - * The special ARGV-element `--' means premature end of options. Skip - * it like a null option, then exchange with previous non-options as if - * it were an option, then skip everything else like a non-option. - */ - if (d->custom_optind != argc && !strcmp(argv[d->custom_optind], "--")) { - d->custom_optind++; - if (d->first_nonopt != d->last_nonopt - && d->last_nonopt != d->custom_optind) - exchange((char **) argv, d); - else if (d->first_nonopt == d->last_nonopt) - d->first_nonopt = d->custom_optind; - d->last_nonopt = argc; - d->custom_optind = argc; - } - /* - * If we have done all the ARGV-elements, stop the scan and back over - * any non-options that we skipped and permuted. - */ - if (d->custom_optind == argc) { - /* - * Set the next-arg-index to point at the non-options that we - * previously skipped, so the caller will digest them. - */ - if (d->first_nonopt != d->last_nonopt) - d->custom_optind = d->first_nonopt; - return -1; - } - /* - * If we have come to a non-option and did not permute it, either stop - * the scan or describe it to the caller and pass it by. - */ - if (NONOPTION_P) { - d->custom_optarg = argv[d->custom_optind++]; - return 1; - } - /* - * We have found another option-ARGV-element. Skip the initial - * punctuation. - */ - d->nextchar = (argv[d->custom_optind] + 1 + (longopts != NULL && argv[d->custom_optind][1] == '-')); - return 0; -} - -/* - * Check whether the ARGV-element is a long option. - * - * If there's a long option "fubar" and the ARGV-element is "-fu", consider - * that an abbreviation of the long option, just like "--fu", and not "-f" with - * arg "u". - * - * This distinction seems to be the most useful approach. - * - */ -static int check_long_opt(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - int print_errors, struct custom_getopt_data *d) -{ - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = d->nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match or abbreviated matches */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp(p->name, d->nextchar, nameend - d->nextchar)) { - if ((unsigned int) (nameend - d->nextchar) - == (unsigned int) strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } else if (pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' is ambiguous\n", - argv[0], argv[d->custom_optind]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optind++; - d->custom_optopt = 0; - return '?'; - } - if (pfound) { - option_index = indfound; - d->custom_optind++; - if (*nameend) { - if (pfound->has_arg != no_argument) - d->custom_optarg = nameend + 1; - else { - if (print_errors) { - if (argv[d->custom_optind - 1][1] == '-') { - /* --option */ - fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", - argv[0], pfound->name); - } else { - /* +option or -option */ - fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", - argv[0], argv[d->custom_optind - 1][0], pfound->name); - } - - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return '?'; - } - } else if (pfound->has_arg == required_argument) { - if (d->custom_optind < argc) - d->custom_optarg = argv[d->custom_optind++]; - else { - if (print_errors) { - fprintf(stderr, - "%s: option `%s' requires an argument\n", - argv[0], - argv[d->custom_optind - 1]); - } - d->nextchar += strlen(d->nextchar); - d->custom_optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - d->nextchar += strlen(d->nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - /* - * Can't find it as a long option. If this is not getopt_long_only, or - * the option starts with '--' or is not a valid short option, then - * it's an error. Otherwise interpret it as a short option. - */ - if (print_errors) { - if (argv[d->custom_optind][1] == '-') { - /* --option */ - fprintf(stderr, - "%s: unrecognized option `--%s'\n", - argv[0], d->nextchar); - } else { - /* +option or -option */ - fprintf(stderr, - "%s: unrecognized option `%c%s'\n", - argv[0], argv[d->custom_optind][0], - d->nextchar); - } - } - d->nextchar = (char *) ""; - d->custom_optind++; - d->custom_optopt = 0; - return '?'; -} - -static int check_short_opt(int argc, char *const *argv, const char *optstring, - int print_errors, struct custom_getopt_data *d) -{ - char c = *d->nextchar++; - const char *temp = strchr(optstring, c); - - /* Increment `custom_optind' when we start to process its last character. */ - if (*d->nextchar == '\0') - ++d->custom_optind; - if (!temp || c == ':') { - if (print_errors) - fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); - - d->custom_optopt = c; - return '?'; - } - if (temp[1] == ':') { - if (temp[2] == ':') { - /* This is an option that accepts an argument optionally. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - d->custom_optind++; - } else - d->custom_optarg = NULL; - d->nextchar = NULL; - } else { - /* This is an option that requires an argument. */ - if (*d->nextchar != '\0') { - d->custom_optarg = d->nextchar; - /* - * If we end this ARGV-element by taking the - * rest as an arg, we must advance to the next - * element now. - */ - d->custom_optind++; - } else if (d->custom_optind == argc) { - if (print_errors) { - fprintf(stderr, - "%s: option requires an argument -- %c\n", - argv[0], c); - } - d->custom_optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } else - /* - * We already incremented `custom_optind' once; - * increment it again when taking next ARGV-elt - * as argument. - */ - d->custom_optarg = argv[d->custom_optind++]; - d->nextchar = NULL; - } - } - return c; -} - -/* - * Scan elements of ARGV for option characters given in OPTSTRING. - * - * If an element of ARGV starts with '-', and is not exactly "-" or "--", - * then it is an option element. The characters of this element - * (aside from the initial '-') are option characters. If `getopt' - * is called repeatedly, it returns successively each of the option characters - * from each of the option elements. - * - * If `getopt' finds another option character, it returns that character, - * updating `custom_optind' and `nextchar' so that the next call to `getopt' can - * resume the scan with the following option character or ARGV-element. - * - * If there are no more option characters, `getopt' returns -1. - * Then `custom_optind' is the index in ARGV of the first ARGV-element - * that is not an option. (The ARGV-elements have been permuted - * so that those that are not options now come last.) - * - * OPTSTRING is a string containing the legitimate option characters. - * If an option character is seen that is not listed in OPTSTRING, - * return '?' after printing an error message. If you set `custom_opterr' to - * zero, the error message is suppressed but we still return '?'. - * - * If a char in OPTSTRING is followed by a colon, that means it wants an arg, - * so the following text in the same ARGV-element, or the text of the following - * ARGV-element, is returned in `custom_optarg'. Two colons mean an option that - * wants an optional arg; if there is text in the current ARGV-element, - * it is returned in `custom_optarg', otherwise `custom_optarg' is set to zero. - * - * If OPTSTRING starts with `-' or `+', it requests different methods of - * handling the non-option ARGV-elements. - * See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - * - * Long-named options begin with `--' instead of `-'. - * Their names may be abbreviated as long as the abbreviation is unique - * or is an exact match for some defined option. If they have an - * argument, it follows the option name in the same ARGV-element, separated - * from the option name by a `=', or else the in next ARGV-element. - * When `getopt' finds a long-named option, it returns 0 if that option's - * `flag' field is nonzero, the value of the option's `val' field - * if the `flag' field is zero. - * - * The elements of ARGV aren't really const, because we permute them. - * But we pretend they're const in the prototype to be compatible - * with other systems. - * - * LONGOPTS is a vector of `struct option' terminated by an - * element containing a name which is zero. - * - * LONGIND returns the index in LONGOPT of the long-named option found. - * It is only valid when a long-named option has been found by the most - * recent call. - * - * Return the option character from OPTS just read. Return -1 when there are - * no more options. For unrecognized options, or options missing arguments, - * `custom_optopt' is set to the option letter, and '?' is returned. - * - * The OPTS string is a list of characters which are recognized option letters, - * optionally followed by colons, specifying that that letter takes an - * argument, to be placed in `custom_optarg'. - * - * If a letter in OPTS is followed by two colons, its argument is optional. - * This behavior is specific to the GNU `getopt'. - * - * The argument `--' causes premature termination of argument scanning, - * explicitly telling `getopt' that there are no more options. If OPTS begins - * with `--', then non-option arguments are treated as arguments to the option - * '\0'. This behavior is specific to the GNU `getopt'. - */ - -static int getopt_internal_r(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - struct custom_getopt_data *d) -{ - int ret, print_errors = d->custom_opterr; - - if (optstring[0] == ':') - print_errors = 0; - if (argc < 1) - return -1; - d->custom_optarg = NULL; - - /* - * This is a big difference with GNU getopt, since optind == 0 - * means initialization while here 1 means first call. - */ - if (d->custom_optind == 0 || !d->initialized) { - if (d->custom_optind == 0) - d->custom_optind = 1; /* Don't scan ARGV[0], the program name. */ - custom_getopt_initialize(d); - } - if (d->nextchar == NULL || *d->nextchar == '\0') { - ret = shuffle_argv(argc, argv, longopts, d); - if (ret) - return ret; - } - if (longopts && (argv[d->custom_optind][1] == '-' )) - return check_long_opt(argc, argv, optstring, longopts, - longind, print_errors, d); - return check_short_opt(argc, argv, optstring, print_errors, d); -} - -static int custom_getopt_internal(int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind) -{ - int result; - /* Keep a global copy of all internal members of d */ - static struct custom_getopt_data d; - - d.custom_optind = custom_optind; - d.custom_opterr = custom_opterr; - result = getopt_internal_r(argc, argv, optstring, longopts, - longind, &d); - custom_optind = d.custom_optind; - custom_optarg = d.custom_optarg; - custom_optopt = d.custom_optopt; - return result; -} - -static int custom_getopt_long (int argc, char *const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return custom_getopt_internal(argc, argv, options, long_options, - opt_index); -} - - -static char *package_name = 0; - -/** - * @brief updates an option - * @param field the generic pointer to the field to update - * @param orig_field the pointer to the orig field - * @param field_given the pointer to the number of occurrence of this option - * @param prev_given the pointer to the number of occurrence already seen - * @param value the argument for this option (if null no arg was specified) - * @param possible_values the possible values for this option (if specified) - * @param default_value the default value (in case the option only accepts fixed values) - * @param arg_type the type of this option - * @param check_ambiguity @see cmdline_parser_params.check_ambiguity - * @param override @see cmdline_parser_params.override - * @param no_free whether to free a possible previous value - * @param multiple_option whether this is a multiple option - * @param long_opt the corresponding long option - * @param short_opt the corresponding short option (or '-' if none) - * @param additional_error possible further error specification - */ -static -int update_arg(void *field, char **orig_field, - unsigned int *field_given, unsigned int *prev_given, - char *value, const char *possible_values[], - const char *default_value, - cmdline_parser_arg_type arg_type, - int check_ambiguity, int override, - int no_free, int multiple_option, - const char *long_opt, char short_opt, - const char *additional_error) -{ - //char *stop_char = 0; - //const char *val = value; - //int found; - FIX_UNUSED (field); - - //stop_char = 0; - //found = 0; - - if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) - { - if (short_opt != '-') - fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", - package_name, long_opt, short_opt, - (additional_error ? additional_error : "")); - else - fprintf (stderr, "%s: `--%s' option given more than once%s\n", - package_name, long_opt, - (additional_error ? additional_error : "")); - return 1; /* failure */ - } - - FIX_UNUSED (default_value); - - if (field_given && *field_given && ! override) - return 0; - if (prev_given) - (*prev_given)++; - if (field_given) - (*field_given)++; - //if (possible_values) - //val = possible_values[found]; - - switch(arg_type) { - default: - break; - }; - - - /* store the original value */ - switch(arg_type) { - case ARG_NO: - break; - default: - if (value && orig_field) { - if (no_free) { - *orig_field = value; - } else { - if (*orig_field) - free (*orig_field); /* free previous string */ - *orig_field = gengetopt_strdup (value); - } - } - }; - - return 0; /* OK */ -} - - -int -cmdline_parser_internal ( - int argc, char * const *argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error) -{ - int c; /* Character of the parsed option. */ - - int error = 0; - struct gengetopt_args_info local_args_info; - - int override; - int initialize; - //int check_required; - int check_ambiguity; - - char *optarg; - int optind; - int opterr; - int optopt; - - package_name = argv[0]; - - override = params->override; - initialize = params->initialize; - //check_required = params->check_required; - check_ambiguity = params->check_ambiguity; - - if (initialize) - cmdline_parser_init (args_info); - - cmdline_parser_init (&local_args_info); - - optarg = 0; - optind = 0; - opterr = params->print_errors; - optopt = '?'; - - while (1) - { - int option_index = 0; - - static struct option long_options[] = { - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - { "raw", 0, NULL, 'r' }, - { "quiet", 0, NULL, 'q' }, - { "loadcells", 0, NULL, 'C' }, - { 0, 0, 0, 0 } - }; - - custom_optarg = optarg; - custom_optind = optind; - custom_opterr = opterr; - custom_optopt = optopt; - - c = custom_getopt_long (argc, argv, "hVrqC", long_options, &option_index); - - optarg = custom_optarg; - optind = custom_optind; - opterr = custom_opterr; - optopt = custom_optopt; - - if (c == -1) break; /* Exit from `while (1)' loop. */ - - switch (c) - { - case 'h': /* Print help and exit. */ - cmdline_parser_print_help (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'V': /* Print version and exit. */ - cmdline_parser_print_version (); - cmdline_parser_free (&local_args_info); - exit (EXIT_SUCCESS); - - case 'r': /* Show an unformattet list of all records and subrecords. */ - - - if (update_arg( 0 , - 0 , &(args_info->raw_given), - &(local_args_info.raw_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "raw", 'r', - additional_error)) - goto failure; - - break; - case 'q': /* Supress all record information. Useful for speed tests.. */ - - - if (update_arg( 0 , - 0 , &(args_info->quiet_given), - &(local_args_info.quiet_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "quiet", 'q', - additional_error)) - goto failure; - - break; - case 'C': /* Browse through contents of all cells.. */ - - - if (update_arg( 0 , - 0 , &(args_info->loadcells_given), - &(local_args_info.loadcells_given), optarg, 0, 0, ARG_NO, - check_ambiguity, override, 0, 0, - "loadcells", 'C', - additional_error)) - goto failure; - - break; - - case 0: /* Long option with no short option */ - case '?': /* Invalid option. */ - /* `getopt_long' already printed an error message. */ - goto failure; - - default: /* bug: option not considered. */ - fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); - abort (); - } /* switch */ - } /* while */ - - - - - cmdline_parser_release (&local_args_info); - - if ( error ) - return (EXIT_FAILURE); - - if (optind < argc) - { - int i = 0 ; - int found_prog_name = 0; - /* whether program name, i.e., argv[0], is in the remaining args - (this may happen with some implementations of getopt, - but surely not with the one included by gengetopt) */ - - - args_info->inputs_num = argc - optind - found_prog_name; - args_info->inputs = - (char **)(malloc ((args_info->inputs_num)*sizeof(char *))) ; - while (optind < argc) - args_info->inputs[ i++ ] = gengetopt_strdup (argv[optind++]) ; - } - - return 0; - -failure: - - cmdline_parser_release (&local_args_info); - return (EXIT_FAILURE); -} diff --git a/apps/esmtool/esmtool_cmd.h b/apps/esmtool/esmtool_cmd.h deleted file mode 100644 index 8c420c189e..0000000000 --- a/apps/esmtool/esmtool_cmd.h +++ /dev/null @@ -1,179 +0,0 @@ -/** @file esmtool_cmd.h - * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.2 - * http://www.gnu.org/software/gengetopt. - * DO NOT modify this file, since it can be overwritten - * @author GNU Gengetopt by Lorenzo Bettini */ - -#ifndef ESMTOOL_CMD_H -#define ESMTOOL_CMD_H - -/* If we use autoconf. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include /* for FILE */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef CMDLINE_PARSER_PACKAGE -/** @brief the program name (used for printing errors) */ -#define CMDLINE_PARSER_PACKAGE "esmtool" -#endif - -#ifndef CMDLINE_PARSER_PACKAGE_NAME -/** @brief the complete program name (used for help and version) */ -#define CMDLINE_PARSER_PACKAGE_NAME "esmtool" -#endif - -#ifndef CMDLINE_PARSER_VERSION -/** @brief the program version */ -#define CMDLINE_PARSER_VERSION "1.0" -#endif - -/** @brief Where the command line options are stored */ -struct gengetopt_args_info -{ - const char *help_help; /**< @brief Print help and exit help description. */ - const char *version_help; /**< @brief Print version and exit help description. */ - const char *raw_help; /**< @brief Show an unformattet list of all records and subrecords help description. */ - const char *quiet_help; /**< @brief Supress all record information. Useful for speed tests. help description. */ - const char *loadcells_help; /**< @brief Browse through contents of all cells. help description. */ - - unsigned int help_given ; /**< @brief Whether help was given. */ - unsigned int version_given ; /**< @brief Whether version was given. */ - unsigned int raw_given ; /**< @brief Whether raw was given. */ - unsigned int quiet_given ; /**< @brief Whether quiet was given. */ - unsigned int loadcells_given ; /**< @brief Whether loadcells was given. */ - - char **inputs ; /**< @brief unamed options (options without names) */ - unsigned inputs_num ; /**< @brief unamed options number */ -} ; - -/** @brief The additional parameters to pass to parser functions */ -struct cmdline_parser_params -{ - int override; /**< @brief whether to override possibly already present options (default 0) */ - int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ - int check_required; /**< @brief whether to check that all required options were provided (default 1) */ - int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ - int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ -} ; - -/** @brief the purpose string of the program */ -extern const char *gengetopt_args_info_purpose; -/** @brief the usage string of the program */ -extern const char *gengetopt_args_info_usage; -/** @brief all the lines making the help output */ -extern const char *gengetopt_args_info_help[]; - -/** - * The command line parser - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser (int argc, char * const *argv, - struct gengetopt_args_info *args_info); - -/** - * The command line parser (version with additional parameters - deprecated) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param override whether to override possibly already present options - * @param initialize whether to initialize the option structure my_args_info - * @param check_required whether to check that all required options were provided - * @return 0 if everything went fine, NON 0 if an error took place - * @deprecated use cmdline_parser_ext() instead - */ -int cmdline_parser2 (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - int override, int initialize, int check_required); - -/** - * The command line parser (version with additional parameters) - * @param argc the number of command line options - * @param argv the command line options - * @param args_info the structure where option information will be stored - * @param params additional parameters for the parser - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_ext (int argc, char * const *argv, - struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params); - -/** - * Save the contents of the option struct into an already open FILE stream. - * @param outfile the stream where to dump options - * @param args_info the option struct to dump - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_dump(FILE *outfile, - struct gengetopt_args_info *args_info); - -/** - * Save the contents of the option struct into a (text) file. - * This file can be read by the config file parser (if generated by gengetopt) - * @param filename the file where to save - * @param args_info the option struct to save - * @return 0 if everything went fine, NON 0 if an error took place - */ -int cmdline_parser_file_save(const char *filename, - struct gengetopt_args_info *args_info); - -/** - * Print the help - */ -void cmdline_parser_print_help(void); -/** - * Print the version - */ -void cmdline_parser_print_version(void); - -/** - * Initializes all the fields a cmdline_parser_params structure - * to their default values - * @param params the structure to initialize - */ -void cmdline_parser_params_init(struct cmdline_parser_params *params); - -/** - * Allocates dynamically a cmdline_parser_params structure and initializes - * all its fields to their default values - * @return the created and initialized cmdline_parser_params structure - */ -struct cmdline_parser_params *cmdline_parser_params_create(void); - -/** - * Initializes the passed gengetopt_args_info structure's fields - * (also set default values for options that have a default) - * @param args_info the structure to initialize - */ -void cmdline_parser_init (struct gengetopt_args_info *args_info); -/** - * Deallocates the string fields of the gengetopt_args_info structure - * (but does not deallocate the structure itself) - * @param args_info the structure to deallocate - */ -void cmdline_parser_free (struct gengetopt_args_info *args_info); - -/** - * Checks that all the required options were specified - * @param args_info the structure to check - * @param prog_name the name of the program that will be used to print - * possible errors - * @return - */ -int cmdline_parser_required (struct gengetopt_args_info *args_info, - const char *prog_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* ESMTOOL_CMD_H */ From fa3fbf940c83fc789a98f916252ae6e024a9a989 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Fri, 2 Mar 2012 14:10:11 +0400 Subject: [PATCH 232/269] Fix for OS X 10.6 support --- libs/platform/string.h | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/libs/platform/string.h b/libs/platform/string.h index 89ac141a89..0b67e50ea2 100644 --- a/libs/platform/string.h +++ b/libs/platform/string.h @@ -2,13 +2,33 @@ #ifndef _STRING_WRAPPER_H #define _STRING_WRAPPER_H +#ifdef __APPLE__ +#include +#endif + #include -#if (defined(__APPLE__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) || defined(__MINGW32__) +#if (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) || defined(__MINGW32__) // need our own implementation of strnlen +#ifdef __MINGW32__ static size_t strnlen(const char *s, size_t n) { - const char *p = (const char *)memchr(s, 0, n); - return(p ? p-s : n); + const char *p = (const char *)memchr(s, 0, n); + return(p ? p-s : n); } +#elif (defined(__APPLE__) && __MAC_OS_X_VERSION_MIN_REQUIRED < 1070) +static size_t mw_strnlen(const char *s, size_t n) +{ + if (strnlen != NULL) { + return strnlen(s, n); + } + else { + const char *p = (const char *)memchr(s, 0, n); + return(p ? p-s : n); + } +} +#define strnlen mw_strnlen #endif + +#endif + #endif From 70da2a2a99f3b42024f6b35763f4d7b09eb9867b Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Fri, 2 Mar 2012 14:18:10 +0400 Subject: [PATCH 233/269] removed tabs --- libs/platform/string.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/platform/string.h b/libs/platform/string.h index 0b67e50ea2..5368d757cc 100644 --- a/libs/platform/string.h +++ b/libs/platform/string.h @@ -21,10 +21,10 @@ static size_t mw_strnlen(const char *s, size_t n) if (strnlen != NULL) { return strnlen(s, n); } - else { - const char *p = (const char *)memchr(s, 0, n); - return(p ? p-s : n); - } + else { + const char *p = (const char *)memchr(s, 0, n); + return(p ? p-s : n); + } } #define strnlen mw_strnlen #endif From 1e998545c7f80639602f825b357192de04708e4f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 2 Mar 2012 15:15:44 +0100 Subject: [PATCH 234/269] fixed log path --- apps/openmw/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index c2700752f5..4765ceadc5 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -316,7 +316,7 @@ void OMW::Engine::go() } mOgre->configure(!boost::filesystem::is_regular_file(mCfgMgr.getOgreConfigPath()), mCfgMgr.getOgreConfigPath().string(), - mCfgMgr.getLogPath().string() + std::string("/"), + mCfgMgr.getLogPath().string(), mCfgMgr.getPluginsConfigPath().string(), false); // This has to be added BEFORE MyGUI is initialized, as it needs From 5aabf22c16dab6a2230845f90fe5fdf67eeeca32 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Mar 2012 16:47:13 +0100 Subject: [PATCH 235/269] collision shape scale fix --- bullet/physic.cpp | 3 ++- bullet/physic.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bullet/physic.cpp b/bullet/physic.cpp index 294b379786..07bad30535 100644 --- a/bullet/physic.cpp +++ b/bullet/physic.cpp @@ -210,12 +210,13 @@ namespace Physic delete mShapeLoader; } - RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name) + RigidBody* PhysicEngine::createRigidBody(std::string mesh,std::string name,float scale) { //get the shape from the .nif mShapeLoader->load(mesh,"General"); BulletShapeManager::getSingletonPtr()->load(mesh,"General"); BulletShapePtr shape = BulletShapeManager::getSingleton().getByName(mesh,"General"); + shape->Shape->setLocalScaling(btVector3(scale,scale,scale)); //create the motionState CMotionState* newMotionState = new CMotionState(this,name); diff --git a/bullet/physic.hpp b/bullet/physic.hpp index 7507619653..88e3699aee 100644 --- a/bullet/physic.hpp +++ b/bullet/physic.hpp @@ -135,7 +135,7 @@ namespace Physic * Create a RigidBody.It does not add it to the simulation, but it does add it to the rigidBody Map, * so you can get it with the getRigidBody function. */ - RigidBody* createRigidBody(std::string mesh,std::string name); + RigidBody* createRigidBody(std::string mesh,std::string name,float scale); /** * Add a RigidBody to the simulation From ba0365a42780ac986866701b080348c65da45f32 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Mar 2012 16:47:39 +0100 Subject: [PATCH 236/269] collision shape scale fix --- apps/openmw/mwworld/physicssystem.cpp | 2 +- libs/openengine | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index b0da4524ed..bb2f9f8a92 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -120,7 +120,7 @@ namespace MWWorld void PhysicsSystem::addObject (const std::string& handle, const std::string& mesh, const Ogre::Quaternion& rotation, float scale, const Ogre::Vector3& position) { - OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle); + OEngine::Physic::RigidBody* body = mEngine->createRigidBody(mesh,handle,scale); mEngine->addRigidBody(body); btTransform tr; tr.setOrigin(btVector3(position.x,position.y,position.z)); diff --git a/libs/openengine b/libs/openengine index 21b8456453..5aabf22c16 160000 --- a/libs/openengine +++ b/libs/openengine @@ -1 +1 @@ -Subproject commit 21b8456453242e132c85f92047cf9bce535c1b22 +Subproject commit 5aabf22c16dab6a2230845f90fe5fdf67eeeca32 From d74b78a302926362bdf0382bc04d1974cde95bef Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 3 Mar 2012 12:26:08 +0100 Subject: [PATCH 237/269] very minor performance improvement --- apps/openmw/mwrender/sky.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index b8bd588c4b..1b324459a5 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -658,9 +658,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather) if (weather.mNight && mStarsOpacity != weather.mNightFade) { - for (int i=0; i<7; ++i) - mStarsMaterials[i]->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, weather.mNightFade); - mStarsOpacity = weather.mNightFade; + if (weather.mNightFade == 0) + mAtmosphereNight->setVisible(false); + else + { + mAtmosphereNight->setVisible(true); + for (int i=0; i<7; ++i) + mStarsMaterials[i]->getTechnique(0)->getPass(0)->setDiffuse(0.0, 0.0, 0.0, weather.mNightFade); + mStarsOpacity = weather.mNightFade; + } } float strength; From a81ecb5f659e1d28b37e1d60d09270ef2597fb17 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Sat, 3 Mar 2012 18:26:11 -0500 Subject: [PATCH 238/269] Bug 210 fix --- apps/openmw/mwrender/npcanimation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 0ceb0a4c35..c6fe023d69 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -42,6 +42,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env,O std::string bodyRaceID = headID.substr(0, headID.find_last_of("head_") - 4); char secondtolast = bodyRaceID.at(bodyRaceID.length() - 2); bool female = tolower(secondtolast) == 'f'; + std::transform(bodyRaceID.begin(), bodyRaceID.end(), bodyRaceID.begin(), ::tolower); bool beast = bodyRaceID == "b_n_khajiit_m_" || bodyRaceID == "b_n_khajiit_f_" || bodyRaceID == "b_n_argonian_m_" || bodyRaceID == "b_n_argonian_f_"; /*std::cout << "Race: " << ref->base->race ; From 04a4e20bb30a0c8be28052462f622e626b44b1b2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 4 Mar 2012 12:47:21 +0100 Subject: [PATCH 239/269] fix ogre.log --- ogre/renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogre/renderer.cpp b/ogre/renderer.cpp index 399d50d426..ecded08d8f 100644 --- a/ogre/renderer.cpp +++ b/ogre/renderer.cpp @@ -49,7 +49,7 @@ bool OgreRenderer::configure(bool showConfig, { // Set up logging first new LogManager; - Log *log = LogManager::getSingleton().createLog(logPath); + Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); logging = _logging; if(logging) From 42a7375a0b83472a66d0deffbb946afb8035246a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 4 Mar 2012 23:26:35 +0100 Subject: [PATCH 240/269] adjusted the batch region size, this was the cause for lights going on/off bug --- apps/openmw/mwrender/objects.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 4e2a3caab2..717064ada5 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -101,6 +101,14 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); //Create the scenenode and put it in the map mStaticGeometry[ptr.getCell()] = sg; + + // This specifies the size of a single batch region. + // If it is set too high: + // - there will be problems choosing the correct lights + // - the culling will be more inefficient + // If it is set too low: + // - there will be too many batches. + sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500)); } else { @@ -108,7 +116,6 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh) } sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); - sg->setRegionDimensions(Ogre::Vector3(100000,10000,100000)); mRenderer.getScene()->destroyEntity(ent); } From 4c240c644fc2baad0b6def265fc4b715d094e39d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 5 Mar 2012 10:58:33 +0100 Subject: [PATCH 241/269] OpenEngine update (log fix) --- libs/openengine | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/openengine b/libs/openengine index 5aabf22c16..8f98718315 160000 --- a/libs/openengine +++ b/libs/openengine @@ -1 +1 @@ -Subproject commit 5aabf22c16dab6a2230845f90fe5fdf67eeeca32 +Subproject commit 8f98718315fe11af359740c4a025fd1ca52a9157 From 1776ede9e1155034429e0e65185523e02f410e87 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Mon, 5 Mar 2012 16:06:46 +0200 Subject: [PATCH 242/269] Broken sound path fix --- apps/openmw/mwsound/soundmanager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 76ef23bc2a..6795eff195 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -410,7 +410,12 @@ namespace MWSound if(useSound) { - mData = new SoundImpl(root, camera, store, dataDirs /* Sound */, dataDirs /* Music */, fsstrict); + Files::PathContainer soundDirs;; + for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + { + soundDirs.push_back( *it / std::string("Sound")); + } + mData = new SoundImpl(root, camera, store, soundDirs /* Sound */, dataDirs /* Music */, fsstrict); } test.name = ""; From f7b706d24eaf5de389b42dc7696a7b5aa38caa89 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Mar 2012 18:50:56 +0100 Subject: [PATCH 243/269] use the vertex colours that morrowind supplies for a lot of meshes --- components/nifogre/ogre_nif_loader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 8b55400193..1c23b55c06 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -243,6 +243,8 @@ void NIFLoader::createMaterial(const String &name, /*TextureUnitState *txt =*/ pass->createTextureUnitState(texName); + pass->setVertexColourTracking(TVC_DIFFUSE); + // As of yet UNTESTED code from Chris: /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL); From 3ea2a9f05d4206c5f9ef0d5d7a968eea7854c659 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 5 Mar 2012 19:13:11 +0100 Subject: [PATCH 244/269] changeWeather bugfix --- apps/openmw/mwworld/weather.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 7cb9f3dfc6..059b0ec1e2 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -758,7 +758,7 @@ unsigned int WeatherManager::getWeatherID() const return 3; else if (mCurrentWeather == "rain") return 4; - else if (mCurrentWeather == "thunder") + else if (mCurrentWeather == "thunderstorm") return 5; else if (mCurrentWeather == "ashstorm") return 6; @@ -787,7 +787,7 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int else if (id==4) weather = "rain"; else if (id==5) - weather = "thunder"; + weather = "thunderstorm"; else if (id==6) weather = "ashstorm"; else if (id==7) From 9848b6717432883683a34d0f6f2efaaf5bb2711b Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Mon, 5 Mar 2012 17:46:29 -0500 Subject: [PATCH 245/269] Fixing errors --- apps/openmw/mwrender/animation.cpp | 10 +++++----- apps/openmw/mwrender/animation.hpp | 7 +++---- components/nifogre/ogre_nif_loader.cpp | 2 ++ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 999d854141..4972654fcd 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -331,7 +331,7 @@ namespace MWRender{ } } - bool Animation::timeIndex( float time, std::vector& times, int & i, int & j, float & x ){ + bool Animation::timeIndex( float time, std::vector & times, int & i, int & j, float & x ){ int count; if ( (count = times.size()) > 0 ) { @@ -427,19 +427,19 @@ namespace MWRender{ float x; float x2; - std::vector& quats = iter->getQuat(); + std::vector & quats = iter->getQuat(); - std::vector& ttime = iter->gettTime(); + std::vector & ttime = iter->gettTime(); - std::vector& rtime = iter->getrTime(); + std::vector & rtime = iter->getrTime(); int rindexJ = rindexI[slot]; timeIndex(time, rtime, rindexI[slot], rindexJ, x2); int tindexJ = tindexI[slot]; - std::vector& translist1 = iter->getTranslist1(); + std::vector & translist1 = iter->getTranslist1(); timeIndex(time, ttime, tindexI[slot], tindexJ, x); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 63ca3da2d9..8e3f54315f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -28,8 +28,7 @@ class Animation{ MWWorld::Environment& mEnvironment; std::map vecRotPos; static std::map mUniqueIDs; - float oldHund; - bool samePlace; + std::vector* > shapeparts; //All the NiTriShape data that we need for animating an npc @@ -57,11 +56,11 @@ class Animation{ Ogre::Entity* base; void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); void handleAnimationTransforms(); - bool timeIndex( float time, std::vector& times, int & i, int & j, float & x ); + bool timeIndex( float time, std::vector & times, int & i, int & j, float & x ); std::string getUniqueID(std::string mesh); public: - Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend): mRend(_rend), mEnvironment(_env), animate(0), oldHund(0){}; + Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend): mRend(_rend), mEnvironment(_env), animate(0){}; virtual void runAnimation(float timepassed) = 0; void startScript(std::string groupname, int mode, int loops); void stopScript(); diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 62dbc29dff..cb626acbeb 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1328,6 +1328,8 @@ void NIFLoader::loadResource(Resource *resource) (*iter)->addBoneAssignment(vba); } + //Don't link on npc parts to eliminate redundant skeletons + //Will have to be changed later slightly for robes/skirts if(triname == "") mesh->_notifySkeleton(mSkel); } From 03ea3bb62f9f1eb2905d47360e2f31090da59aa5 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Tue, 6 Mar 2012 01:21:00 +0200 Subject: [PATCH 246/269] SoundManager: first attempt at refactoring. BROKEN --- apps/openmw/engine.cpp | 1 - apps/openmw/mwsound/soundmanager.cpp | 225 +++++++++------------------ apps/openmw/mwsound/soundmanager.hpp | 72 +++++++-- 3 files changed, 139 insertions(+), 159 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4765ceadc5..89cda8f7c9 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -341,7 +341,6 @@ void OMW::Engine::go() // Create sound system mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(), mOgre->getCamera(), - mEnvironment.mWorld->getStore(), mDataDirs, mUseSound, mFSStrict, mEnvironment); diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 6795eff195..c269c02368 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -45,7 +45,7 @@ using namespace Mangle::Sound; typedef OEngine::Sound::SoundManager OEManager; -typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; +////typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; // Set the position on a sound based on a Ptr. static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) @@ -60,73 +60,53 @@ static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) namespace MWSound { - struct SoundManager::SoundImpl - { - /* This is the sound manager. It loades, stores and deletes - sounds based on the sound factory it is given. - */ - OEManagerPtr mgr; - SoundPtr music; - /* This class calls update() on the sound manager each frame - using and Ogre::FrameListener - */ - Mangle::Sound::OgreOutputUpdater updater; - - /* This class tracks the movement of an Ogre::Camera and moves - a sound listener automatically to follow it. - */ - Mangle::Sound::OgreListenerMover cameraTracker; - - const ESMS::ESMStore &store; - - typedef std::map IDMap; - typedef std::map PtrMap; - PtrMap sounds; - - // This is used for case insensitive and slash-type agnostic file - // finding. It takes DOS paths (any case, \\ slashes or / slashes) - // relative to the sound dir, and translates them into full paths - // of existing files in the filesystem, if they exist. - bool FSstrict; - FileFinder::LessTreeFileFinder files; - FileFinder::StrictTreeFileFinder strict; - FileFinder::LessTreeFileFinder musicpath; - FileFinder::StrictTreeFileFinder musicpathStrict; - - SoundImpl(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &str, - const Files::PathContainer& soundDir, - const Files::PathContainer& musicDir, - bool fsstrict) - : mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) - , updater(mgr) - , cameraTracker(mgr) - , store(str) - , FSstrict(fsstrict) - , files(soundDir) - , strict(soundDir) - , musicpath(musicDir) - , musicpathStrict(musicDir) + SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, + const Files::PathContainer& dataDirs, + bool useSound, bool fsstrict, MWWorld::Environment& environment) + : fsStrict(fsstrict) + , mEnvironment(environment) + , mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) + , updater(mgr) + , cameraTracker(mgr) { + for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + { + MP3Lookup((*it) / "Music/Explore/"); + } - std::cout << "Sound output: " << SOUND_OUT << std::endl; - std::cout << "Sound decoder: " << SOUND_IN << std::endl; - // Attach the camera to the camera tracker - cameraTracker.followCamera(camera); + if(useSound) + { + Files::PathContainer soundDirs;; + for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + { + soundDirs.push_back( *it / std::string("Sound")); + } + soundfiles = soundDirs; + strict = soundDirs; + musicpath = dataDirs; + musicpathStrict = dataDirs; + std::cout << "Sound output: " << SOUND_OUT << std::endl; + std::cout << "Sound decoder: " << SOUND_IN << std::endl; + // Attach the camera to the camera tracker + cameraTracker.followCamera(camera); - // Tell Ogre to update the sound system each frame - root->addFrameListener(&updater); - } + // Tell Ogre to update the sound system each frame + root->addFrameListener(&updater); + } - ~SoundImpl() + test.name = ""; + total = 0; + } + + SoundManager::~SoundManager() { Ogre::Root::getSingleton().removeFrameListener(&updater); cameraTracker.unfollowCamera(); } - - static std::string toMp3(std::string str) + static std::string SoundManager::toMp3(std::string str) { std::string::size_type i = str.rfind('.'); if(str.find('/', i) == std::string::npos && @@ -137,10 +117,10 @@ namespace MWSound return str; } - bool hasFile(const std::string &str, bool music = false) + bool SoundManager::hasFile(const std::string &str, bool music) { bool found = false; - if(!FSstrict) + if(!fsStrict) { if(music) { @@ -153,10 +133,10 @@ namespace MWSound } else { - found = files.has(str); + found = soundfiles.has(str); if (!found) { - found = files.has(toMp3(str)); + found = soundfiles.has(toMp3(str)); } } } @@ -186,22 +166,22 @@ namespace MWSound // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) - std::string convertPath(const std::string &str, bool music = false) + std::string SoundManager::convertPath(const std::string &str, bool music) { - if(FSstrict == false) + if(fsStrict == false) { // Search and return if(music && musicpath.has(str)) return musicpath.lookup(str); - else if(files.has(str)) - return files.lookup(str); + else if(soundfiles.has(str)) + return soundfiles.lookup(str); // Try mp3 if the wav wasn't found std::string mp3 = toMp3(str); if(music && musicpath.has(mp3)) return musicpath.lookup(mp3); - else if(files.has(mp3)) - return files.lookup(mp3); + else if(soundfiles.has(mp3)) + return soundfiles.lookup(mp3); } else { @@ -225,10 +205,10 @@ namespace MWSound // Convert a soundId to file name, and modify the volume // according to the sounds local volume setting, minRange and // maxRange. - std::string lookup(const std::string &soundId, + std::string SoundManager::lookup(const std::string &soundId, float &volume, float &min, float &max) { - const ESM::Sound *snd = store.sounds.search(soundId); + const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId); if(snd == NULL) return ""; if(snd->data.volume == 0) @@ -253,7 +233,7 @@ namespace MWSound } // Add a sound to the list and play it - void add(const std::string &file, + void SoundManager::add(const std::string &file, MWWorld::Ptr ptr, const std::string &id, float volume, float pitch, @@ -280,7 +260,7 @@ namespace MWSound // Clears all the sub-elements of a given iterator, and then // removes it from 'sounds'. - void clearAll(PtrMap::iterator& it) + void SoundManager::clearAll(PtrMap::iterator& it) { IDMap::iterator sit = it->second.begin(); @@ -301,7 +281,7 @@ namespace MWSound // Stop a sound and remove it from the list. If id="" then // remove the entire object and stop all its sounds. - void remove(MWWorld::Ptr ptr, const std::string &id = "") + void SoundManager::remove(MWWorld::Ptr ptr, const std::string &id) { PtrMap::iterator it = sounds.find(ptr); if(it != sounds.end()) @@ -324,7 +304,7 @@ namespace MWSound } } - bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const + bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const { PtrMap::const_iterator it = sounds.find(ptr); if(it != sounds.end()) @@ -348,7 +328,7 @@ namespace MWSound } // Remove all references to objects belonging to a given cell - void removeCell(const MWWorld::Ptr::CellStore *cell) + void SoundManager::removeCell(const MWWorld::Ptr::CellStore *cell) { PtrMap::iterator it2, it = sounds.begin(); while(it != sounds.end()) @@ -360,7 +340,7 @@ namespace MWSound } } - void updatePositions(MWWorld::Ptr ptr) + void SoundManager::updatePositions(MWWorld::Ptr ptr) { // Find the reference (if any) PtrMap::iterator it = sounds.find(ptr); @@ -378,61 +358,27 @@ namespace MWSound } } } - }; /* SoundImpl */ + void SoundManager::streamMusicFull(const std::string& filename) { - if(!mData) return; - // Play the sound and tell it to stream, if possible. TODO: // Store the reference, the jukebox will need to check status, // control volume etc. - if (mData->music) - mData->music->stop(); - mData->music = mData->mgr->load(filename); - mData->music->setStreaming(true); - mData->music->setVolume(0.4); - mData->music->play(); + if (music) + music->stop(); + music = mgr->load(filename); + music->setStreaming(true); + music->setVolume(0.4); + music->play(); } - SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, - const ESMS::ESMStore &store, const Files::PathContainer& dataDirs, - bool useSound, bool fsstrict, MWWorld::Environment& environment) - : mData(NULL) - , fsStrict(fsstrict) - , mEnvironment(environment) - { - for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) - { - MP3Lookup((*it) / "Music/Explore/"); - } - - if(useSound) - { - Files::PathContainer soundDirs;; - for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) - { - soundDirs.push_back( *it / std::string("Sound")); - } - mData = new SoundImpl(root, camera, store, soundDirs /* Sound */, dataDirs /* Music */, fsstrict); - } - - test.name = ""; - total = 0; - } - - SoundManager::~SoundManager() - { - if(mData) - delete mData; - } - void SoundManager::streamMusic(const std::string& filename) { - if(mData->hasFile(filename, true)) + if(hasFile(filename, true)) { - streamMusicFull(mData->convertPath(filename, true)); + streamMusicFull(convertPath(filename, true)); } } @@ -476,45 +422,36 @@ namespace MWSound bool SoundManager::isMusicPlaying() { bool test = false; - if(mData && mData->music) + if(music) { - test = mData->music->isPlaying(); + test = music->isPlaying(); } return test; } - SoundManager::SoundImpl SoundManager::getMData() - { - // bool test = mData->music->isPlaying(); - return *mData; - } - void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename) { // The range values are not tested - if(!mData) return; - if(mData->hasFile(filename)) - mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 20000, false); + if(hasFile(filename)) + add(convertPath(filename), ptr, "_say_sound", 1, 1, 100, 20000, false); else std::cout << "Sound file " << filename << " not found, skipping.\n"; } bool SoundManager::sayDone (MWWorld::Ptr ptr) const { - if(!mData) return false; - return !mData->isPlaying(ptr, "_say_sound"); + return !isPlaying(ptr, "_say_sound"); } void SoundManager::playSound(const std::string& soundId, float volume, float pitch) { - if(!mData) return; // Play and forget float min, max; - const std::string &file = mData->lookup(soundId, volume, min, max); + const std::string &file = ookup(soundId, volume, min, max); if (file != "") { - SoundPtr snd = mData->mgr->load(file); + SoundPtr snd = mgr->load(file); snd->setVolume(volume); snd->setRange(min,max); snd->setPitch(pitch); @@ -525,42 +462,34 @@ namespace MWSound void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId, float volume, float pitch, bool loop) { - if(!mData) return; - // Look up the sound in the ESM data float min, max; - const std::string &file = mData->lookup(soundId, volume, min, max); + const std::string &file = lookup(soundId, volume, min, max); if (file != "") - mData->add(file, ptr, soundId, volume, pitch, min, max, loop); + add(file, ptr, soundId, volume, pitch, min, max, loop); } void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId) { - if(!mData) return; - mData->remove(ptr, soundId); + remove(ptr, soundId); } void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell) { - if(!mData) return; - mData->removeCell(cell); + removeCell(cell); } bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const { // Mark all sounds as playing, otherwise the scripts will just // keep trying to play them every frame. - if(!mData) return true; - return mData->isPlaying(ptr, soundId); + return isPlaying(ptr, soundId); } void SoundManager::updateObject(MWWorld::Ptr ptr) { - if (mData != NULL) - { - mData->updatePositions(ptr); - } + updatePositions(ptr); } void SoundManager::update (float duration) diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 5c3f473f2e..cde8475e94 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -6,10 +6,14 @@ #include #include -#include "../mwworld/ptr.hpp" +#include +#include + #include #include +#include +#include "../mwworld/ptr.hpp" namespace Ogre @@ -18,6 +22,17 @@ namespace Ogre class Camera; } +namespace Mangle +{ + namespace Sound + { + typedef boost::shared_ptr SoundPtr; + //struct OgreOutputUpdater; + } +} + +typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; + namespace ESMS { struct ESMStore; @@ -30,14 +45,8 @@ namespace MWWorld namespace MWSound { - //SoundPtr *music; class SoundManager { - // Hide implementation details - engine.cpp is compiling - // enough as it is. - struct SoundImpl; - - SoundImpl *mData; Files::PathContainer files; bool fsStrict; MWWorld::Environment& mEnvironment; @@ -50,9 +59,54 @@ namespace MWSound ///< Play a soundifle /// \param absolute filename + /* This is the sound manager. It loades, stores and deletes + sounds based on the sound factory it is given. + */ + OEManagerPtr mgr; + Mangle::Sound::SoundPtr music; + + /* This class calls update() on the sound manager each frame + using and Ogre::FrameListener + */ + Mangle::Sound::OgreOutputUpdater updater; + + /* This class tracks the movement of an Ogre::Camera and moves + a sound listener automatically to follow it. + */ + Mangle::Sound::OgreListenerMover cameraTracker; + + typedef std::map IDMap; + typedef std::map PtrMap; + PtrMap sounds; + + // This is used for case insensitive and slash-type agnostic file + // finding. It takes DOS paths (any case, \\ slashes or / slashes) + // relative to the sound dir, and translates them into full paths + // of existing files in the filesystem, if they exist. + bool FSstrict; + FileFinder::LessTreeFileFinder soundfiles; + FileFinder::StrictTreeFileFinder strict; + FileFinder::LessTreeFileFinder musicpath; + FileFinder::StrictTreeFileFinder musicpathStrict; + + static std::string toMp3(std::string str); + bool hasFile(const std::string &str, bool music = false); + std::string convertPath(const std::string &str, bool music = false); + std::string lookup(const std::string &soundId, + float &volume, float &min, float &max); + void add(const std::string &file, + MWWorld::Ptr ptr, const std::string &id, + float volume, float pitch, float min, float max, + bool loop); + void clearAll(PtrMap::iterator& it); + void remove(MWWorld::Ptr ptr, const std::string &id = ""); + bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const; + void removeCell(const MWWorld::Ptr::CellStore *cell); + void updatePositions(MWWorld::Ptr ptr); + public: - SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, + SoundManager(Ogre::Root*, Ogre::Camera*, const Files::PathContainer& dataDir, bool useSound, bool fsstrict, MWWorld::Environment& environment); ~SoundManager(); @@ -66,8 +120,6 @@ namespace MWSound bool isMusicPlaying(); - SoundImpl getMData(); - void say (MWWorld::Ptr reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/Vo/" in the data directory. From 39ff8d6a01bd0486402aa137bb668dc649860612 Mon Sep 17 00:00:00 2001 From: Jason Hooks Date: Tue, 6 Mar 2012 18:28:41 -0500 Subject: [PATCH 247/269] Compile error retry --- apps/openmw/mwrender/animation.cpp | 10 +++++----- apps/openmw/mwrender/animation.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 4972654fcd..9df987dc18 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -331,7 +331,7 @@ namespace MWRender{ } } - bool Animation::timeIndex( float time, std::vector & times, int & i, int & j, float & x ){ + bool Animation::timeIndex( float time, const std::vector & times, int & i, int & j, float & x ){ int count; if ( (count = times.size()) > 0 ) { @@ -427,19 +427,19 @@ namespace MWRender{ float x; float x2; - std::vector & quats = iter->getQuat(); + const std::vector & quats = iter->getQuat(); - std::vector & ttime = iter->gettTime(); + const std::vector & ttime = iter->gettTime(); - std::vector & rtime = iter->getrTime(); + const std::vector & rtime = iter->getrTime(); int rindexJ = rindexI[slot]; timeIndex(time, rtime, rindexI[slot], rindexJ, x2); int tindexJ = tindexI[slot]; - std::vector & translist1 = iter->getTranslist1(); + const std::vector & translist1 = iter->getTranslist1(); timeIndex(time, ttime, tindexI[slot], tindexJ, x); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 8e3f54315f..e08b86e8de 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -56,7 +56,7 @@ class Animation{ Ogre::Entity* base; void handleShapes(std::vector* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel); void handleAnimationTransforms(); - bool timeIndex( float time, std::vector & times, int & i, int & j, float & x ); + bool timeIndex( float time, const std::vector & times, int & i, int & j, float & x ); std::string getUniqueID(std::string mesh); public: From 54353794e580248b1a7638e38fe31371a20dd59b Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Wed, 7 Mar 2012 02:20:15 +0200 Subject: [PATCH 248/269] SoundManager: Completely rewrote the file management --- apps/openmw/mwsound/soundmanager.cpp | 161 +++++---------------------- apps/openmw/mwsound/soundmanager.hpp | 27 ++--- components/files/fileops.cpp | 87 +++++++++++++++ components/files/fileops.hpp | 24 ++++ 4 files changed, 152 insertions(+), 147 deletions(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index c269c02368..53da9040bc 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -64,28 +64,36 @@ namespace MWSound SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, const Files::PathContainer& dataDirs, bool useSound, bool fsstrict, MWWorld::Environment& environment) - : fsStrict(fsstrict) + : mFSStrict(fsstrict) , mEnvironment(environment) , mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) , updater(mgr) , cameraTracker(mgr) { - for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) - { - MP3Lookup((*it) / "Music/Explore/"); - } - if(useSound) { - Files::PathContainer soundDirs;; + // Make a list of all the sounds + Files::PathContainer soundDirs; for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { soundDirs.push_back( *it / std::string("Sound")); } - soundfiles = soundDirs; - strict = soundDirs; - musicpath = dataDirs; - musicpathStrict = dataDirs; + for (Files::PathContainer::const_iterator it = soundDirs.begin(); it != soundDirs.end(); ++it) + { + Files::FileLister(*it, mSoundFiles, true); + } + + // Make a list of all the music tracks + Files::PathContainer musicDirs; + for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + { + musicDirs.push_back( *it / std::string("Music") / std::string("Explore")); + } + for (Files::PathContainer::const_iterator it = musicDirs.begin(); it != musicDirs.end(); ++it) + { + Files::FileLister(*it, mMusicFiles, true); + } + std::cout << "Sound output: " << SOUND_OUT << std::endl; std::cout << "Sound decoder: " << SOUND_IN << std::endl; // Attach the camera to the camera tracker @@ -105,103 +113,6 @@ namespace MWSound cameraTracker.unfollowCamera(); } - - static std::string SoundManager::toMp3(std::string str) - { - std::string::size_type i = str.rfind('.'); - if(str.find('/', i) == std::string::npos && - str.find('\\', i) == std::string::npos) - str = str.substr(0, i) + ".mp3"; - else - str += ".mp3"; - return str; - } - - bool SoundManager::hasFile(const std::string &str, bool music) - { - bool found = false; - if(!fsStrict) - { - if(music) - { - found = musicpath.has(str); - // Not found? Try with .mp3 - if (!found) - { - found = musicpath.has(toMp3(str)); - } - } - else - { - found = soundfiles.has(str); - if (!found) - { - found = soundfiles.has(toMp3(str)); - } - } - } - else - { - if(music) - { - found = musicpathStrict.has(str); - // Not found? Try with .mp3 - if (!found) - { - found = musicpathStrict.has(toMp3(str)); - } - } - else - { - found = strict.has(str); - if (!found) - { - found = strict.has(toMp3(str)); - } - } - } - - return found; - } - - // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path - // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) - std::string SoundManager::convertPath(const std::string &str, bool music) - { - if(fsStrict == false) - { - // Search and return - if(music && musicpath.has(str)) - return musicpath.lookup(str); - else if(soundfiles.has(str)) - return soundfiles.lookup(str); - - // Try mp3 if the wav wasn't found - std::string mp3 = toMp3(str); - if(music && musicpath.has(mp3)) - return musicpath.lookup(mp3); - else if(soundfiles.has(mp3)) - return soundfiles.lookup(mp3); - } - else - { - if(music && musicpathStrict.has(str)) - return musicpathStrict.lookup(str); - else if(strict.has(str)) - return strict.lookup(str); - - // Try mp3 if the wav wasn't found - std::string mp3 = toMp3(str); - if(music && musicpathStrict.has(mp3)) - return musicpathStrict.lookup(mp3); - else if(strict.has(str)) - return strict.lookup(mp3); - } - - // Give up - return ""; - } - // Convert a soundId to file name, and modify the volume // according to the sounds local volume setting, minRange and // maxRange. @@ -229,7 +140,7 @@ namespace MWSound max = std::max(min, max); } - return convertPath(snd->sound); + return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict); } // Add a sound to the list and play it @@ -376,33 +287,20 @@ namespace MWSound void SoundManager::streamMusic(const std::string& filename) { - if(hasFile(filename, true)) + std::string filePath = Files::FileListLocator(mMusicFiles, filename, mFSStrict); + if(!filePath.empty()) { - streamMusicFull(convertPath(filename, true)); + streamMusicFull(filePath); } } - void SoundManager::MP3Lookup(const boost::filesystem::path& dir) - { - boost::filesystem::directory_iterator dir_iter(dir), dir_end; - - std::string mp3extension = ".mp3"; - for(;dir_iter != dir_end; dir_iter++) - { - if(boost::filesystem::extension(*dir_iter) == mp3extension) - { - files.push_back(*dir_iter); - } - } - } - void SoundManager::startRandomTitle() { - if(!files.empty()) + if(!mMusicFiles.empty()) { - Files::PathContainer::iterator fileIter = files.begin(); + Files::PathContainer::iterator fileIter = mMusicFiles.begin(); srand( time(NULL) ); - int r = rand() % files.size() + 1; //old random code + int r = rand() % mMusicFiles.size() + 1; //old random code std::advance(fileIter, r - 1); std::string music = fileIter->string(); @@ -432,8 +330,9 @@ namespace MWSound void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename) { // The range values are not tested - if(hasFile(filename)) - add(convertPath(filename), ptr, "_say_sound", 1, 1, 100, 20000, false); + std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict); + if(!filePath.empty()) + add(filePath, ptr, "_say_sound", 1, 1, 100, 20000, false); else std::cout << "Sound file " << filename << " not found, skipping.\n"; } @@ -448,7 +347,7 @@ namespace MWSound { // Play and forget float min, max; - const std::string &file = ookup(soundId, volume, min, max); + const std::string &file = lookup(soundId, volume, min, max); if (file != "") { SoundPtr snd = mgr->load(file); diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index cde8475e94..8f63cdb58a 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include "../mwworld/ptr.hpp" @@ -47,8 +47,13 @@ namespace MWSound { class SoundManager { - Files::PathContainer files; - bool fsStrict; + + // This is used for case insensitive and slash-type agnostic file + // finding. It takes DOS paths (any case, \\ slashes or / slashes) + // relative to the sound dir, and translates them into full paths + // of existing files in the filesystem, if they exist. + bool mFSStrict; + MWWorld::Environment& mEnvironment; int total; @@ -79,19 +84,10 @@ namespace MWSound typedef std::map PtrMap; PtrMap sounds; - // This is used for case insensitive and slash-type agnostic file - // finding. It takes DOS paths (any case, \\ slashes or / slashes) - // relative to the sound dir, and translates them into full paths - // of existing files in the filesystem, if they exist. - bool FSstrict; - FileFinder::LessTreeFileFinder soundfiles; - FileFinder::StrictTreeFileFinder strict; - FileFinder::LessTreeFileFinder musicpath; - FileFinder::StrictTreeFileFinder musicpathStrict; + Files::PathContainer mSoundFiles; + + Files::PathContainer mMusicFiles; - static std::string toMp3(std::string str); - bool hasFile(const std::string &str, bool music = false); - std::string convertPath(const std::string &str, bool music = false); std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void add(const std::string &file, @@ -116,7 +112,6 @@ namespace MWSound /// \param filename name of a sound file in "Music/" in the data directory. void startRandomTitle(); - void MP3Lookup(const boost::filesystem::path& dir); bool isMusicPlaying(); diff --git a/components/files/fileops.cpp b/components/files/fileops.cpp index 329e4eb05f..d6365e7534 100644 --- a/components/files/fileops.cpp +++ b/components/files/fileops.cpp @@ -1,5 +1,6 @@ #include "fileops.hpp" #include +#include namespace Files { @@ -9,4 +10,90 @@ bool isFile(const char *name) return boost::filesystem::exists(boost::filesystem::path(name)); } + // Makes a list of files from a directory + void FileLister( boost::filesystem::path currentPath, Files::PathContainer& list, bool recursive) + { + if (!boost::filesystem::exists(currentPath)) + { + std::cout << "WARNING: " << currentPath.string() << " does not exist.\n"; + return ; + } + if (recursive) + { + for ( boost::filesystem::recursive_directory_iterator end, itr(currentPath.string()); + itr != end; ++itr ) + { + if ( boost::filesystem::is_regular_file(*itr)) + list.push_back(itr->path()); + } + } + else + { + for ( boost::filesystem::directory_iterator end, itr(currentPath.string()); + itr != end; ++itr ) + { + if ( boost::filesystem::is_regular_file(*itr)) + list.push_back(itr->path()); + } + } + } + + // Locates path in path container + boost::filesystem::path FileListLocator (const Files::PathContainer& list, const boost::filesystem::path& toFind, bool strict) + { + boost::filesystem::path result(""); + if (list.empty()) + return result; + + std::string toFindStr = toFind.string(); + + std::string fullPath; + + // The filesystems slash sets the default slash + std::string slash; + std::string wrongslash; + if(list[0].string().find("\\") != std::string::npos) + { + slash = "\\"; + wrongslash = "/"; + } + else + { + slash = "/"; + wrongslash = "\\"; + } + + // The file being looked for is converted to the new slash + if(toFindStr.find(wrongslash) != std::string::npos ) + { + boost::replace_all(toFindStr, wrongslash, slash); + } + + if (!strict) + { + boost::algorithm::to_lower(toFindStr); + } + + for (Files::PathContainer::const_iterator it = list.begin(); it != list.end(); ++it) + { + fullPath = it->string(); + if (!strict) + { + boost::algorithm::to_lower(fullPath); + } + if(fullPath.find(toFindStr) != std::string::npos) + { + result = *it; + break; + } + } + return result; + } + + // Overloaded form of the locator that takes a string and returns a string + std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict) + { + return FileListLocator(list, boost::filesystem::path(toFind), strict).string(); + } + } diff --git a/components/files/fileops.hpp b/components/files/fileops.hpp index a541beffc2..49ef7b9472 100644 --- a/components/files/fileops.hpp +++ b/components/files/fileops.hpp @@ -1,6 +1,12 @@ #ifndef COMPONENTS_FILES_FILEOPS_HPP #define COMPONENTS_FILES_FILEOPS_HPP +#include +#include +#include + +#include + namespace Files { @@ -8,6 +14,24 @@ namespace Files ///\param [in] name - filename bool isFile(const char *name); + /// A vector of Boost Paths, very handy + typedef std::vector PathContainer; + + /// Makes a list of files from a directory by taking a boost + /// path and a Path Container and adds to the Path container + /// all files in the path. It has a recursive option. + void FileLister( boost::filesystem::path currentPath, Files::PathContainer& list, bool recursive); + + /// Locates boost path in path container + /// returns the path from the container + /// that contains the searched path. + /// If it's not found it returns and empty path + /// Takes care of slashes, backslashes and it has a strict option. + boost::filesystem::path FileListLocator (const Files::PathContainer& list, const boost::filesystem::path& toFind, bool strict); + + /// Overloaded form of the locator that takes a string and returns a string + std::string FileListLocator (const Files::PathContainer& list,const std::string& toFind, bool strict); + } #endif /* COMPONENTS_FILES_FILEOPS_HPP */ From 054a176c8628f835a7d159e8a635f619deb8bf4f Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Wed, 7 Mar 2012 17:46:51 +0200 Subject: [PATCH 249/269] Added new FileLibrary class to handle music, this fixes a number of issues. --- apps/openmw/engine.cpp | 5 +- apps/openmw/mwsound/soundmanager.cpp | 70 +++++++++++++---- apps/openmw/mwsound/soundmanager.hpp | 16 +++- components/CMakeLists.txt | 1 + components/files/filelibrary.cpp | 113 +++++++++++++++++++++++++++ components/files/filelibrary.hpp | 48 ++++++++++++ 6 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 components/files/filelibrary.cpp create mode 100644 components/files/filelibrary.hpp diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 89cda8f7c9..0e21893070 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -118,8 +118,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) // sound if (mUseSound) { - if (!mEnvironment.mSoundManager->isMusicPlaying()) - mEnvironment.mSoundManager->startRandomTitle(); + mEnvironment.mSoundManager->playPlaylist(); mEnvironment.mSoundManager->update (evt.timeSinceLastFrame); } @@ -389,7 +388,7 @@ void OMW::Engine::go() mOgre->getRoot()->addFrameListener (this); // Play some good 'ol tunes - mEnvironment.mSoundManager->startRandomTitle(); + mEnvironment.mSoundManager->playPlaylist(std::string("Explore")); // scripts if (mCompileAll) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 53da9040bc..70118ea547 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -13,7 +13,6 @@ #include #include - #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" #include "../mwworld/player.hpp" @@ -45,7 +44,6 @@ using namespace Mangle::Sound; typedef OEngine::Sound::SoundManager OEManager; -////typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; // Set the position on a sound based on a Ptr. static void setPos(SoundPtr &snd, const MWWorld::Ptr ref) @@ -69,11 +67,21 @@ namespace MWSound , mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) , updater(mgr) , cameraTracker(mgr) + , mCurrentPlaylist(NULL) { if(useSound) { - // Make a list of all the sounds + // Temporary list of all sound directories Files::PathContainer soundDirs; + + // The music library will accept these filetypes + // If none is given then it will accept all filetypes + std::vector acceptableExtensions; + acceptableExtensions.push_back(".mp3"); + acceptableExtensions.push_back(".wav"); + acceptableExtensions.push_back(".ogg"); + acceptableExtensions.push_back(".flac"); + for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { soundDirs.push_back( *it / std::string("Sound")); @@ -83,17 +91,14 @@ namespace MWSound Files::FileLister(*it, mSoundFiles, true); } - // Make a list of all the music tracks - Files::PathContainer musicDirs; for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) { - musicDirs.push_back( *it / std::string("Music") / std::string("Explore")); - } - for (Files::PathContainer::const_iterator it = musicDirs.begin(); it != musicDirs.end(); ++it) - { - Files::FileLister(*it, mMusicFiles, true); + mMusicLibrary.add(*it / std::string("Music"), true, mFSStrict, acceptableExtensions); } + std::string anything = "anything"; // anything is better that a segfault + mCurrentPlaylist = mMusicLibrary.section(anything, mFSStrict); // now points to an empty path + std::cout << "Sound output: " << SOUND_OUT << std::endl; std::cout << "Sound decoder: " << SOUND_IN << std::endl; // Attach the camera to the camera tracker @@ -287,7 +292,8 @@ namespace MWSound void SoundManager::streamMusic(const std::string& filename) { - std::string filePath = Files::FileListLocator(mMusicFiles, filename, mFSStrict); + std::cout << filename << std::endl; + std::string filePath = mMusicLibrary.locate(filename, mFSStrict).string(); if(!filePath.empty()) { streamMusicFull(filePath); @@ -296,11 +302,11 @@ namespace MWSound void SoundManager::startRandomTitle() { - if(!mMusicFiles.empty()) + if(mCurrentPlaylist && !mCurrentPlaylist->empty()) { - Files::PathContainer::iterator fileIter = mMusicFiles.begin(); + Files::PathContainer::const_iterator fileIter = mCurrentPlaylist->begin(); srand( time(NULL) ); - int r = rand() % mMusicFiles.size() + 1; //old random code + int r = rand() % mCurrentPlaylist->size() + 1; //old random code std::advance(fileIter, r - 1); std::string music = fileIter->string(); @@ -327,6 +333,42 @@ namespace MWSound return test; } + bool SoundManager::setPlaylist(std::string playlist) + { + const Files::PathContainer* previousPlaylist; + previousPlaylist = mCurrentPlaylist; + if(mMusicLibrary.containsSection(playlist, mFSStrict)) + { + mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict); + } + else + { + std::cout << "Warning: playlist named " << playlist << " does not exist.\n"; + } + return previousPlaylist == mCurrentPlaylist; + } + + void SoundManager::playPlaylist(std::string playlist) + { + if (playlist == "") + { + if(!isMusicPlaying()) + { + startRandomTitle(); + } + return; + } + + if(!setPlaylist(playlist)) + { + startRandomTitle(); + } + else if (!isMusicPlaying()) + { + startRandomTitle(); + } + } + void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename) { // The range values are not tested diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 8f63cdb58a..64cac77135 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "../mwworld/ptr.hpp" @@ -86,7 +87,9 @@ namespace MWSound Files::PathContainer mSoundFiles; - Files::PathContainer mMusicFiles; + Files::FileLibrary mMusicLibrary; + + const Files::PathContainer* mCurrentPlaylist; std::string lookup(const std::string &soundId, float &volume, float &min, float &max); @@ -115,6 +118,17 @@ namespace MWSound bool isMusicPlaying(); + bool setPlaylist(std::string playlist=""); + ///< Set the playlist to an existing folder + /// \param name of the folder that contains the playlist + /// if none is set then it is set to an empty playlist + /// \return Return true if the previous playlist was the same + + void playPlaylist(std::string playlist=""); + ///< Start playing music from the selected folder + /// \param name of the folder that contains the playlist + /// if none is set then it plays from the current playlist + void say (MWWorld::Ptr reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/Vo/" in the data directory. diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6bf7bacf40..c95efb37df 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -44,6 +44,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager + filelibrary ) add_component_dir (compiler diff --git a/components/files/filelibrary.cpp b/components/files/filelibrary.cpp new file mode 100644 index 0000000000..b896379d50 --- /dev/null +++ b/components/files/filelibrary.cpp @@ -0,0 +1,113 @@ +#include "filelibrary.hpp" + +#include + +#include + +namespace Files +{ + // Looks for a string in a vector of strings + bool containsVectorString(const StringVector& list, const std::string& str) + { + for (StringVector::const_iterator iter = list.begin(); + iter != list.end(); iter++) + { + if (*iter == str) + return true; + } + return false; + } + + // Searches a path and adds the results to the library + void FileLibrary::add(const boost::filesystem::path &root, bool recursive, bool strict, + const StringVector &acceptableExtensions) + { + PathContainer list; + std::string fileExtension; + std::string type; + FileLister(root, list, recursive); + + for (PathContainer::iterator listIter = list.begin(); + listIter != list.end(); ++listIter) + { + if( !acceptableExtensions.empty() ) + { + fileExtension = listIter->extension().string(); + boost::algorithm::to_lower(fileExtension); + if(!containsVectorString(acceptableExtensions, fileExtension)) + continue; + } + + type = listIter->parent_path().leaf().string(); + if (!strict) + boost::algorithm::to_lower(type); + + mMap[type].push_back(*listIter); + //std::cout << "Added path: " << listIter->string() << " in section "<< type <second); + } + } + + // Searches the library for an item and returns a boost path to it + boost::filesystem::path FileLibrary::locate(std::string item, bool strict, std::string sectionName) + { + boost::filesystem::path result(""); + if (sectionName == "") + { + for(StringPathContMap::iterator iter = mMap.begin(); iter != mMap.end(); iter++) + { + result = FileListLocator(iter->second, boost::filesystem::path(item), strict); + if (result != boost::filesystem::path("")) + return result; + } + } + else + { + if (!containsSection(sectionName, strict)) + { + std::cout << "Warning: There is no section named " << sectionName << "\n"; + return result; + } + result = FileListLocator(mMap[sectionName], boost::filesystem::path(item), strict); + } + return result; + } + + // Prints all the available sections, used for debugging + void FileLibrary::printSections() + { + for(StringPathContMap::const_iterator mapIter = mMap.begin(); + mapIter != mMap.end(); mapIter++) + { + std::cout << mapIter->first < + +namespace Files +{ + typedef std::map StringPathContMap; + typedef std::vector StringVector; + + /// Looks for a string in a vector of strings + bool containsVectorString(const StringVector& list, const std::string& str); + + /// \brief Searches directories and makes lists of files according to folder name + class FileLibrary + { + private: + StringPathContMap mMap; + PathContainer mEmptyPath; + + public: + /// Searches a path and adds the results to the library + /// Recursive search and fs strict options are available + /// Takes a vector of acceptable files extensions, if none is given it lists everything. + void add(const boost::filesystem::path &root, bool recursive, bool strict, + const StringVector &acceptableExtensions); + + /// Returns true if the named section exists + /// You can run this check before running section() + bool containsSection(std::string sectionName, bool strict); + + /// Returns a pointer to const for a section of the library + /// which is essentially a PathContainer. + /// If the section does not exists it returns a pointer to an empty path. + const PathContainer* section(std::string sectionName, bool strict); + + /// Searches the library for an item and returns a boost path to it + /// Optionally you can provide a specific section + /// The result is the first that comes up according to alphabetical + /// section naming + boost::filesystem::path locate(std::string item, bool strict, std::string sectionName=""); + + /// Prints all the available sections, used for debugging + void printSections(); + }; +} + +#endif From ad6175c78a809f0acd5d547a1444bfebd99cb80b Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Thu, 8 Mar 2012 23:06:52 +0200 Subject: [PATCH 250/269] SoundManager: Set up the priority for file look up right and take care of a corner case --- apps/openmw/mwsound/soundmanager.cpp | 16 +++++---------- components/files/filelibrary.cpp | 29 +++++++++++++++++----------- components/files/filelibrary.hpp | 1 + 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 70118ea547..5f48081f22 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -71,9 +71,6 @@ namespace MWSound { if(useSound) { - // Temporary list of all sound directories - Files::PathContainer soundDirs; - // The music library will accept these filetypes // If none is given then it will accept all filetypes std::vector acceptableExtensions; @@ -82,16 +79,14 @@ namespace MWSound acceptableExtensions.push_back(".ogg"); acceptableExtensions.push_back(".flac"); - for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + // Makes a list of all sound files, searches in reverse for priority reasons + for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it) { - soundDirs.push_back( *it / std::string("Sound")); - } - for (Files::PathContainer::const_iterator it = soundDirs.begin(); it != soundDirs.end(); ++it) - { - Files::FileLister(*it, mSoundFiles, true); + Files::FileLister(*it / std::string("Sound"), mSoundFiles, true); } - for (Files::PathContainer::const_iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) + // Makes a FileLibrary of all music files, searches in reverse for priority reasons + for (Files::PathContainer::const_reverse_iterator it = dataDirs.rbegin(); it != dataDirs.rend(); ++it) { mMusicLibrary.add(*it / std::string("Music"), true, mFSStrict, acceptableExtensions); } @@ -292,7 +287,6 @@ namespace MWSound void SoundManager::streamMusic(const std::string& filename) { - std::cout << filename << std::endl; std::string filePath = mMusicLibrary.locate(filename, mFSStrict).string(); if(!filePath.empty()) { diff --git a/components/files/filelibrary.cpp b/components/files/filelibrary.cpp index b896379d50..baefd79ebc 100644 --- a/components/files/filelibrary.cpp +++ b/components/files/filelibrary.cpp @@ -2,6 +2,7 @@ #include +#include #include namespace Files @@ -22,13 +23,24 @@ namespace Files void FileLibrary::add(const boost::filesystem::path &root, bool recursive, bool strict, const StringVector &acceptableExtensions) { - PathContainer list; + if (!boost::filesystem::exists(root)) + { + std::cout << "Warning " << root.string() << " does not exist.\n"; + return; + } + std::string fileExtension; std::string type; - FileLister(root, list, recursive); - for (PathContainer::iterator listIter = list.begin(); - listIter != list.end(); ++listIter) + // remember the last location of the priority list when listing new items + int length = mPriorityList.size(); + + // First makes a list of all candidate files + FileLister(root, mPriorityList, recursive); + + // Then sort these files into sections according to the folder they belong to + for (PathContainer::iterator listIter = mPriorityList.begin() + length; + listIter != mPriorityList.end(); ++listIter) { if( !acceptableExtensions.empty() ) { @@ -43,7 +55,7 @@ namespace Files boost::algorithm::to_lower(type); mMap[type].push_back(*listIter); - //std::cout << "Added path: " << listIter->string() << " in section "<< type <string() << " in section "<< type <second, boost::filesystem::path(item), strict); - if (result != boost::filesystem::path("")) - return result; - } + return FileListLocator(mPriorityList, boost::filesystem::path(item), strict); } else { diff --git a/components/files/filelibrary.hpp b/components/files/filelibrary.hpp index 9013dbb37a..55978084f9 100644 --- a/components/files/filelibrary.hpp +++ b/components/files/filelibrary.hpp @@ -17,6 +17,7 @@ namespace Files private: StringPathContMap mMap; PathContainer mEmptyPath; + PathContainer mPriorityList; public: /// Searches a path and adds the results to the library From 44620ada861e9060f5eb6e0dd1817bd7bd0614d9 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Fri, 9 Mar 2012 03:22:16 +0200 Subject: [PATCH 251/269] SoundManager: Finish up with Task #172, plus cleanup --- apps/openmw/mwsound/soundmanager.cpp | 27 +++++++++++++++------------ apps/openmw/mwsound/soundmanager.hpp | 21 ++++++--------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 5f48081f22..105dc5a3d0 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include "../mwworld/environment.hpp" @@ -102,9 +101,6 @@ namespace MWSound // Tell Ogre to update the sound system each frame root->addFrameListener(&updater); } - - test.name = ""; - total = 0; } SoundManager::~SoundManager() @@ -430,15 +426,22 @@ namespace MWSound void SoundManager::update (float duration) { MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell(); + static int total = 0; + static std::string regionName = ""; + static float timePassed = 0.0; + timePassed += duration; //If the region has changed - if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10) + if(!(current->cell->data.flags & current->cell->Interior) && timePassed >= 10) { - timer.restart(); - if (test.name != current->cell->region) + + ESM::Region test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region)); + + timePassed = 0; + if (regionName != current->cell->region) { + regionName = current->cell->region; total = 0; - test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region)); } if(test.soundList.size() > 0) @@ -462,15 +465,15 @@ namespace MWSound soundIter = test.soundList.begin(); while (soundIter != test.soundList.end()) { - const ESM::NAME32 go = soundIter->sound; + const std::string go = soundIter->sound.toString(); int chance = (int) soundIter->chance; //std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; soundIter++; if( r - pos < chance) { //play sound - std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n"; - mEnvironment.mSoundManager->playSound(go.name, 20.0, 1.0); + std::cout << "Sound: " << go <<" Chance:" << chance << "\n"; + mEnvironment.mSoundManager->playSound(go, 20.0, 1.0); break; } @@ -480,7 +483,7 @@ namespace MWSound } else if(current->cell->data.flags & current->cell->Interior) { - test.name = ""; + regionName = ""; } } diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 64cac77135..39370d755d 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -3,15 +3,11 @@ #include -#include -#include - #include #include #include -#include -#include + #include #include "../mwworld/ptr.hpp" @@ -28,17 +24,11 @@ namespace Mangle namespace Sound { typedef boost::shared_ptr SoundPtr; - //struct OgreOutputUpdater; } } typedef OEngine::Sound::SoundManagerPtr OEManagerPtr; -namespace ESMS -{ - struct ESMStore; -} - namespace MWWorld { struct Environment; @@ -57,10 +47,6 @@ namespace MWSound MWWorld::Environment& mEnvironment; - int total; - ESM::Region test; - boost::timer timer; - void streamMusicFull (const std::string& filename); ///< Play a soundifle /// \param absolute filename @@ -85,10 +71,13 @@ namespace MWSound typedef std::map PtrMap; PtrMap sounds; + // A list of all sound files used to lookup paths Files::PathContainer mSoundFiles; + // A library of all Music file paths stored by the folder they are contained in Files::FileLibrary mMusicLibrary; + // Points to the current playlist of music files stored in the music library const Files::PathContainer* mCurrentPlaylist; std::string lookup(const std::string &soundId, @@ -115,8 +104,10 @@ namespace MWSound /// \param filename name of a sound file in "Music/" in the data directory. void startRandomTitle(); + ///< Starts a random track from the current playlist bool isMusicPlaying(); + ///< Returns true if music is playing bool setPlaylist(std::string playlist=""); ///< Set the playlist to an existing folder From a309ef7b55bbf7dfc73e3851cd0e2edabe931ce2 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Fri, 9 Mar 2012 03:56:29 +0200 Subject: [PATCH 252/269] Corrected setPlaylist and added stopMusic --- apps/openmw/mwsound/soundmanager.cpp | 13 ++++++++++++- apps/openmw/mwsound/soundmanager.hpp | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 105dc5a3d0..270a45656b 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -266,6 +266,13 @@ namespace MWSound } } + void SoundManager::stopMusic() + { + if (music) + music->stop(); + setPlaylist(); + } + void SoundManager::streamMusicFull(const std::string& filename) { @@ -327,7 +334,11 @@ namespace MWSound { const Files::PathContainer* previousPlaylist; previousPlaylist = mCurrentPlaylist; - if(mMusicLibrary.containsSection(playlist, mFSStrict)) + if (playlist == "") + { + mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict); + } + else if(mMusicLibrary.containsSection(playlist, mFSStrict)) { mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict); } diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 39370d755d..29aacb373e 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -99,6 +99,9 @@ namespace MWSound MWWorld::Environment& environment); ~SoundManager(); + void stopMusic(); + ///< Stops music if it's playing + void streamMusic(const std::string& filename); ///< Play a soundifle /// \param filename name of a sound file in "Music/" in the data directory. From 5beee8598ded738fc30ab4386c7b8c6cdf029ef1 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Fri, 9 Mar 2012 15:34:47 +0100 Subject: [PATCH 253/269] Giving the windows installer a more personal look. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 370b499e6a..7564fab90e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -343,6 +343,9 @@ if(WIN32) SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org") SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe") + SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") + SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.ico") + # SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp") SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe") if(EXISTS ${VCREDIST32}) From 6f46f2b7a0d5fd16ba89a843aae82fa8b48d46e0 Mon Sep 17 00:00:00 2001 From: Michael Papageorgiou Date: Fri, 9 Mar 2012 18:10:23 +0200 Subject: [PATCH 254/269] SoundManager: add the ability to play non-3d looping sounds --- apps/openmw/mwsound/soundmanager.cpp | 25 +++++++++++++++++++++++-- apps/openmw/mwsound/soundmanager.hpp | 7 ++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 270a45656b..2440eda238 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -386,18 +386,28 @@ namespace MWSound } - void SoundManager::playSound(const std::string& soundId, float volume, float pitch) + void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop) { - // Play and forget float min, max; const std::string &file = lookup(soundId, volume, min, max); if (file != "") { SoundPtr snd = mgr->load(file); + snd->setRepeat(loop); snd->setVolume(volume); snd->setRange(min,max); snd->setPitch(pitch); snd->play(); + + if (loop) + { + // Only add the looping sound once + IDMap::iterator it = mLoopedSounds.find(soundId); + if(it == mLoopedSounds.end()) + { + mLoopedSounds[soundId] = WSoundPtr(snd); + } + } } } @@ -421,6 +431,17 @@ namespace MWSound removeCell(cell); } + void SoundManager::stopSound(const std::string& soundId) + { + IDMap::iterator it = mLoopedSounds.find(soundId); + if(it != mLoopedSounds.end()) + { + SoundPtr snd = it->second.lock(); + if(snd) snd->stop(); + mLoopedSounds.erase(it); + } + } + bool SoundManager::getSoundPlaying (MWWorld::Ptr ptr, const std::string& soundId) const { // Mark all sounds as playing, otherwise the scripts will just diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp index 29aacb373e..03c19ce774 100644 --- a/apps/openmw/mwsound/soundmanager.hpp +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -80,6 +80,8 @@ namespace MWSound // Points to the current playlist of music files stored in the music library const Files::PathContainer* mCurrentPlaylist; + IDMap mLoopedSounds; + std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void add(const std::string &file, @@ -130,7 +132,7 @@ namespace MWSound bool sayDone (MWWorld::Ptr reference) const; ///< Is actor not speaking? - void playSound (const std::string& soundId, float volume, float pitch); + void playSound (const std::string& soundId, float volume, float pitch, bool loop=false); ///< Play a sound, independently of 3D-position void playSound3D (MWWorld::Ptr reference, const std::string& soundId, @@ -144,6 +146,9 @@ namespace MWSound void stopSound (MWWorld::Ptr::CellStore *cell); ///< Stop all sounds for the given cell. + void stopSound(const std::string& soundId); + ///< Stop a non-3d looping sound + bool getSoundPlaying (MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? From 69127fa8f13fb900da9bb867428cd99917f0fc1c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 9 Mar 2012 17:33:10 +0100 Subject: [PATCH 255/269] boost compatibility fix; added a missing include --- components/files/filelibrary.cpp | 4 ++-- components/files/fileops.cpp | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/files/filelibrary.cpp b/components/files/filelibrary.cpp index baefd79ebc..f1467166e3 100644 --- a/components/files/filelibrary.cpp +++ b/components/files/filelibrary.cpp @@ -44,13 +44,13 @@ namespace Files { if( !acceptableExtensions.empty() ) { - fileExtension = listIter->extension().string(); + fileExtension = boost::filesystem::path (listIter->extension()).string(); boost::algorithm::to_lower(fileExtension); if(!containsVectorString(acceptableExtensions, fileExtension)) continue; } - type = listIter->parent_path().leaf().string(); + type = boost::filesystem::path (listIter->parent_path().leaf()).string(); if (!strict) boost::algorithm::to_lower(type); diff --git a/components/files/fileops.cpp b/components/files/fileops.cpp index d6365e7534..f57eaa5462 100644 --- a/components/files/fileops.cpp +++ b/components/files/fileops.cpp @@ -1,4 +1,7 @@ #include "fileops.hpp" + +#include + #include #include From 2196e1321b71b83c182b2dcb8bfb0ee79e6ec751 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 9 Mar 2012 18:30:03 +0100 Subject: [PATCH 256/269] added weather sounds --- apps/openmw/mwworld/weather.cpp | 34 +++++++++++++++++++++++++++++++++ apps/openmw/mwworld/weather.hpp | 4 +++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 059b0ec1e2..eadd4b353e 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -722,6 +722,40 @@ void WeatherManager::update(float duration) mRendering->skyDisable(); mRendering->getSkyManager()->setThunder(0.f); } + + // play sounds + std::string ambientSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID : ""); + if (ambientSnd != "") + { + if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) + { + mSoundsPlaying.push_back(ambientSnd); + mEnvironment->mSoundManager->playSound(ambientSnd, 1.0, 1.0, true); + } + } + + std::string rainSnd = (mNextWeather == "" ? mWeatherSettings[mCurrentWeather].mRainLoopSoundID : ""); + if (rainSnd != "") + { + if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) + { + mSoundsPlaying.push_back(rainSnd); + mEnvironment->mSoundManager->playSound(rainSnd, 1.0, 1.0, true); + } + } + + // stop sounds + std::vector::iterator it=mSoundsPlaying.begin(); + while (it!=mSoundsPlaying.end()) + { + if ( *it != ambientSnd && *it != rainSnd) + { + mEnvironment->mSoundManager->stopSound(*it); + it = mSoundsPlaying.erase(it); + } + else + ++it; + } } void WeatherManager::setHour(const float hour) diff --git a/apps/openmw/mwworld/weather.hpp b/apps/openmw/mwworld/weather.hpp index 7a719252b0..1828a90c94 100644 --- a/apps/openmw/mwworld/weather.hpp +++ b/apps/openmw/mwworld/weather.hpp @@ -243,8 +243,10 @@ namespace MWWorld MWWorld::Environment* mEnvironment; std::map mWeatherSettings; - + std::map mRegionOverrides; + + std::vector mSoundsPlaying; Ogre::String mCurrentWeather; Ogre::String mNextWeather; From 06e0706b355aa9568986e875e530aea2f6ea0c68 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 10 Mar 2012 12:11:07 +0100 Subject: [PATCH 257/269] added fill and clear functions to container store --- apps/openmw/mwworld/containerstore.cpp | 40 +++++++++++++++++++++++++- apps/openmw/mwworld/containerstore.hpp | 11 +++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 54908deec1..a08aabdec6 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -5,6 +5,10 @@ #include #include +#include + +#include "manualref.hpp" + MWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask) { return ContainerStoreIterator (mask, this); @@ -17,7 +21,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() void MWWorld::ContainerStore::add (const Ptr& ptr) { - /// \todo implement item stocking + /// \todo implement item stacking switch (getType (ptr)) { @@ -36,6 +40,40 @@ void MWWorld::ContainerStore::add (const Ptr& ptr) } } +void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const ESMS::ESMStore& store) +{ + for (std::vector::const_iterator iter (items.list.begin()); iter!=items.list.end(); + ++iter) + { + ManualRef ref (store, iter->item.toString()); + + if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) + { + /// \todo implement leveled item lists + continue; + } + + ref.getPtr().getRefData().setCount (iter->count); + add (ref.getPtr()); + } +} + +void MWWorld::ContainerStore::clear() +{ + potions.list.clear(); + appas.list.clear(); + armors.list.clear(); + books.list.clear(); + clothes.list.clear(); + ingreds.list.clear(); + lights.list.clear(); + lockpicks.list.clear(); + miscItems.list.clear(); + probes.list.clear(); + repairs.list.clear(); + weapons.list.clear(); +} + int MWWorld::ContainerStore::getType (const Ptr& ptr) { if (ptr.isEmpty()) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 7263245f36..a38d9509d6 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -6,6 +6,11 @@ #include "refdata.hpp" #include "ptr.hpp" +namespace ESM +{ + struct InventoryList; +} + namespace MWWorld { class ContainerStoreIterator; @@ -60,6 +65,12 @@ namespace MWWorld /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! + void fill (const ESM::InventoryList& items, const ESMS::ESMStore& store); + ///< Insert items into *this. + + void clear(); + ///< Empty container. + static int getType (const Ptr& ptr); ///< This function throws an exception, if ptr does not point to an object, that can be /// put into a container. From 55626535786f9a3e0adbd940615084e1cbe55191 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 10 Mar 2012 12:36:29 +0100 Subject: [PATCH 258/269] provisionally fill up containers on cell load --- apps/openmw/mwworld/cells.cpp | 49 +++++++++++++++++++++++++++++++++++ apps/openmw/mwworld/cells.hpp | 2 ++ 2 files changed, 51 insertions(+) diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 079c888aa6..8c657a5c83 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -5,6 +5,8 @@ #include #include "world.hpp" +#include "class.hpp" +#include "containerstore.hpp" MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) { @@ -35,6 +37,39 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getCellStore (const ESM::Cell *cell) } } +void MWWorld::Cells::fillContainers (Ptr::CellStore& cellStore) +{ + for (ESMS::CellRefList::List::iterator iter ( + cellStore.containers.list.begin()); + iter!=cellStore.containers.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } + + for (ESMS::CellRefList::List::iterator iter ( + cellStore.creatures.list.begin()); + iter!=cellStore.creatures.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } + + for (ESMS::CellRefList::List::iterator iter ( + cellStore.npcs.list.begin()); + iter!=cellStore.npcs.list.end(); ++iter) + { + Ptr container (&*iter, &cellStore); + + Class::get (container).getContainerStore (container).fill ( + iter->base->inventory, mStore); + } +} + MWWorld::Cells::Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world) : mStore (store), mReader (reader), mWorld (world) {} @@ -43,6 +78,8 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) std::map, Ptr::CellStore>::iterator result = mExteriors.find (std::make_pair (x, y)); + bool fill = false; + if (result==mExteriors.end()) { const ESM::Cell *cell = mStore.cells.searchExt (x, y); @@ -63,11 +100,16 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y) result = mExteriors.insert (std::make_pair ( std::make_pair (x, y), Ptr::CellStore (cell))).first; + + fill = true; } if (result->second.mState!=Ptr::CellStore::State_Loaded) result->second.load (mStore, mReader); + if (fill) + fillContainers (result->second); + return &result->second; } @@ -75,16 +117,23 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getInterior (const std::string& name) { std::map::iterator result = mInteriors.find (name); + bool fill = false; + if (result==mInteriors.end()) { const ESM::Cell *cell = mStore.cells.findInt (name); result = mInteriors.insert (std::make_pair (name, Ptr::CellStore (cell))).first; + + fill = true; } if (result->second.mState!=Ptr::CellStore::State_Loaded) result->second.load (mStore, mReader); + if (fill) + fillContainers (result->second); + return &result->second; } diff --git a/apps/openmw/mwworld/cells.hpp b/apps/openmw/mwworld/cells.hpp index 0ecbd02d37..42aa1050c0 100644 --- a/apps/openmw/mwworld/cells.hpp +++ b/apps/openmw/mwworld/cells.hpp @@ -34,6 +34,8 @@ namespace MWWorld Ptr::CellStore *getCellStore (const ESM::Cell *cell); + void fillContainers (Ptr::CellStore& cellStore); + public: Cells (const ESMS::ESMStore& store, ESM::ESMReader& reader, MWWorld::World& world); From 84c8cd3720088f7a4d467700fc43325715688a9b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 10 Mar 2012 12:43:48 +0100 Subject: [PATCH 259/269] added InventoryStore class (doesn't do anything yet) --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwworld/containerstore.cpp | 2 ++ apps/openmw/mwworld/containerstore.hpp | 2 ++ apps/openmw/mwworld/inventorystore.cpp | 2 ++ apps/openmw/mwworld/inventorystore.hpp | 16 ++++++++++++++++ 5 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 apps/openmw/mwworld/inventorystore.cpp create mode 100644 apps/openmw/mwworld/inventorystore.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 39cd99cf6c..77f465b4c9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -44,7 +44,7 @@ add_openmw_dir (mwsound add_openmw_dir (mwworld refdata world physicssystem scene environment globals class action nullaction actionteleport containerstore actiontalk actiontake manualref player cellfunctors - cells localscripts customdata weather + cells localscripts customdata weather inventorystore ) add_openmw_dir (mwclass diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index a08aabdec6..0edc8ca141 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -9,6 +9,8 @@ #include "manualref.hpp" +MWWorld::ContainerStore::~ContainerStore() {} + MWWorld::ContainerStoreIterator MWWorld::ContainerStore::begin (int mask) { return ContainerStoreIterator (mask, this); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index a38d9509d6..ec4a0323c8 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -53,6 +53,8 @@ namespace MWWorld public: + virtual ~ContainerStore(); + ContainerStoreIterator begin (int mask = Type_All); ContainerStoreIterator end(); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp new file mode 100644 index 0000000000..aa7431da25 --- /dev/null +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -0,0 +1,2 @@ + +#include "inventorystore.hpp" diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp new file mode 100644 index 0000000000..5d78f4a0eb --- /dev/null +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -0,0 +1,16 @@ +#ifndef GAME_MWWORLD_INVENTORYSTORE_H +#define GAME_MWWORLD_INVENTORYSTORE_H + +#include "containerstore.hpp" + +namespace MWWorld +{ + ///< \brief Variant of the ContainerStore for NPCs + class InventoryStore : public ContainerStore + { + + + }; +} + +#endif From 79158b93258f8902aef4db0e4743ac08b9351112 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 10 Mar 2012 12:49:54 +0100 Subject: [PATCH 260/269] NPCs are using InventoryStore instead of ContainerStore from now on --- apps/openmw/mwclass/npc.cpp | 14 +++++++++++--- apps/openmw/mwclass/npc.hpp | 3 +++ apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 5 +++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index cc7daa83eb..83a94d27d1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -16,7 +16,7 @@ #include "../mwworld/actiontalk.hpp" #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" -#include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwworld/customdata.hpp" namespace @@ -29,7 +29,7 @@ namespace MWMechanics::NpcStats mNpcStats; MWMechanics::CreatureStats mCreatureStats; MWMechanics::Movement mMovement; - MWWorld::ContainerStore mContainerStore; + MWWorld::InventoryStore mInventoryStore; virtual MWWorld::CustomData *clone() const; }; @@ -161,7 +161,15 @@ namespace MWClass { ensureCustomData (ptr); - return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; + } + + MWWorld::InventoryStore& Npc::getInventoryStore (const MWWorld::Ptr& ptr) + const + { + ensureCustomData (ptr); + + return dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore; } std::string Npc::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index bef417332d..f210eda5f0 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -38,6 +38,9 @@ namespace MWClass virtual MWWorld::ContainerStore& getContainerStore (const MWWorld::Ptr& ptr) const; ///< Return container store + virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; + ///< Return inventory store + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const; ///< Generate action for activation diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 641da73e1e..b0b291d7b8 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -77,6 +77,11 @@ namespace MWWorld throw std::runtime_error ("class does not have a container store"); } + InventoryStore& Class::getInventoryStore (const Ptr& ptr) const + { + throw std::runtime_error ("class does not have an inventory store"); + } + void Class::lock (const Ptr& ptr, int lockLevel) const { throw std::runtime_error ("class does not support locking"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 9b6acb3ce3..da55bd8ead 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -34,6 +34,7 @@ namespace MWWorld class Ptr; class Environment; class ContainerStore; + class InventoryStore; /// \brief Base class for referenceable esm records class Class @@ -108,6 +109,10 @@ namespace MWWorld ///< Return container store or throw an exception, if class does not have a /// container store (default implementation: throw an exceoption) + virtual InventoryStore& getInventoryStore (const Ptr& ptr) const; + ///< Return inventory store or throw an exception, if class does not have a + /// inventory store (default implementation: throw an exceoption) + virtual void lock (const Ptr& ptr, int lockLevel) const; ///< Lock object (default implementation: throw an exception) From 96c0b8760e46116b69cb6da1815d05179d088877 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Mon, 12 Mar 2012 02:31:37 +0100 Subject: [PATCH 261/269] Got rid of the console showing up on Windows and removed libpng stuff --- apps/launcher/CMakeLists.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index f0e5eba948..a34ad7429c 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -41,10 +41,11 @@ source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC find_package(Qt4 REQUIRED) set(QT_USE_QTGUI 1) -if (NOT APPLE) # this dependency can be completely removed, but now it only tested on OS X - find_package(PNG REQUIRED) - include_directories(${PNG_INCLUDE_DIR}) -endif() +# Set some platform specific settings +if(WIN32) + set(GUI_TYPE WIN32) + set(QT_USE_QTMAIN TRUE) +endif(WIN32) QT4_ADD_RESOURCES(RCC_SRCS resources.qrc) QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC}) @@ -53,6 +54,7 @@ include(${QT_USE_FILE}) # Main executable add_executable(omwlauncher + ${GUI_TYPE} ${LAUNCHER} ${RCC_SRCS} ${MOC_SRCS} @@ -62,7 +64,6 @@ target_link_libraries(omwlauncher ${Boost_LIBRARIES} ${OGRE_LIBRARIES} ${QT_LIBRARIES} - ${PNG_LIBRARY} components ) From 1ed1c29db9a62fc5765787a5d7dfd3ce6c406885 Mon Sep 17 00:00:00 2001 From: Pieter van der Kloet Date: Mon, 12 Mar 2012 14:42:24 +0100 Subject: [PATCH 262/269] Fix Boost path problem for Boost versions below 1.43 --- apps/launcher/datafilespage.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index c954468080..054cbf1414 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -9,6 +9,23 @@ #include "pluginsmodel.hpp" #include "pluginsview.hpp" +#include +/** + * Workaround for problems with whitespaces in paths in older versions of Boost library + */ +#if (BOOST_VERSION <= 104600) +namespace boost +{ + + template<> + inline boost::filesystem::path lexical_cast(const std::string& arg) + { + return boost::filesystem::path(arg); + } + +} /* namespace boost */ +#endif /* (BOOST_VERSION <= 104600) */ + using namespace ESM; using namespace std; From 6325697bfebfb1d0ff4e3f93d8ed28ee3de77f7e Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Mon, 12 Mar 2012 20:47:47 +0400 Subject: [PATCH 263/269] Change in CMakeLists.txt for OS X due to changed ogre plugin path variable name --- CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7564fab90e..ca747f9a56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,6 +255,19 @@ if (APPLE) "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) # prepare plugins + if (${CMAKE_BUILD_TYPE} MATCHES "Release") + set(OPENMW_RELEASE_BUILD 1) + endif() + if (${CMAKE_BUILD_TYPE} MATCHES "RelWithDebugInfo") + set(OPENMW_RELEASE_BUILD 1) + endif() + + if (${OPENMW_RELEASE_BUILD}) + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + else() + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) + endif() + foreach(plugin ${USED_OGRE_PLUGINS}) configure_file("${OGRE_PLUGIN_DIR}/${plugin}.dylib" "${APP_BUNDLE_DIR}/Contents/Plugins/${plugin}.dylib" From 2d9648f88e19b5f6da31747fbabb5baec7077b05 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 13 Mar 2012 12:11:39 +0100 Subject: [PATCH 264/269] deleted submodules --- libs/mangle | 1 - libs/openengine | 1 - 2 files changed, 2 deletions(-) delete mode 160000 libs/mangle delete mode 160000 libs/openengine diff --git a/libs/mangle b/libs/mangle deleted file mode 160000 index 14b2851e72..0000000000 --- a/libs/mangle +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 14b2851e72f610ae81dd296598867e6fb0babd2a diff --git a/libs/openengine b/libs/openengine deleted file mode 160000 index 8f98718315..0000000000 --- a/libs/openengine +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8f98718315fe11af359740c4a025fd1ca52a9157 From 635b7ec6cb323408be8c87783ced7191087b9e07 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 13 Mar 2012 13:31:11 +0100 Subject: [PATCH 265/269] slot system --- apps/openmw/mwworld/containerstore.hpp | 3 ++ apps/openmw/mwworld/inventorystore.cpp | 68 ++++++++++++++++++++++++++ apps/openmw/mwworld/inventorystore.hpp | 41 ++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index ec4a0323c8..89f65bc5b3 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -1,6 +1,8 @@ #ifndef GAME_MWWORLD_CONTAINERSTORE_H #define GAME_MWWORLD_CONTAINERSTORE_H +#include + #include #include "refdata.hpp" @@ -84,6 +86,7 @@ namespace MWWorld /// /// \note The iterator will automatically skip over deleted objects. class ContainerStoreIterator + : public std::iterator { int mType; int mMask; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index aa7431da25..363781294d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -1,2 +1,70 @@ #include "inventorystore.hpp" + +#include + +void MWWorld::InventoryStore::copySlots (const InventoryStore& store) +{ + // some const-trickery, required because of a flaw in the handling of MW-references and the + // resulting workarounds + for (std::vector::const_iterator iter ( + const_cast (store).mSlots.begin()); + iter!=const_cast (store).mSlots.end(); ++iter) + { + std::size_t distance = std::distance (const_cast (store).begin(), *iter); + + ContainerStoreIterator slot = begin(); + + std::advance (slot, distance); + + mSlots.push_back (slot); + } +} + +MWWorld::InventoryStore::InventoryStore() +{ + for (int i=0; i=static_cast (mSlots.size())) + throw std::runtime_error ("slot number out of range"); + + /// \todo verify slot + + mSlots[slot] = iterator; +} + +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) +{ + if (slot<0 || slot>=static_cast (mSlots.size())) + throw std::runtime_error ("slot number out of range"); + + if (mSlots[slot]==end()) + return end(); + + if (mSlots[slot]->getRefData().getCount()<1) + { + // object has been deleted + mSlots[slot] = end(); + return end(); + } + + return mSlots[slot]; +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 5d78f4a0eb..cfde6c6876 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -8,8 +8,49 @@ namespace MWWorld ///< \brief Variant of the ContainerStore for NPCs class InventoryStore : public ContainerStore { + public: + static const int Slot_Helmet = 0; + static const int Slot_Cuirass = 1; + static const int Slot_Greaves = 2; + static const int Slot_LeftPauldron = 3; + static const int Slot_RightPauldron = 4; + static const int Slot_LeftGauntlet = 5; + static const int Slot_RightGauntlet = 6; + static const int Slot_Boots = 7; + static const int Slot_Shirt = 8; + static const int Slot_Pants = 9; + static const int Slot_Skirt = 10; + static const int Slot_Robe = 11; + static const int Slot_LeftRing = 12; + static const int Slot_RightRing = 13; + static const int Slot_Amulet = 14; + static const int Slot_Belt = 15; + static const int Slot_CarriedRight = 16; + static const int Slot_CarriedLeft = 17; + static const int Slot_Ammunition = 18; + static const int Slots = 19; + + static const int Slot_NoSlot = -1; + + private: + + mutable std::vector mSlots; + + void copySlots (const InventoryStore& store); + + public: + + InventoryStore(); + + InventoryStore (const InventoryStore& store); + + InventoryStore& operator= (const InventoryStore& store); + + void equip (int slot, const ContainerStoreIterator& iterator); + + ContainerStoreIterator getSlot (int slot); }; } From 4b73116b9d6b2787879c399e820441ef817eb990 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 13 Mar 2012 13:45:50 +0100 Subject: [PATCH 266/269] added getEquipmentSlots function to MWWorld::Class hierarchy --- apps/openmw/mwworld/class.cpp | 5 +++++ apps/openmw/mwworld/class.hpp | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index b0b291d7b8..dd8b1efade 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -127,6 +127,11 @@ namespace MWWorld return Ogre::Vector3 (0, 0, 0); } + std::pair, bool> Class::getEquipmentSlots (const Ptr& pt) const + { + return std::make_pair (std::vector(), false); + } + const Class& Class::get (const std::string& key) { std::map >::const_iterator iter = sClasses.find (key); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index da55bd8ead..5643e8df47 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -142,6 +143,12 @@ namespace MWWorld ///< Return desired movement vector (determined based on movement settings, /// stance and stats). + virtual std::pair, bool> getEquipmentSlots (const Ptr& pt) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + /// + /// Default implementation: return (empty vector, false). + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. From ea336214de6f4139f69aa3a66391180255aae684 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 13 Mar 2012 14:04:19 +0100 Subject: [PATCH 267/269] more inventory sanity checks --- apps/openmw/mwworld/containerstore.cpp | 5 +++++ apps/openmw/mwworld/containerstore.hpp | 2 ++ apps/openmw/mwworld/inventorystore.cpp | 18 +++++++++++++++++- apps/openmw/mwworld/inventorystore.hpp | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 0edc8ca141..c498cabced 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -371,6 +371,11 @@ int MWWorld::ContainerStoreIterator::getType() const return mType; } +const MWWorld::ContainerStore *MWWorld::ContainerStoreIterator::getContainerStore() const +{ + return mContainer; +} + bool MWWorld::operator== (const ContainerStoreIterator& left, const ContainerStoreIterator& right) { return left.isEqual (right); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 89f65bc5b3..69e5ba4466 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -142,6 +142,8 @@ namespace MWWorld int getType() const; + const ContainerStore *getContainerStore() const; + friend class ContainerStore; }; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 363781294d..264be7bb33 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -2,6 +2,9 @@ #include "inventorystore.hpp" #include +#include + +#include "class.hpp" void MWWorld::InventoryStore::copySlots (const InventoryStore& store) { @@ -46,7 +49,20 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite if (slot<0 || slot>=static_cast (mSlots.size())) throw std::runtime_error ("slot number out of range"); - /// \todo verify slot + if (iterator.getContainerStore()!=this) + throw std::runtime_error ("attempt to equip an item that is not in the inventory"); + + if (iterator!=end()) + { + std::pair, bool> slots = Class::get (*iterator).getEquipmentSlots (*iterator); + + if (std::find (slots.first.begin(), slots.first.end(), slot)==slots.first.end()) + throw std::runtime_error ("invalid slot"); + } + + /// \todo restack item previously in this slot (if required) + + /// \todo unstack item pointed to by iterator if required) mSlots[slot] = iterator; } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index cfde6c6876..c41f9e8e37 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -49,6 +49,7 @@ namespace MWWorld InventoryStore& operator= (const InventoryStore& store); void equip (int slot, const ContainerStoreIterator& iterator); + ///< \note \a iteartor can be an end-iterator ContainerStoreIterator getSlot (int slot); }; From c314cddb1cc6dd9dcf2fee5d9793da091f76f2af Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 13 Mar 2012 14:12:07 +0100 Subject: [PATCH 268/269] added getEquipmentSkill function to Class hierarchy --- apps/openmw/mwworld/class.cpp | 7 ++++++- apps/openmw/mwworld/class.hpp | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index dd8b1efade..96afc89a1b 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -127,11 +127,16 @@ namespace MWWorld return Ogre::Vector3 (0, 0, 0); } - std::pair, bool> Class::getEquipmentSlots (const Ptr& pt) const + std::pair, bool> Class::getEquipmentSlots (const Ptr& ptr) const { return std::make_pair (std::vector(), false); } + int Class::getEuqipmentSkill (const Ptr& ptr, const Environment& environment) const + { + return -1; + } + const Class& Class::get (const std::string& key) { std::map >::const_iterator iter = sClasses.find (key); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 5643e8df47..49c7bed0e2 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -143,12 +143,18 @@ namespace MWWorld ///< Return desired movement vector (determined based on movement settings, /// stance and stats). - virtual std::pair, bool> getEquipmentSlots (const Ptr& pt) const; + virtual std::pair, bool> getEquipmentSlots (const Ptr& ptr) const; ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? /// /// Default implementation: return (empty vector, false). + virtual int getEuqipmentSkill (const Ptr& ptr, const Environment& environment) + const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + /// (default implementation: return -1) + static const Class& get (const std::string& key); ///< If there is no class for this \a key, an exception is thrown. From cbbc295eb66c427ad047b59b10961b2c841741f4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 13 Mar 2012 15:35:06 +0100 Subject: [PATCH 269/269] implemented new equipment functions in MWClass --- apps/openmw/mwclass/armor.cpp | 78 ++++++++++++++++++++++++++++++++ apps/openmw/mwclass/armor.hpp | 9 ++++ apps/openmw/mwclass/clothing.cpp | 53 ++++++++++++++++++++++ apps/openmw/mwclass/clothing.hpp | 9 ++++ apps/openmw/mwclass/light.cpp | 14 ++++++ apps/openmw/mwclass/light.hpp | 4 ++ apps/openmw/mwclass/lockpick.cpp | 10 ++++ apps/openmw/mwclass/lockpick.hpp | 4 ++ apps/openmw/mwclass/probe.cpp | 10 ++++ apps/openmw/mwclass/probe.hpp | 4 ++ apps/openmw/mwclass/weapon.cpp | 56 +++++++++++++++++++++++ apps/openmw/mwclass/weapon.hpp | 9 ++++ 12 files changed, 260 insertions(+) diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 3cdf631197..2dc8614307 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -2,11 +2,16 @@ #include "armor.hpp" #include +#include +#include #include #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" +#include "../mwworld/environment.hpp" +#include "../mwworld/world.hpp" #include "../mwrender/objects.hpp" @@ -77,6 +82,79 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Armor::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + const int size = 11; + + static const int sMapping[size][2] = + { + { ESM::Armor::Helmet, MWWorld::InventoryStore::Slot_Helmet }, + { ESM::Armor::Cuirass, MWWorld::InventoryStore::Slot_Cuirass }, + { ESM::Armor::LPauldron, MWWorld::InventoryStore::Slot_LeftPauldron }, + { ESM::Armor::RPauldron, MWWorld::InventoryStore::Slot_RightPauldron }, + { ESM::Armor::Greaves, MWWorld::InventoryStore::Slot_Greaves }, + { ESM::Armor::Boots, MWWorld::InventoryStore::Slot_Boots }, + { ESM::Armor::LGauntlet, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Armor::RGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet }, + { ESM::Armor::Shield, MWWorld::InventoryStore::Slot_CarriedLeft }, + { ESM::Armor::LBracer, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Armor::RBracer, MWWorld::InventoryStore::Slot_RightGauntlet } + }; + + for (int i=0; ibase->data.type) + { + slots.push_back (int (sMapping[i][1])); + break; + } + + return std::make_pair (slots, false); + } + + int Armor::getEuqipmentSkill (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::string typeGmst; + + switch (ref->base->data.type) + { + case ESM::Armor::Helmet: typeGmst = "iHelmWeight"; break; + case ESM::Armor::Cuirass: typeGmst = "iCuirassWeight"; break; + case ESM::Armor::LPauldron: + case ESM::Armor::RPauldron: typeGmst = "iPauldronWeight"; break; + case ESM::Armor::Greaves: typeGmst = "iGreavesWeight"; break; + case ESM::Armor::Boots: typeGmst = "iBootsWeight"; break; + case ESM::Armor::LGauntlet: + case ESM::Armor::RGauntlet: typeGmst = "iGauntletWeight"; break; +/// \todo how to determine if shield light, medium or heavy? +// case ESM::Armor::Shield: + case ESM::Armor::LBracer: + case ESM::Armor::RBracer: typeGmst = "iGauntletWeight"; break; + } + + if (typeGmst.empty()) + return -1; + + float iWeight = environment.mWorld->getStore().gameSettings.find (typeGmst)->f; + + if (iWeight * environment.mWorld->getStore().gameSettings.find ("fLightMaxMod")->f<= + ref->base->data.weight) + return ESM::Skill::LightArmor; + + if (iWeight * environment.mWorld->getStore().gameSettings.find ("fMedMaxMod")->f<= + ref->base->data.weight) + return ESM::Skill::MediumArmor; + + return ESM::Skill::HeavyArmor; + } + void Armor::registerSelf() { boost::shared_ptr instance (new Armor); diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 060bc364e8..6c78a535ab 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -31,6 +31,15 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEuqipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 88c43d82cb..9af59937a7 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -7,6 +7,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" @@ -65,6 +66,58 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Clothing::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + if (ref->base->data.type==ESM::Clothing::Ring) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_LeftRing)); + slots.push_back (int (MWWorld::InventoryStore::Slot_RightRing)); + } + else + { + const int size = 9; + + static const int sMapping[size][2] = + { + { ESM::Clothing::Shirt, MWWorld::InventoryStore::Slot_Cuirass }, + { ESM::Clothing::Belt, MWWorld::InventoryStore::Slot_Belt }, + { ESM::Clothing::Robe, MWWorld::InventoryStore::Slot_Robe }, + { ESM::Clothing::Pants, MWWorld::InventoryStore::Slot_Pants }, + { ESM::Clothing::Shoes, MWWorld::InventoryStore::Slot_Boots }, + { ESM::Clothing::LGlove, MWWorld::InventoryStore::Slot_LeftGauntlet }, + { ESM::Clothing::RGlove, MWWorld::InventoryStore::Slot_RightGauntlet }, + { ESM::Clothing::Skirt, MWWorld::InventoryStore::Slot_Skirt }, + { ESM::Clothing::Amulet, MWWorld::InventoryStore::Slot_Amulet } + }; + + for (int i=0; ibase->data.type) + { + slots.push_back (int (sMapping[i][1])); + break; + } + } + + return std::make_pair (slots, false); + } + + int Clothing::getEuqipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + if (ref->base->data.type==ESM::Clothing::Shoes) + return ESM::Skill::Unarmored; + + return -1; + } + void Clothing::registerSelf() { boost::shared_ptr instance (new Clothing); diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 606aba9e02..448c73a738 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -25,6 +25,15 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEuqipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index f9ec1c9569..fc3c7a6a94 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -9,6 +9,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/nullaction.hpp" #include "../mwworld/environment.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwsound/soundmanager.hpp" @@ -94,6 +95,19 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Light::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + + if (ref->base->data.flags & ESM::Light::Carry) + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedLeft)); + + return std::make_pair (slots, false); + } + void Light::registerSelf() { boost::shared_ptr instance (new Light); diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index c9940d0a59..179531808b 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -30,6 +30,10 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); }; } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 98c05a1b3f..5fc6ea9c7d 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -7,6 +7,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" @@ -66,6 +67,15 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Lockpick::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + std::vector slots; + + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, false); + } + void Lockpick::registerSelf() { boost::shared_ptr instance (new Lockpick); diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index 9cbfa0d238..5465e52155 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -25,6 +25,10 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); }; } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index de024e4303..d70cda83d2 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -7,6 +7,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" @@ -65,6 +66,15 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Probe::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + std::vector slots; + + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, false); + } + void Probe::registerSelf() { boost::shared_ptr instance (new Probe); diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index 3f2bfed5be..2aadaa22ec 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -25,6 +25,10 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + static void registerSelf(); }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 90fd3e33bb..e4cb4582b2 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -7,6 +7,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwrender/objects.hpp" @@ -78,6 +79,61 @@ namespace MWClass return ref->base->script; } + std::pair, bool> Weapon::getEquipmentSlots (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + std::vector slots; + bool stack = false; + + if (ref->base->data.type==ESM::Weapon::Arrow || ref->base->data.type==ESM::Weapon::Bolt) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_Ammunition)); + stack = true; + } + else if (ref->base->data.type==ESM::Weapon::MarksmanThrown) + { + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + stack = true; + } + else + slots.push_back (int (MWWorld::InventoryStore::Slot_CarriedRight)); + + return std::make_pair (slots, stack); + } + + int Weapon::getEuqipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + const int size = 12; + + static const int sMapping[size][2] = + { + { ESM::Weapon::ShortBladeOneHand, ESM::Skill::ShortBlade }, + { ESM::Weapon::LongBladeOneHand, ESM::Skill::LongBlade }, + { ESM::Weapon::LongBladeTwoHand, ESM::Skill::LongBlade }, + { ESM::Weapon::BluntOneHand, ESM::Skill::BluntWeapon }, + { ESM::Weapon::BluntTwoClose, ESM::Skill::BluntWeapon }, + { ESM::Weapon::BluntTwoWide, ESM::Skill::BluntWeapon }, + { ESM::Weapon::SpearTwoWide, ESM::Skill::Spear }, + { ESM::Weapon::AxeOneHand, ESM::Skill::Axe }, + { ESM::Weapon::AxeTwoHand, ESM::Skill::Axe }, + { ESM::Weapon::MarksmanBow, ESM::Skill::Marksman }, + { ESM::Weapon::MarksmanCrossbow, ESM::Skill::Marksman }, + { ESM::Weapon::MarksmanThrown, ESM::Skill::Marksman } + }; + + for (int i=0; ibase->data.type) + return sMapping[i][1]; + + return -1; + } + void Weapon::registerSelf() { boost::shared_ptr instance (new Weapon); diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index b056249b93..2b6dfb5a8f 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -31,6 +31,15 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual std::pair, bool> getEquipmentSlots (const MWWorld::Ptr& ptr) const; + ///< \return first: Return IDs of the slot this object can be equipped in; second: can object + /// stay stacked when equipped? + + virtual int getEuqipmentSkill (const MWWorld::Ptr& ptr, + const MWWorld::Environment& environment) const; + /// Return the index of the skill this item corresponds to when equiopped or -1, if there is + /// no such skill. + static void registerSelf(); }; }