forked from mirror/openmw-tes3mp
Merge branch 'master' into water
Conflicts: apps/openmw/CMakeLists.txt apps/openmw/mwrender/renderingmanager.hpp
This commit is contained in:
commit
e6303fc3f4
138 changed files with 3986 additions and 4322 deletions
123
Bitstream Vera License.txt
Normal file
123
Bitstream Vera License.txt
Normal file
|
@ -0,0 +1,123 @@
|
|||
Bitstream Vera Fonts Copyright
|
||||
|
||||
The fonts have a generous copyright, allowing derivative works (as
|
||||
long as "Bitstream" or "Vera" are not in the names), and full
|
||||
redistribution (so long as they are not *sold* by themselves). They
|
||||
can be be bundled, redistributed and sold with any software.
|
||||
|
||||
The fonts are distributed under the following copyright:
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||
Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute
|
||||
the Font Software, including without limitation the rights to use,
|
||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||
Software, and to permit persons to whom the Font Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font
|
||||
Software without prior written authorization from the Gnome Foundation
|
||||
or Bitstream Inc., respectively. For further information, contact:
|
||||
fonts at gnome dot org.
|
||||
|
||||
Copyright FAQ
|
||||
=============
|
||||
|
||||
1. I don't understand the resale restriction... What gives?
|
||||
|
||||
Bitstream is giving away these fonts, but wishes to ensure its
|
||||
competitors can't just drop the fonts as is into a font sale system
|
||||
and sell them as is. It seems fair that if Bitstream can't make money
|
||||
from the Bitstream Vera fonts, their competitors should not be able to
|
||||
do so either. You can sell the fonts as part of any software package,
|
||||
however.
|
||||
|
||||
2. I want to package these fonts separately for distribution and
|
||||
sale as part of a larger software package or system. Can I do so?
|
||||
|
||||
Yes. A RPM or Debian package is a "larger software package" to begin
|
||||
with, and you aren't selling them independently by themselves.
|
||||
See 1. above.
|
||||
|
||||
3. Are derivative works allowed?
|
||||
Yes!
|
||||
|
||||
4. Can I change or add to the font(s)?
|
||||
Yes, but you must change the name(s) of the font(s).
|
||||
|
||||
5. Under what terms are derivative works allowed?
|
||||
|
||||
You must change the name(s) of the fonts. This is to ensure the
|
||||
quality of the fonts, both to protect Bitstream and Gnome. We want to
|
||||
ensure that if an application has opened a font specifically of these
|
||||
names, it gets what it expects (though of course, using fontconfig,
|
||||
substitutions could still could have occurred during font
|
||||
opening). You must include the Bitstream copyright. Additional
|
||||
copyrights can be added, as per copyright law. Happy Font Hacking!
|
||||
|
||||
6. If I have improvements for Bitstream Vera, is it possible they might get
|
||||
adopted in future versions?
|
||||
|
||||
Yes. The contract between the Gnome Foundation and Bitstream has
|
||||
provisions for working with Bitstream to ensure quality additions to
|
||||
the Bitstream Vera font family. Please contact us if you have such
|
||||
additions. Note, that in general, we will want such additions for the
|
||||
entire family, not just a single font, and that you'll have to keep
|
||||
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
|
||||
glyphs to the font, they must be stylistically in keeping with Vera's
|
||||
design. Vera cannot become a "ransom note" font. Jim Lyles will be
|
||||
providing a document describing the design elements used in Vera, as a
|
||||
guide and aid for people interested in contributing to Vera.
|
||||
|
||||
7. I want to sell a software package that uses these fonts: Can I do so?
|
||||
|
||||
Sure. Bundle the fonts with your software and sell your software
|
||||
with the fonts. That is the intent of the copyright.
|
||||
|
||||
8. If applications have built the names "Bitstream Vera" into them,
|
||||
can I override this somehow to use fonts of my choosing?
|
||||
|
||||
This depends on exact details of the software. Most open source
|
||||
systems and software (e.g., Gnome, KDE, etc.) are now converting to
|
||||
use fontconfig (see www.fontconfig.org) to handle font configuration,
|
||||
selection and substitution; it has provisions for overriding font
|
||||
names and subsituting alternatives. An example is provided by the
|
||||
supplied local.conf file, which chooses the family Bitstream Vera for
|
||||
"sans", "serif" and "monospace". Other software (e.g., the XFree86
|
||||
core server) has other mechanisms for font substitution.
|
|
@ -4,9 +4,6 @@ if (APPLE)
|
|||
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
|
||||
|
||||
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
|
||||
|
||||
# using 10.6 sdk
|
||||
set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk")
|
||||
endif (APPLE)
|
||||
|
||||
# Macros
|
||||
|
@ -30,7 +27,6 @@ configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_
|
|||
option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE)
|
||||
|
||||
# Sound source selection
|
||||
option(USE_AUDIERE "use Audiere for sound" OFF)
|
||||
option(USE_FFMPEG "use ffmpeg for sound" OFF)
|
||||
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
|
||||
|
||||
|
@ -120,52 +116,31 @@ set(OENGINE_BULLET
|
|||
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
|
||||
)
|
||||
|
||||
# Sound setup
|
||||
if (USE_AUDIERE)
|
||||
set(MANGLE_SOUND_OUTPUT
|
||||
${LIBDIR}/mangle/sound/sources/audiere_source.cpp
|
||||
${LIBDIR}/mangle/sound/sources/sample_reader.cpp
|
||||
${LIBDIR}/mangle/stream/clients/audiere_file.cpp)
|
||||
find_package(Audiere REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${AUDIERE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${AUDIERE_LIBRARY})
|
||||
set(SOUND_DEFINE -DOPENMW_USE_AUDIERE)
|
||||
endif (USE_AUDIERE)
|
||||
|
||||
if (USE_FFMPEG)
|
||||
set(MANGLE_SOUND_OUTPUT
|
||||
${LIBDIR}/mangle/sound/sources/ffmpeg_source.cpp)
|
||||
find_package(FFMPEG REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES})
|
||||
set(SOUND_DEFINE -DOPENMW_USE_FFMPEG)
|
||||
endif (USE_FFMPEG)
|
||||
|
||||
if (USE_MPG123)
|
||||
set(MANGLE_SOUND_OUTPUT
|
||||
${LIBDIR}/mangle/sound/sources/mpg123_source.cpp
|
||||
${LIBDIR}/mangle/sound/sources/libsndfile.cpp
|
||||
${LIBDIR}/mangle/sound/sources/sample_reader.cpp)
|
||||
find_package(MPG123 REQUIRED)
|
||||
find_package(SNDFILE REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
|
||||
set(SOUND_DEFINE -DOPENMW_USE_MPG123)
|
||||
endif (USE_MPG123)
|
||||
|
||||
set(OENGINE_SOUND
|
||||
# Mangle and OEngine sound files are sort of intertwined, so put
|
||||
# them together here
|
||||
${LIBDIR}/openengine/sound/sndmanager.cpp
|
||||
${LIBDIR}/mangle/sound/outputs/openal_out.cpp
|
||||
${MANGLE_SOUND_OUTPUT}
|
||||
)
|
||||
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND} ${OENGINE_BULLET})
|
||||
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_BULLET})
|
||||
source_group(libs\\openengine FILES ${OENGINE_ALL})
|
||||
|
||||
set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL})
|
||||
set(OPENMW_LIBS_HEADER)
|
||||
|
||||
# Sound setup
|
||||
set(SOUND_INPUT_INCLUDES "")
|
||||
set(SOUND_INPUT_LIBRARY "")
|
||||
set(SOUND_DEFINE "")
|
||||
if (USE_FFMPEG)
|
||||
find_package(FFMPEG REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG)
|
||||
endif (USE_FFMPEG)
|
||||
|
||||
if (USE_MPG123)
|
||||
find_package(MPG123 REQUIRED)
|
||||
find_package(SNDFILE REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
|
||||
endif (USE_MPG123)
|
||||
|
||||
# Platform specific
|
||||
if (WIN32)
|
||||
set(PLATFORM_INCLUDE_DIR "platform")
|
||||
|
@ -185,7 +160,7 @@ endif (APPLE)
|
|||
|
||||
# Dependencies
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package (Threads)
|
||||
endif()
|
||||
|
@ -281,7 +256,16 @@ endif (APPLE)
|
|||
|
||||
# Compiler settings
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-reorder)
|
||||
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder)
|
||||
|
||||
# Silence warnings in OGRE headers. Remove once OGRE got fixed!
|
||||
add_definitions (-Wno-ignored-qualifiers)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||
OUTPUT_VARIABLE GCC_VERSION)
|
||||
if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||
add_definitions (-Wno-unused-but-set-parameter)
|
||||
endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||
endif (CMAKE_COMPILER_IS_GNUCC)
|
||||
|
||||
if(DPKG_PROGRAM)
|
||||
|
@ -341,6 +325,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_SOURCE_DIR}/readme.txt" DESTINATION ".")
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
|
||||
|
||||
SET(CPACK_GENERATOR "NSIS")
|
||||
|
@ -351,6 +336,7 @@ if(WIN32)
|
|||
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO})
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;esmtool;Esmtool;omwlauncher;OpenMW Launcher")
|
||||
set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
|
||||
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
|
||||
SET(CPACK_RESOURCE_FILE_LICENSE "${OpenMW_SOURCE_DIR}/GPL3.txt")
|
||||
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||
|
@ -493,6 +479,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})
|
||||
|
|
93
OFL.txt
Normal file
93
OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@ -225,7 +225,7 @@ void DataFilesPage::setupDataFiles()
|
|||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(tr("<br><b>Could not find the Data Files location</b><br><br> \
|
||||
The directory containing the Data Files was not found.<br><br> \
|
||||
The directory containing the data files was not found.<br><br> \
|
||||
Press \"Browse...\" to specify the location manually.<br>"));
|
||||
|
||||
QAbstractButton *dirSelectButton =
|
||||
|
@ -1057,16 +1057,8 @@ void DataFilesPage::writeConfig(QString profile)
|
|||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
file.setFileName(config);
|
||||
// Open the OpenMW 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
|
||||
|
|
|
@ -45,9 +45,28 @@ MainDialog::MainDialog()
|
|||
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setMinimumSize(QSize(575, 575));
|
||||
|
||||
// Install the stylesheet font
|
||||
QFile file;
|
||||
QFontDatabase fontDatabase;
|
||||
|
||||
const QStringList fonts = fontDatabase.families();
|
||||
|
||||
// Check if the font is installed
|
||||
if (!fonts.contains("EB Garamond")) {
|
||||
|
||||
QString font = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
|
||||
file.setFileName(font);
|
||||
|
||||
if (!file.exists()) {
|
||||
font = QString::fromStdString((mCfgMgr.getLocalPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
|
||||
}
|
||||
|
||||
fontDatabase.addApplicationFont(font);
|
||||
}
|
||||
|
||||
// Load the stylesheet
|
||||
QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string());
|
||||
QFile file(config);
|
||||
file.setFileName(config);
|
||||
|
||||
if (!file.exists()) {
|
||||
file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string()));
|
||||
|
@ -175,6 +194,7 @@ void MainDialog::play()
|
|||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
QString game = dir.absoluteFilePath("openmw");
|
||||
QFile file(game);
|
||||
game = "\"" + game + "\"";
|
||||
#else
|
||||
QString game = "./openmw";
|
||||
QFile file(game);
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 50 KiB |
|
@ -14,11 +14,8 @@ set(GAME_HEADER
|
|||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||
|
||||
add_openmw_dir (mwrender
|
||||
|
||||
renderingmanager debugging sky player renderinginterface water
|
||||
|
||||
animation npcanimation creatureanimation actors objects renderinginterface
|
||||
|
||||
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
|
||||
renderinginterface localmap water
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
@ -42,7 +39,7 @@ add_openmw_dir (mwscript
|
|||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
soundmanager
|
||||
soundmanager openal_output mpgsnd_decoder ffmpeg_decoder
|
||||
)
|
||||
|
||||
add_openmw_dir (mwworld
|
||||
|
@ -92,7 +89,7 @@ target_link_libraries(openmw
|
|||
${SOUND_INPUT_LIBRARY}
|
||||
${BULLET_LIBRARIES}
|
||||
${MYGUI_LIBRARIES}
|
||||
MyGUI.OgrePlatform #TODO MyGUI ogre platform is not added by the find script
|
||||
${MYGUI_PLATFORM_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
||||
|
|
|
@ -117,11 +117,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
|
|||
|
||||
// sound
|
||||
if (mUseSound)
|
||||
{
|
||||
mEnvironment.mSoundManager->playPlaylist();
|
||||
|
||||
mEnvironment.mSoundManager->update (evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
// update GUI
|
||||
Ogre::RenderWindow* window = mOgre->getWindow();
|
||||
|
@ -341,10 +337,7 @@ void OMW::Engine::go()
|
|||
mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"));
|
||||
|
||||
// Create sound system
|
||||
mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(),
|
||||
mOgre->getCamera(),
|
||||
mDataDirs,
|
||||
mUseSound, mFSStrict, mEnvironment);
|
||||
mEnvironment.mSoundManager = new MWSound::SoundManager(mUseSound, mEnvironment);
|
||||
|
||||
// Create script system
|
||||
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
|
||||
|
|
|
@ -15,6 +15,22 @@ using namespace MWGui;
|
|||
|
||||
HUD::HUD(int width, int height, int fpsLevel)
|
||||
: Layout("openmw_hud_layout.xml")
|
||||
, health(NULL)
|
||||
, magicka(NULL)
|
||||
, stamina(NULL)
|
||||
, weapImage(NULL)
|
||||
, spellImage(NULL)
|
||||
, weapStatus(NULL)
|
||||
, spellStatus(NULL)
|
||||
, effectBox(NULL)
|
||||
, effect1(NULL)
|
||||
, minimap(NULL)
|
||||
, compass(NULL)
|
||||
, crosshair(NULL)
|
||||
, fpsbox(NULL)
|
||||
, fpscounter(NULL)
|
||||
, trianglecounter(NULL)
|
||||
, batchcounter(NULL)
|
||||
{
|
||||
setCoord(0,0, width, height);
|
||||
|
||||
|
@ -61,6 +77,8 @@ HUD::HUD(int width, int height, int fpsLevel)
|
|||
setSpellIcon("icons\\s\\b_tx_s_rstor_health.dds");
|
||||
setSpellStatus(65, 100);
|
||||
setEffect("icons\\s\\tx_s_chameleon.dds");
|
||||
|
||||
LocalMapBase::init(minimap, this);
|
||||
}
|
||||
|
||||
void HUD::setFPS(float fps)
|
||||
|
@ -142,3 +160,162 @@ void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat<int>& v
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HUD::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
MyGUI::ISubWidget* main = compass->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
}
|
||||
|
||||
void HUD::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
MyGUI::IntSize size = minimap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = minimap->getCoord();
|
||||
MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
|
||||
|
||||
minimap->setViewOffset(pos);
|
||||
compass->setPosition(MyGUI::IntPoint(x*512-16, y*512-16));
|
||||
}
|
||||
|
||||
MapWindow::MapWindow()
|
||||
: Layout("openmw_map_window_layout.xml"), mGlobal(false)
|
||||
{
|
||||
setCoord(500,0,320,300);
|
||||
setText("WorldButton", "World");
|
||||
setImage("Compass", "textures\\compass.dds");
|
||||
|
||||
// Obviously you should override this later on
|
||||
setCellName("No Cell Loaded");
|
||||
|
||||
getWidget(mLocalMap, "LocalMap");
|
||||
getWidget(mGlobalMap, "GlobalMap");
|
||||
getWidget(mPlayerArrow, "Compass");
|
||||
|
||||
getWidget(mButton, "WorldButton");
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
|
||||
MyGUI::Button* eventbox;
|
||||
getWidget(eventbox, "EventBox");
|
||||
eventbox->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
eventbox->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
|
||||
LocalMapBase::init(mLocalMap, this);
|
||||
}
|
||||
|
||||
void MapWindow::setVisible(bool b)
|
||||
{
|
||||
mMainWidget->setVisible(b);
|
||||
if (b)
|
||||
mVisible = true;
|
||||
else
|
||||
mVisible = false;
|
||||
}
|
||||
|
||||
void MapWindow::setCellName(const std::string& cellName)
|
||||
{
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(cellName);
|
||||
adjustWindowCaption();
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
if (mGlobal || mVisible) return;
|
||||
MyGUI::IntSize size = mLocalMap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = mLocalMap->getCoord();
|
||||
MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
|
||||
mLocalMap->setViewOffset(pos);
|
||||
|
||||
mPlayerArrow->setPosition(MyGUI::IntPoint(x*512-16, y*512-16));
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
if (!mVisible) return;
|
||||
MyGUI::ISubWidget* main = mPlayerArrow->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
}
|
||||
|
||||
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
if (!mGlobal)
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
|
||||
if (!mGlobal)
|
||||
{
|
||||
MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos;
|
||||
mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff );
|
||||
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
}
|
||||
|
||||
void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mGlobal = !mGlobal;
|
||||
mGlobalMap->setVisible(mGlobal);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
||||
mButton->setCaption( mGlobal ? "Local" : "World" );
|
||||
}
|
||||
|
||||
void LocalMapBase::init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout)
|
||||
{
|
||||
mLocalMap = widget;
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
void LocalMapBase::setCellPrefix(const std::string& prefix)
|
||||
{
|
||||
mPrefix = prefix;
|
||||
mChanged = true;
|
||||
}
|
||||
|
||||
void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
|
||||
{
|
||||
if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
{
|
||||
for (int my=0; my<3; ++my)
|
||||
{
|
||||
std::string name = "Map_" + boost::lexical_cast<std::string>(mx) + "_"
|
||||
+ boost::lexical_cast<std::string>(my);
|
||||
|
||||
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(x + (mx-1)) + "_"
|
||||
+ boost::lexical_cast<std::string>(y + (interior ? (my-1) : -1*(my-1)));
|
||||
|
||||
MyGUI::ImageBox* box;
|
||||
mLayout->getWidget(box, name);
|
||||
MyGUI::ImageBox* fog;
|
||||
mLayout->getWidget(fog, name+"_fog");
|
||||
|
||||
if (MyGUI::RenderManager::getInstance().getTexture(image) != 0)
|
||||
box->setImageTexture(image);
|
||||
else
|
||||
box->setImageTexture("black.png");
|
||||
|
||||
if (MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0)
|
||||
fog->setImageTexture(image+"_fog");
|
||||
else
|
||||
fog->setImageTexture("black.png");
|
||||
}
|
||||
}
|
||||
mInterior = interior;
|
||||
mCurX = x;
|
||||
mCurY = y;
|
||||
mChanged = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "../mwmechanics/stat.hpp"
|
||||
#include "window_base.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
/*
|
||||
This file contains classes corresponding to window layouts
|
||||
defined in resources/mygui/ *.xml.
|
||||
|
@ -29,7 +31,25 @@
|
|||
|
||||
namespace MWGui
|
||||
{
|
||||
class HUD : public OEngine::GUI::Layout
|
||||
class LocalMapBase
|
||||
{
|
||||
public:
|
||||
void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout);
|
||||
|
||||
void setCellPrefix(const std::string& prefix);
|
||||
void setActiveCell(const int x, const int y, bool interior=false);
|
||||
|
||||
protected:
|
||||
int mCurX, mCurY;
|
||||
bool mInterior;
|
||||
MyGUI::ScrollView* mLocalMap;
|
||||
std::string mPrefix;
|
||||
bool mChanged;
|
||||
|
||||
OEngine::GUI::Layout* mLayout;
|
||||
};
|
||||
|
||||
class HUD : public OEngine::GUI::Layout, public LocalMapBase
|
||||
{
|
||||
public:
|
||||
HUD(int width, int height, int fpsLevel);
|
||||
|
@ -43,13 +63,15 @@ namespace MWGui
|
|||
void setFPS(float fps);
|
||||
void setTriangleCount(size_t count);
|
||||
void setBatchCount(size_t count);
|
||||
void setPlayerDir(const float x, const float y);
|
||||
void setPlayerPos(const float x, const float y);
|
||||
|
||||
MyGUI::ProgressPtr health, magicka, stamina;
|
||||
MyGUI::ImageBox *weapImage, *spellImage;
|
||||
MyGUI::ProgressPtr weapStatus, spellStatus;
|
||||
MyGUI::WidgetPtr effectBox;
|
||||
MyGUI::ImageBox* effect1;
|
||||
MyGUI::ImageBox* minimap;
|
||||
MyGUI::ScrollView* minimap;
|
||||
MyGUI::ImageBox* compass;
|
||||
MyGUI::ImageBox* crosshair;
|
||||
|
||||
|
@ -59,24 +81,27 @@ namespace MWGui
|
|||
MyGUI::TextBox* batchcounter;
|
||||
};
|
||||
|
||||
class MapWindow : public OEngine::GUI::Layout
|
||||
class MapWindow : public OEngine::GUI::Layout, public LocalMapBase
|
||||
{
|
||||
public:
|
||||
MapWindow()
|
||||
: Layout("openmw_map_window_layout.xml")
|
||||
{
|
||||
setCoord(500,0,320,300);
|
||||
setText("WorldButton", "World");
|
||||
setImage("Compass", "compass.dds");
|
||||
MapWindow();
|
||||
|
||||
// Obviously you should override this later on
|
||||
setCellName("No Cell Loaded");
|
||||
}
|
||||
void setVisible(bool b);
|
||||
void setPlayerPos(const float x, const float y);
|
||||
void setPlayerDir(const float x, const float y);
|
||||
void setCellName(const std::string& cellName);
|
||||
|
||||
private:
|
||||
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onWorldButtonClicked(MyGUI::Widget* _sender);
|
||||
|
||||
void setCellName(const std::string& cellName)
|
||||
{
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(cellName);
|
||||
}
|
||||
MyGUI::ScrollView* mGlobalMap;
|
||||
MyGUI::ImageBox* mPlayerArrow;
|
||||
MyGUI::Button* mButton;
|
||||
MyGUI::IntPoint mLastDragPos;
|
||||
bool mVisible;
|
||||
bool mGlobal;
|
||||
};
|
||||
|
||||
class MainMenu : public OEngine::GUI::Layout
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "window_base.hpp"
|
||||
#include "window_manager.hpp"
|
||||
|
||||
#undef MessageBox
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
|
|
@ -13,9 +13,22 @@ const int StatsWindow::lineHeight = 18;
|
|||
|
||||
StatsWindow::StatsWindow (WindowManager& parWindowManager)
|
||||
: WindowBase("openmw_stats_window_layout.xml", parWindowManager)
|
||||
, skillAreaWidget(NULL)
|
||||
, skillClientWidget(NULL)
|
||||
, skillScrollerWidget(NULL)
|
||||
, lastPos(0)
|
||||
, clientHeight(0)
|
||||
, majorSkills()
|
||||
, minorSkills()
|
||||
, miscSkills()
|
||||
, skillValues()
|
||||
, skillWidgetMap()
|
||||
, factionWidgetMap()
|
||||
, factions()
|
||||
, birthSignId()
|
||||
, reputation(0)
|
||||
, bounty(0)
|
||||
, skillWidgets()
|
||||
{
|
||||
setCoord(0,0,498, 342);
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
|
||||
#undef MYGUI_EXPORT
|
||||
#define MYGUI_EXPORT
|
||||
|
||||
/*
|
||||
This file contains various custom widgets used in OpenMW.
|
||||
*/
|
||||
|
|
|
@ -22,15 +22,40 @@ using namespace MWGui;
|
|||
|
||||
WindowManager::WindowManager(MWWorld::Environment& environment,
|
||||
const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string logpath)
|
||||
: environment(environment)
|
||||
: mGuiManager(NULL)
|
||||
, environment(environment)
|
||||
, hud(NULL)
|
||||
, map(NULL)
|
||||
, menu(NULL)
|
||||
, stats(NULL)
|
||||
, mMessageBoxManager(NULL)
|
||||
, console(NULL)
|
||||
, mJournal(NULL)
|
||||
, dialogueWindow(nullptr)
|
||||
, mCharGen(NULL)
|
||||
, playerClass()
|
||||
, playerName()
|
||||
, playerRaceId()
|
||||
, playerBirthSignId()
|
||||
, playerAttributes()
|
||||
, playerMajorSkills()
|
||||
, playerMinorSkills()
|
||||
, playerSkillValues()
|
||||
, playerHealth()
|
||||
, playerMagicka()
|
||||
, playerFatigue()
|
||||
, gui(NULL)
|
||||
, mode(GM_Game)
|
||||
, nextMode(GM_Game)
|
||||
, needModeChange(false)
|
||||
, garbageDialogs()
|
||||
, shown(GW_ALL)
|
||||
, allowed(newGame ? GW_None : GW_ALL)
|
||||
, showFPSLevel(fpsLevel)
|
||||
, mFPS(0.0f)
|
||||
, mTriangleCount(0)
|
||||
, mBatchCount(0)
|
||||
{
|
||||
showFPSLevel = fpsLevel;
|
||||
|
||||
// Set up the GUI system
|
||||
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath);
|
||||
|
@ -400,3 +425,47 @@ const ESMS::ESMStore& WindowManager::getStore() const
|
|||
{
|
||||
return environment.mWorld->getStore();
|
||||
}
|
||||
|
||||
void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell)
|
||||
{
|
||||
if (!(cell->cell->data.flags & ESM::Cell::Interior))
|
||||
{
|
||||
std::string name;
|
||||
if (cell->cell->name != "")
|
||||
name = cell->cell->name;
|
||||
else
|
||||
name = cell->cell->region;
|
||||
|
||||
map->setCellName( name );
|
||||
|
||||
map->setCellPrefix("Cell");
|
||||
hud->setCellPrefix("Cell");
|
||||
map->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY );
|
||||
hud->setActiveCell( cell->cell->data.gridX, cell->cell->data.gridY );
|
||||
}
|
||||
else
|
||||
{
|
||||
map->setCellName( cell->cell->name );
|
||||
map->setCellPrefix( cell->cell->name );
|
||||
hud->setCellPrefix( cell->cell->name );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WindowManager::setInteriorMapTexture(const int x, const int y)
|
||||
{
|
||||
map->setActiveCell(x,y, true);
|
||||
hud->setActiveCell(x,y, true);
|
||||
}
|
||||
|
||||
void WindowManager::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
map->setPlayerPos(x,y);
|
||||
hud->setPlayerPos(x,y);
|
||||
}
|
||||
|
||||
void WindowManager::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
map->setPlayerDir(x,y);
|
||||
hud->setPlayerDir(x,y);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <openengine/ogre/renderer.hpp>
|
||||
#include <openengine/gui/manager.hpp>
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "mode.hpp"
|
||||
|
||||
namespace MyGUI
|
||||
|
@ -152,6 +153,12 @@ namespace MWGui
|
|||
void setBounty (int bounty); ///< set the current bounty value
|
||||
void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty
|
||||
|
||||
void changeCell(MWWorld::Ptr::CellStore* cell); ///< change the active cell
|
||||
void setPlayerPos(const float x, const float y); ///< set player position in map space
|
||||
void setPlayerDir(const float x, const float y); ///< set player view direction in map space
|
||||
|
||||
void setInteriorMapTexture(const int x, const int y);
|
||||
///< set the index of the map texture that should be used (for interiors)
|
||||
|
||||
template<typename T>
|
||||
void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr.
|
||||
|
|
|
@ -8,6 +8,15 @@ using namespace Ogre;
|
|||
using namespace MWRender;
|
||||
using namespace NifOgre;
|
||||
|
||||
Actors::~Actors(){
|
||||
|
||||
std::map<MWWorld::Ptr, Animation*>::iterator it = mAllActors.begin();
|
||||
for (; it != mAllActors.end(); ++it) {
|
||||
delete it->second;
|
||||
it->second = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Actors::setMwRoot(Ogre::SceneNode* root){
|
||||
mMwRoot = root;
|
||||
}
|
||||
|
@ -61,6 +70,7 @@ void Actors::insertCreature (const MWWorld::Ptr& ptr){
|
|||
insertBegin(ptr, true, true);
|
||||
CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr, mEnvironment, mRend);
|
||||
//mAllActors.insert(std::pair<MWWorld::Ptr, Animation*>(ptr,anim));
|
||||
delete mAllActors[ptr];
|
||||
mAllActors[ptr] = anim;
|
||||
//mAllActors.push_back(&anim);*/
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace MWRender{
|
|||
|
||||
public:
|
||||
Actors(OEngine::Render::OgreRenderer& _rend, MWWorld::Environment& _env): mRend(_rend), mEnvironment(_env){}
|
||||
~Actors(){}
|
||||
~Actors();
|
||||
void setMwRoot(Ogre::SceneNode* root);
|
||||
void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_);
|
||||
void insertCreature (const MWWorld::Ptr& ptr);
|
||||
|
|
|
@ -4,7 +4,30 @@
|
|||
namespace MWRender{
|
||||
std::map<std::string, int> Animation::mUniqueIDs;
|
||||
|
||||
Animation::~Animation(){
|
||||
Animation::Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend)
|
||||
: insert(NULL)
|
||||
, mRend(_rend)
|
||||
, mEnvironment(_env)
|
||||
, vecRotPos()
|
||||
, shapeparts()
|
||||
, time(0.0f)
|
||||
, startTime(0.0f)
|
||||
, stopTime(0.0f)
|
||||
, animate(0)
|
||||
, rindexI()
|
||||
, tindexI()
|
||||
, shapeNumber(0)
|
||||
, shapeIndexI()
|
||||
, shapes(NULL)
|
||||
, entityparts()
|
||||
, transformations(NULL)
|
||||
, textmappings(NULL)
|
||||
, base(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Animation::~Animation()
|
||||
{
|
||||
}
|
||||
|
||||
std::string Animation::getUniqueID(std::string mesh){
|
||||
|
|
|
@ -60,14 +60,14 @@ class Animation{
|
|||
std::string getUniqueID(std::string mesh);
|
||||
|
||||
public:
|
||||
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();
|
||||
|
||||
|
||||
virtual ~Animation();
|
||||
Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend);
|
||||
virtual void runAnimation(float timepassed) = 0;
|
||||
void startScript(std::string groupname, int mode, int loops);
|
||||
void stopScript();
|
||||
|
||||
|
||||
virtual ~Animation();
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
305
apps/openmw/mwrender/localmap.cpp
Normal file
305
apps/openmw/mwrender/localmap.cpp
Normal file
|
@ -0,0 +1,305 @@
|
|||
#include "localmap.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwgui/window_manager.hpp"
|
||||
|
||||
#include <OgreOverlayManager.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
||||
LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWWorld::Environment* env)
|
||||
{
|
||||
mRendering = rend;
|
||||
mEnvironment = env;
|
||||
|
||||
mCellCamera = mRendering->getScene()->createCamera("CellCamera");
|
||||
mCellCamera->setProjectionType(PT_ORTHOGRAPHIC);
|
||||
// look down -y
|
||||
const float sqrt0pt5 = 0.707106781;
|
||||
mCellCamera->setOrientation(Quaternion(sqrt0pt5, -sqrt0pt5, 0, 0));
|
||||
}
|
||||
|
||||
LocalMap::~LocalMap()
|
||||
{
|
||||
deleteBuffers();
|
||||
}
|
||||
|
||||
void LocalMap::deleteBuffers()
|
||||
{
|
||||
mBuffers.clear();
|
||||
}
|
||||
|
||||
void LocalMap::saveTexture(const std::string& texname, const std::string& filename)
|
||||
{
|
||||
TexturePtr tex = TextureManager::getSingleton().getByName(texname);
|
||||
if (tex.isNull()) return;
|
||||
HardwarePixelBufferSharedPtr readbuffer = tex->getBuffer();
|
||||
readbuffer->lock(HardwareBuffer::HBL_NORMAL );
|
||||
const PixelBox &readrefpb = readbuffer->getCurrentLock();
|
||||
uchar *readrefdata = static_cast<uchar*>(readrefpb.data);
|
||||
|
||||
Image img;
|
||||
img = img.loadDynamicImage (readrefdata, tex->getWidth(),
|
||||
tex->getHeight(), tex->getFormat());
|
||||
img.save("./" + filename);
|
||||
|
||||
readbuffer->unlock();
|
||||
}
|
||||
|
||||
std::string LocalMap::coordStr(const int x, const int y)
|
||||
{
|
||||
return StringConverter::toString(x) + "_" + StringConverter::toString(y);
|
||||
}
|
||||
|
||||
void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell)
|
||||
{
|
||||
if (!mInterior)
|
||||
{
|
||||
/*saveTexture("Cell_"+coordStr(mCellX, mCellY)+"_fog",
|
||||
"Cell_"+coordStr(mCellX, mCellY)+"_fog.png");*/
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z);
|
||||
/// \todo why is this workaround needed?
|
||||
min *= 1.3;
|
||||
max *= 1.3;
|
||||
Vector2 length = max-min;
|
||||
|
||||
// divide into segments
|
||||
const int segsX = std::ceil( length.x / sSize );
|
||||
const int segsY = std::ceil( length.y / sSize );
|
||||
|
||||
for (int x=0; x<segsX; ++x)
|
||||
{
|
||||
for (int y=0; y<segsY; ++y)
|
||||
{
|
||||
/*saveTexture(
|
||||
mInteriorName + "_" + coordStr(x,y) + "_fog",
|
||||
mInteriorName + "_" + coordStr(x,y) + "_fog.png");*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell)
|
||||
{
|
||||
mInterior = false;
|
||||
|
||||
std::string name = "Cell_"+coordStr(cell->cell->data.gridX, cell->cell->data.gridY);
|
||||
|
||||
int x = cell->cell->data.gridX;
|
||||
int y = cell->cell->data.gridY;
|
||||
|
||||
render((x+0.5)*sSize, (-y-0.5)*sSize, -10000, 10000, sSize, sSize, name);
|
||||
}
|
||||
|
||||
void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell,
|
||||
AxisAlignedBox bounds)
|
||||
{
|
||||
mInterior = true;
|
||||
mBounds = bounds;
|
||||
|
||||
Vector2 z(bounds.getMaximum().y, bounds.getMinimum().y);
|
||||
Vector2 min(bounds.getMinimum().x, bounds.getMinimum().z);
|
||||
Vector2 max(bounds.getMaximum().x, bounds.getMaximum().z);
|
||||
|
||||
/// \todo why is this workaround needed?
|
||||
min *= 1.3;
|
||||
max *= 1.3;
|
||||
|
||||
Vector2 length = max-min;
|
||||
Vector2 center(bounds.getCenter().x, bounds.getCenter().z);
|
||||
|
||||
// divide into segments
|
||||
const int segsX = std::ceil( length.x / sSize );
|
||||
const int segsY = std::ceil( length.y / sSize );
|
||||
|
||||
mInteriorName = cell->cell->name;
|
||||
|
||||
for (int x=0; x<segsX; ++x)
|
||||
{
|
||||
for (int y=0; y<segsY; ++y)
|
||||
{
|
||||
Vector2 start = min + Vector2(sSize*x,sSize*y);
|
||||
Vector2 newcenter = start + 4096;
|
||||
|
||||
render(newcenter.x, newcenter.y, z.y, z.x, sSize, sSize,
|
||||
cell->cell->name + "_" + coordStr(x,y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalMap::render(const float x, const float y,
|
||||
const float zlow, const float zhigh,
|
||||
const float xw, const float yw, const std::string& texture)
|
||||
{
|
||||
// disable fog
|
||||
// changing FOG_MODE is not a solution when using shaders, thus we have to push linear start/end
|
||||
const float fStart = mRendering->getScene()->getFogStart();
|
||||
const float fEnd = mRendering->getScene()->getFogEnd();
|
||||
const ColourValue& clr = mRendering->getScene()->getFogColour();
|
||||
mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, 1000000, 10000000);
|
||||
|
||||
// make everything visible
|
||||
mRendering->getScene()->setAmbientLight(ColourValue(1,1,1));
|
||||
|
||||
mCellCamera->setPosition(Vector3(x, zhigh+100000, y));
|
||||
//mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 );
|
||||
mCellCamera->setFarClipDistance(0); // infinite
|
||||
|
||||
mCellCamera->setOrthoWindow(xw, yw);
|
||||
|
||||
TexturePtr tex;
|
||||
// try loading from memory
|
||||
tex = TextureManager::getSingleton().getByName(texture);
|
||||
if (tex.isNull())
|
||||
{
|
||||
// try loading from disk
|
||||
//if (boost::filesystem::exists(texture+".jpg"))
|
||||
//{
|
||||
/// \todo
|
||||
//}
|
||||
//else
|
||||
{
|
||||
// render
|
||||
tex = TextureManager::getSingleton().createManual(
|
||||
texture,
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
TEX_TYPE_2D,
|
||||
xw*sMapResolution/sSize, yw*sMapResolution/sSize,
|
||||
0,
|
||||
PF_R8G8B8,
|
||||
TU_RENDERTARGET);
|
||||
|
||||
RenderTarget* rtt = tex->getBuffer()->getRenderTarget();
|
||||
rtt->setAutoUpdated(false);
|
||||
Viewport* vp = rtt->addViewport(mCellCamera);
|
||||
vp->setOverlaysEnabled(false);
|
||||
vp->setShadowsEnabled(false);
|
||||
vp->setBackgroundColour(ColourValue(0, 0, 0));
|
||||
//vp->setVisibilityMask( ... );
|
||||
|
||||
rtt->update();
|
||||
|
||||
// create "fog of war" texture
|
||||
TexturePtr tex2 = TextureManager::getSingleton().createManual(
|
||||
texture + "_fog",
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
TEX_TYPE_2D,
|
||||
xw*sFogOfWarResolution/sSize, yw*sFogOfWarResolution/sSize,
|
||||
0,
|
||||
PF_A8R8G8B8,
|
||||
TU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
|
||||
|
||||
// create a buffer to use for dynamic operations
|
||||
std::vector<uint32> buffer;
|
||||
buffer.resize(sFogOfWarResolution*sFogOfWarResolution);
|
||||
|
||||
// initialize to (0, 0, 0, 1)
|
||||
for (int p=0; p<sFogOfWarResolution*sFogOfWarResolution; ++p)
|
||||
{
|
||||
buffer[p] = (255 << 24);
|
||||
}
|
||||
|
||||
memcpy(tex2->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4);
|
||||
tex2->getBuffer()->unlock();
|
||||
|
||||
mBuffers[texture] = buffer;
|
||||
|
||||
// save to cache for next time
|
||||
//rtt->writeContentsToFile("./" + texture + ".jpg");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// re-enable fog
|
||||
mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, fStart, fEnd);
|
||||
}
|
||||
|
||||
void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction)
|
||||
{
|
||||
if (sFogOfWarSkip != 0)
|
||||
{
|
||||
static int count=0;
|
||||
if (++count % sFogOfWarSkip != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// retrieve the x,y grid coordinates the player is in
|
||||
int x,y;
|
||||
Vector2 pos(position.x, position.z);
|
||||
if (!mInterior)
|
||||
{
|
||||
x = std::ceil(pos.x / sSize)-1;
|
||||
y = std::ceil(-pos.y / sSize)-1;
|
||||
mCellX = x;
|
||||
mCellY = y;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
min *= 1.3;
|
||||
|
||||
x = std::ceil((pos.x - min.x)/sSize)-1;
|
||||
y = std::ceil((pos.y - min.y)/sSize)-1;
|
||||
|
||||
mEnvironment->mWindowManager->setInteriorMapTexture(x,y);
|
||||
}
|
||||
|
||||
// convert from world coordinates to texture UV coordinates
|
||||
float u,v;
|
||||
std::string texName;
|
||||
if (!mInterior)
|
||||
{
|
||||
u = std::abs((pos.x - (sSize*x))/sSize);
|
||||
v = 1-std::abs((pos.y + (sSize*y))/sSize);
|
||||
texName = "Cell_"+coordStr(x,y);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
min *= 1.3;
|
||||
|
||||
u = (pos.x - min.x - sSize*x)/sSize;
|
||||
v = (pos.y - min.y - sSize*y)/sSize;
|
||||
|
||||
texName = mInteriorName + "_" + coordStr(x,y);
|
||||
}
|
||||
mEnvironment->mWindowManager->setPlayerPos(u, v);
|
||||
mEnvironment->mWindowManager->setPlayerDir(direction.x, -direction.z);
|
||||
|
||||
// explore radius (squared)
|
||||
const float sqrExploreRadius = 0.01 * sFogOfWarResolution*sFogOfWarResolution;
|
||||
|
||||
// get the appropriate fog of war texture
|
||||
TexturePtr tex = TextureManager::getSingleton().getByName(texName+"_fog");
|
||||
if (!tex.isNull())
|
||||
{
|
||||
// get its buffer
|
||||
if (mBuffers.find(texName) == mBuffers.end()) return;
|
||||
int i=0;
|
||||
for (int texV = 0; texV<sFogOfWarResolution; ++texV)
|
||||
{
|
||||
for (int texU = 0; texU<sFogOfWarResolution; ++texU)
|
||||
{
|
||||
float sqrDist = Math::Sqr(texU - u*sFogOfWarResolution) + Math::Sqr(texV - v*sFogOfWarResolution);
|
||||
uint32 clr = mBuffers[texName][i];
|
||||
uint8 alpha = (clr >> 24);
|
||||
alpha = std::min( alpha, (uint8) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) );
|
||||
mBuffers[texName][i] = (uint32) (alpha << 24);
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// copy to the texture
|
||||
memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &mBuffers[texName][0], sFogOfWarResolution*sFogOfWarResolution*4);
|
||||
tex->getBuffer()->unlock();
|
||||
}
|
||||
}
|
100
apps/openmw/mwrender/localmap.hpp
Normal file
100
apps/openmw/mwrender/localmap.hpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
#ifndef _GAME_RENDER_LOCALMAP_H
|
||||
#define _GAME_RENDER_LOCALMAP_H
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include <openengine/ogre/renderer.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Environment;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
///
|
||||
/// \brief Local map rendering
|
||||
///
|
||||
class LocalMap
|
||||
{
|
||||
public:
|
||||
LocalMap(OEngine::Render::OgreRenderer*, MWWorld::Environment* env);
|
||||
~LocalMap();
|
||||
|
||||
/**
|
||||
* Request the local map for an exterior cell.
|
||||
* @remarks It will either be loaded from a disk cache,
|
||||
* or rendered if it is not already cached.
|
||||
* @param exterior cell
|
||||
*/
|
||||
void requestMap (MWWorld::Ptr::CellStore* cell);
|
||||
|
||||
/**
|
||||
* Request the local map for an interior cell.
|
||||
* @remarks It will either be loaded from a disk cache,
|
||||
* or rendered if it is not already cached.
|
||||
* @param interior cell
|
||||
* @param bounding box of the cell
|
||||
*/
|
||||
void requestMap (MWWorld::Ptr::CellStore* cell,
|
||||
Ogre::AxisAlignedBox bounds);
|
||||
|
||||
/**
|
||||
* Set the position & direction of the player.
|
||||
* @remarks This is used to draw a "fog of war" effect
|
||||
* to hide areas on the map the player has not discovered yet.
|
||||
* @param position (OGRE coordinates)
|
||||
* @param view direction (OGRE coordinates)
|
||||
*/
|
||||
void updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction);
|
||||
|
||||
/**
|
||||
* Save the fog of war for the current cell to disk.
|
||||
* @remarks This should be called before loading a
|
||||
* new cell, as well as when the game is quit.
|
||||
* @param current cell
|
||||
*/
|
||||
void saveFogOfWar(MWWorld::Ptr::CellStore* cell);
|
||||
|
||||
private:
|
||||
OEngine::Render::OgreRenderer* mRendering;
|
||||
MWWorld::Environment* mEnvironment;
|
||||
|
||||
// 1024*1024 pixels for a cell
|
||||
static const int sMapResolution = 1024;
|
||||
|
||||
// the dynamic texture is a bottleneck, so don't set this too high
|
||||
static const int sFogOfWarResolution = 32;
|
||||
|
||||
// frames to skip before rendering fog of war
|
||||
static const int sFogOfWarSkip = 2;
|
||||
|
||||
// size of a map segment (for exteriors, 1 cell)
|
||||
static const int sSize = 8192;
|
||||
|
||||
Ogre::Camera* mCellCamera;
|
||||
|
||||
void render(const float x, const float y,
|
||||
const float zlow, const float zhigh,
|
||||
const float xw, const float yw,
|
||||
const std::string& texture);
|
||||
|
||||
void saveTexture(const std::string& texname, const std::string& filename);
|
||||
|
||||
std::string coordStr(const int x, const int y);
|
||||
|
||||
// a buffer for the "fog of war" texture of the current cell.
|
||||
// interior cells could be divided into multiple textures,
|
||||
// so we store in a map.
|
||||
std::map <std::string, std::vector<Ogre::uint32> > mBuffers;
|
||||
|
||||
void deleteBuffers();
|
||||
|
||||
bool mInterior;
|
||||
int mCellX, mCellY;
|
||||
Ogre::AxisAlignedBox mBounds;
|
||||
std::string mInteriorName;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -109,6 +109,9 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
|
|||
// If it is set too low:
|
||||
// - there will be too many batches.
|
||||
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
|
||||
|
||||
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
|
||||
mBounds[ptr.getCell()].merge(ent->getBoundingBox());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -116,6 +119,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
|
|||
}
|
||||
|
||||
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
|
||||
mBounds[ptr.getCell()].merge(insert->_getDerivedPosition());
|
||||
|
||||
mRenderer.getScene()->destroyEntity(ent);
|
||||
}
|
||||
|
@ -202,6 +206,9 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store)
|
|||
mRenderer.getScene()->destroyStaticGeometry (sg);
|
||||
sg = 0;
|
||||
}
|
||||
|
||||
if(mBounds.find(store) != mBounds.end())
|
||||
mBounds.erase(store);
|
||||
}
|
||||
|
||||
void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
|
||||
|
@ -212,3 +219,8 @@ void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
|
|||
sg->build();
|
||||
}
|
||||
}
|
||||
|
||||
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
|
||||
{
|
||||
return mBounds[cell];
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ class Objects{
|
|||
OEngine::Render::OgreRenderer &mRenderer;
|
||||
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
|
||||
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometry;
|
||||
std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds;
|
||||
Ogre::SceneNode* mMwRoot;
|
||||
bool mIsStatic;
|
||||
static int uniqueID;
|
||||
|
@ -42,6 +43,9 @@ public:
|
|||
void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh);
|
||||
void insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius);
|
||||
|
||||
Ogre::AxisAlignedBox getDimensions(MWWorld::Ptr::CellStore*);
|
||||
///< get a bounding box that encloses all objects in the specified cell
|
||||
|
||||
bool deleteObject (const MWWorld::Ptr& ptr);
|
||||
///< \return found?
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
|
|||
|
||||
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
|
||||
mSun = 0;
|
||||
|
||||
mLocalMap = new MWRender::LocalMap(&mRendering, &environment);
|
||||
}
|
||||
|
||||
RenderingManager::~RenderingManager ()
|
||||
|
@ -66,6 +68,7 @@ RenderingManager::~RenderingManager ()
|
|||
//TODO: destroy mSun?
|
||||
delete mPlayer;
|
||||
delete mSkyManager;
|
||||
delete mLocalMap;
|
||||
}
|
||||
|
||||
MWRender::SkyManager* RenderingManager::getSkyManager()
|
||||
|
@ -149,6 +152,8 @@ void RenderingManager::update (float duration){
|
|||
mSkyManager->update(duration);
|
||||
|
||||
mRendering.update(duration);
|
||||
|
||||
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );
|
||||
}
|
||||
void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){
|
||||
if(store->cell->data.flags & store->cell->HasWater){
|
||||
|
@ -357,4 +362,17 @@ void RenderingManager::setGlare(bool glare)
|
|||
mSkyManager->setGlare(glare);
|
||||
}
|
||||
|
||||
void RenderingManager::requestMap(MWWorld::Ptr::CellStore* cell)
|
||||
{
|
||||
if (!(cell->cell->data.flags & ESM::Cell::Interior))
|
||||
mLocalMap->requestMap(cell);
|
||||
else
|
||||
mLocalMap->requestMap(cell, mObjects.getDimensions(cell));
|
||||
}
|
||||
|
||||
void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell)
|
||||
{
|
||||
mLocalMap->saveFogOfWar(cell);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "actors.hpp"
|
||||
#include "player.hpp"
|
||||
#include "water.hpp"
|
||||
#include "localmap.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
|
@ -79,9 +80,11 @@ class RenderingManager: private RenderingInterface {
|
|||
void cellAdded (MWWorld::Ptr::CellStore *store);
|
||||
void waterAdded(MWWorld::Ptr::CellStore *store);
|
||||
|
||||
|
||||
void removeWater();
|
||||
|
||||
void preCellChange (MWWorld::Ptr::CellStore* store);
|
||||
///< this event is fired immediately before changing cell
|
||||
|
||||
void addObject (const MWWorld::Ptr& ptr);
|
||||
void removeObject (const MWWorld::Ptr& ptr);
|
||||
|
||||
|
@ -110,6 +113,9 @@ class RenderingManager: private RenderingInterface {
|
|||
int skyGetSecundaPhase() const;
|
||||
void skySetMoonColour (bool red);
|
||||
void configureAmbient(ESMS::CellStore<MWWorld::RefData> &mCell);
|
||||
|
||||
void requestMap (MWWorld::Ptr::CellStore* cell);
|
||||
///< request the local map for a cell
|
||||
|
||||
/// configure fog according to cell
|
||||
void configureFog(ESMS::CellStore<MWWorld::RefData> &mCell);
|
||||
|
@ -162,6 +168,8 @@ class RenderingManager: private RenderingInterface {
|
|||
|
||||
MWRender::Player *mPlayer;
|
||||
MWRender::Debugging mDebugging;
|
||||
|
||||
MWRender::LocalMap* mLocalMap;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -254,7 +254,7 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType)
|
|||
// Get a pointer to the vertex colour
|
||||
ves_diffuse->baseVertexPointerToElement( pData, ¤tVertex );
|
||||
|
||||
unsigned char alpha;
|
||||
unsigned char alpha=0;
|
||||
if (meshType == 0) alpha = i%2 ? 0 : 255; // this is a cylinder, so every second vertex belongs to the bottom-most row
|
||||
else if (meshType == 1)
|
||||
{
|
||||
|
@ -292,10 +292,40 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType)
|
|||
ent->getMesh()->getSubMesh(0)->vertexData->vertexBufferBinding->getBuffer(ves_diffuse->getSource())->unlock();
|
||||
}
|
||||
|
||||
SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env) :
|
||||
mGlareFade(0), mGlareEnabled(false)
|
||||
SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env)
|
||||
: mEnvironment(env)
|
||||
, mHour(0.0f)
|
||||
, mDay(0)
|
||||
, mMonth(0)
|
||||
, mSun(NULL)
|
||||
, mSunGlare(NULL)
|
||||
, mMasser(NULL)
|
||||
, mSecunda(NULL)
|
||||
, mViewport(NULL)
|
||||
, mRootNode(NULL)
|
||||
, mSceneMgr(NULL)
|
||||
, mAtmosphereDay(NULL)
|
||||
, mAtmosphereNight(NULL)
|
||||
, mCloudMaterial()
|
||||
, mAtmosphereMaterial()
|
||||
, mCloudFragmentShader()
|
||||
, mClouds()
|
||||
, mNextClouds()
|
||||
, mCloudBlendFactor(0.0f)
|
||||
, mCloudOpacity(0.0f)
|
||||
, mCloudSpeed(0.0f)
|
||||
, mStarsOpacity(0.0f)
|
||||
, mThunderOverlay(NULL)
|
||||
, mThunderTextureUnit(NULL)
|
||||
, mRemainingTransitionTime(0.0f)
|
||||
, mGlareFade(0.0f)
|
||||
, mEnabled(true)
|
||||
, mGlareEnabled(true)
|
||||
, mSunEnabled(true)
|
||||
, mMasserEnabled(true)
|
||||
, mSecundaEnabled(true)
|
||||
{
|
||||
mEnvironment = env;
|
||||
|
||||
mViewport = pCamera->getViewport();
|
||||
mSceneMgr = pMwRoot->getCreator();
|
||||
mRootNode = pCamera->getParentSceneNode()->createChildSceneNode();
|
||||
|
@ -446,6 +476,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
|
||||
vshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
|
||||
mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName());
|
||||
mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram("");
|
||||
|
||||
// Clouds
|
||||
NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif");
|
||||
|
|
407
apps/openmw/mwsound/ffmpeg_decoder.cpp
Normal file
407
apps/openmw/mwsound/ffmpeg_decoder.cpp
Normal file
|
@ -0,0 +1,407 @@
|
|||
#ifdef OPENMW_USE_FFMPEG
|
||||
|
||||
#include "ffmpeg_decoder.hpp"
|
||||
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("FFmpeg exception: "+msg); }
|
||||
|
||||
|
||||
struct PacketList {
|
||||
AVPacket pkt;
|
||||
PacketList *next;
|
||||
};
|
||||
|
||||
struct FFmpeg_Decoder::MyStream {
|
||||
AVCodecContext *mCodecCtx;
|
||||
int mStreamIdx;
|
||||
|
||||
PacketList *mPackets;
|
||||
|
||||
char *mDecodedData;
|
||||
size_t mDecodedDataSize;
|
||||
|
||||
FFmpeg_Decoder *mParent;
|
||||
|
||||
void clearPackets();
|
||||
void *getAVAudioData(size_t *length);
|
||||
size_t readAVAudioData(void *data, size_t length);
|
||||
};
|
||||
|
||||
|
||||
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;
|
||||
return stream->read(buf, buf_size);
|
||||
}
|
||||
|
||||
int FFmpeg_Decoder::writePacket(void *user_data, uint8_t *buf, int buf_size)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;
|
||||
return stream->write(buf, buf_size);
|
||||
}
|
||||
|
||||
int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<FFmpeg_Decoder*>(user_data)->mDataStream;
|
||||
|
||||
whence &= ~AVSEEK_FORCE;
|
||||
if(whence == AVSEEK_SIZE)
|
||||
return stream->size();
|
||||
if(whence == SEEK_SET)
|
||||
stream->seek(offset);
|
||||
else if(whence == SEEK_CUR)
|
||||
stream->seek(stream->tell()+offset);
|
||||
else if(whence == SEEK_END)
|
||||
stream->seek(stream->size()+offset);
|
||||
else
|
||||
return -1;
|
||||
|
||||
return stream->tell();
|
||||
}
|
||||
|
||||
|
||||
/* Used by getAV*Data to search for more compressed data, and buffer it in the
|
||||
* correct stream. It won't buffer data for streams that the app doesn't have a
|
||||
* handle for. */
|
||||
bool FFmpeg_Decoder::getNextPacket(int streamidx)
|
||||
{
|
||||
PacketList *packet;
|
||||
|
||||
packet = (PacketList*)av_malloc(sizeof(*packet));
|
||||
packet->next = NULL;
|
||||
|
||||
next_packet:
|
||||
while(av_read_frame(mFormatCtx, &packet->pkt) >= 0)
|
||||
{
|
||||
std::vector<MyStream*>::iterator iter = mStreams.begin();
|
||||
|
||||
/* Check each stream the user has a handle for, looking for the one
|
||||
* this packet belongs to */
|
||||
while(iter != mStreams.end())
|
||||
{
|
||||
if((*iter)->mStreamIdx == packet->pkt.stream_index)
|
||||
{
|
||||
PacketList **last;
|
||||
|
||||
last = &(*iter)->mPackets;
|
||||
while(*last != NULL)
|
||||
last = &(*last)->next;
|
||||
|
||||
*last = packet;
|
||||
if((*iter)->mStreamIdx == streamidx)
|
||||
return true;
|
||||
|
||||
packet = (PacketList*)av_malloc(sizeof(*packet));
|
||||
packet->next = NULL;
|
||||
goto next_packet;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
/* Free the packet and look for another */
|
||||
av_free_packet(&packet->pkt);
|
||||
}
|
||||
av_free(packet);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FFmpeg_Decoder::MyStream::clearPackets()
|
||||
{
|
||||
while(mPackets)
|
||||
{
|
||||
PacketList *self = mPackets;
|
||||
mPackets = self->next;
|
||||
|
||||
av_free_packet(&self->pkt);
|
||||
av_free(self);
|
||||
}
|
||||
}
|
||||
|
||||
void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length)
|
||||
{
|
||||
int size;
|
||||
int len;
|
||||
|
||||
if(length) *length = 0;
|
||||
if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO)
|
||||
return NULL;
|
||||
|
||||
mDecodedDataSize = 0;
|
||||
|
||||
next_packet:
|
||||
if(!mPackets && !mParent->getNextPacket(mStreamIdx))
|
||||
return NULL;
|
||||
|
||||
/* Decode some data, and check for errors */
|
||||
size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
|
||||
while((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size,
|
||||
&mPackets->pkt)) == 0)
|
||||
{
|
||||
PacketList *self;
|
||||
|
||||
if(size > 0)
|
||||
break;
|
||||
|
||||
/* Packet went unread and no data was given? Drop it and try the next,
|
||||
* I guess... */
|
||||
self = mPackets;
|
||||
mPackets = self->next;
|
||||
|
||||
av_free_packet(&self->pkt);
|
||||
av_free(self);
|
||||
|
||||
if(!mPackets)
|
||||
goto next_packet;
|
||||
|
||||
size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
|
||||
}
|
||||
|
||||
if(len < 0)
|
||||
return NULL;
|
||||
|
||||
if(len < mPackets->pkt.size)
|
||||
{
|
||||
/* Move the unread data to the front and clear the end bits */
|
||||
int remaining = mPackets->pkt.size - len;
|
||||
memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining);
|
||||
memset(&mPackets->pkt.data[remaining], 0, mPackets->pkt.size - remaining);
|
||||
mPackets->pkt.size -= len;
|
||||
}
|
||||
else
|
||||
{
|
||||
PacketList *self;
|
||||
|
||||
self = mPackets;
|
||||
mPackets = self->next;
|
||||
|
||||
av_free_packet(&self->pkt);
|
||||
av_free(self);
|
||||
}
|
||||
|
||||
if(size == 0)
|
||||
goto next_packet;
|
||||
|
||||
/* Set the output buffer size */
|
||||
mDecodedDataSize = size;
|
||||
if(length) *length = mDecodedDataSize;
|
||||
|
||||
return mDecodedData;
|
||||
}
|
||||
|
||||
size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length)
|
||||
{
|
||||
size_t dec = 0;
|
||||
|
||||
while(dec < length)
|
||||
{
|
||||
/* If there's no decoded data, find some */
|
||||
if(mDecodedDataSize == 0)
|
||||
{
|
||||
if(getAVAudioData(NULL) == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if(mDecodedDataSize > 0)
|
||||
{
|
||||
/* Get the amount of bytes remaining to be written, and clamp to
|
||||
* the amount of decoded data we have */
|
||||
size_t rem = length-dec;
|
||||
if(rem > mDecodedDataSize)
|
||||
rem = mDecodedDataSize;
|
||||
|
||||
/* Copy the data to the app's buffer and increment */
|
||||
if(data != NULL)
|
||||
{
|
||||
memcpy(data, mDecodedData, rem);
|
||||
data = (char*)data + rem;
|
||||
}
|
||||
dec += rem;
|
||||
|
||||
/* If there's any decoded data left, move it to the front of the
|
||||
* buffer for next time */
|
||||
if(rem < mDecodedDataSize)
|
||||
memmove(mDecodedData, &mDecodedData[rem], mDecodedDataSize - rem);
|
||||
mDecodedDataSize -= rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of bytes we were able to get */
|
||||
return dec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FFmpeg_Decoder::open(const std::string &fname)
|
||||
{
|
||||
close();
|
||||
mDataStream = mResourceMgr.openResource(fname);
|
||||
|
||||
if((mFormatCtx=avformat_alloc_context()) == NULL)
|
||||
fail("Failed to allocate context");
|
||||
|
||||
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
|
||||
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
|
||||
{
|
||||
avformat_free_context(mFormatCtx);
|
||||
mFormatCtx = NULL;
|
||||
fail("Failed to allocate input stream");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(avformat_find_stream_info(mFormatCtx, NULL) < 0)
|
||||
fail("Failed to find stream info in "+fname);
|
||||
|
||||
for(size_t j = 0;j < mFormatCtx->nb_streams;j++)
|
||||
{
|
||||
if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
{
|
||||
std::auto_ptr<MyStream> stream(new MyStream);
|
||||
stream->mCodecCtx = mFormatCtx->streams[j]->codec;
|
||||
stream->mStreamIdx = j;
|
||||
stream->mPackets = NULL;
|
||||
|
||||
AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id);
|
||||
if(!codec)
|
||||
{
|
||||
std::stringstream ss("No codec found for id ");
|
||||
ss << stream->mCodecCtx->codec_id;
|
||||
fail(ss.str());
|
||||
}
|
||||
if(avcodec_open(stream->mCodecCtx, codec) < 0)
|
||||
fail("Failed to open audio codec " + std::string(codec->long_name));
|
||||
|
||||
stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
||||
stream->mDecodedDataSize = 0;
|
||||
|
||||
stream->mParent = this;
|
||||
mStreams.push_back(stream.release());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(mStreams.empty())
|
||||
fail("No audio streams in "+fname);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
av_close_input_file(mFormatCtx);
|
||||
mFormatCtx = NULL;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void FFmpeg_Decoder::close()
|
||||
{
|
||||
while(!mStreams.empty())
|
||||
{
|
||||
MyStream *stream = mStreams.front();
|
||||
|
||||
stream->clearPackets();
|
||||
avcodec_close(stream->mCodecCtx);
|
||||
av_free(stream->mDecodedData);
|
||||
delete stream;
|
||||
|
||||
mStreams.erase(mStreams.begin());
|
||||
}
|
||||
if(mFormatCtx)
|
||||
av_close_input_file(mFormatCtx);
|
||||
mFormatCtx = NULL;
|
||||
|
||||
mDataStream.setNull();
|
||||
}
|
||||
|
||||
void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
|
||||
{
|
||||
if(mStreams.empty())
|
||||
fail("No audio stream info");
|
||||
|
||||
MyStream *stream = mStreams[0];
|
||||
if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8)
|
||||
*type = SampleType_UInt8;
|
||||
else if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16)
|
||||
*type = SampleType_Int16;
|
||||
else
|
||||
fail(std::string("Unsupported sample format: ")+
|
||||
av_get_sample_fmt_name(stream->mCodecCtx->sample_fmt));
|
||||
|
||||
if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO)
|
||||
*chans = ChannelConfig_Mono;
|
||||
else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO)
|
||||
*chans = ChannelConfig_Stereo;
|
||||
else if(stream->mCodecCtx->channel_layout == 0)
|
||||
{
|
||||
/* Unknown channel layout. Try to guess. */
|
||||
if(stream->mCodecCtx->channels == 1)
|
||||
*chans = ChannelConfig_Mono;
|
||||
else if(stream->mCodecCtx->channels == 2)
|
||||
*chans = ChannelConfig_Stereo;
|
||||
else
|
||||
{
|
||||
std::stringstream sstr("Unsupported raw channel count: ");
|
||||
sstr << stream->mCodecCtx->channels;
|
||||
fail(sstr.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char str[1024];
|
||||
av_get_channel_layout_string(str, sizeof(str), stream->mCodecCtx->channels,
|
||||
stream->mCodecCtx->channel_layout);
|
||||
fail(std::string("Unsupported channel layout: ")+str);
|
||||
}
|
||||
|
||||
*samplerate = stream->mCodecCtx->sample_rate;
|
||||
}
|
||||
|
||||
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
|
||||
{
|
||||
if(mStreams.empty())
|
||||
fail("No audio streams");
|
||||
|
||||
return mStreams.front()->readAVAudioData(buffer, bytes);
|
||||
}
|
||||
|
||||
void FFmpeg_Decoder::readAll(std::vector<char> &output)
|
||||
{
|
||||
if(mStreams.empty())
|
||||
fail("No audio streams");
|
||||
MyStream *stream = mStreams.front();
|
||||
char *inbuf;
|
||||
size_t got;
|
||||
|
||||
while((inbuf=(char*)stream->getAVAudioData(&got)) != NULL && got > 0)
|
||||
output.insert(output.end(), inbuf, inbuf+got);
|
||||
}
|
||||
|
||||
void FFmpeg_Decoder::rewind()
|
||||
{
|
||||
av_seek_frame(mFormatCtx, -1, 0, 0);
|
||||
std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets));
|
||||
}
|
||||
|
||||
FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL)
|
||||
{
|
||||
static bool done_init = false;
|
||||
|
||||
/* We need to make sure ffmpeg is initialized. Optionally silence warning
|
||||
* output from the lib */
|
||||
if(!done_init)
|
||||
{
|
||||
av_register_all();
|
||||
av_log_set_level(AV_LOG_ERROR);
|
||||
done_init = true;
|
||||
}
|
||||
}
|
||||
|
||||
FFmpeg_Decoder::~FFmpeg_Decoder()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
59
apps/openmw/mwsound/ffmpeg_decoder.hpp
Normal file
59
apps/openmw/mwsound/ffmpeg_decoder.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#ifndef GAME_SOUND_FFMPEG_DECODER_H
|
||||
#define GAME_SOUND_FFMPEG_DECODER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
// FIXME: This can't be right? The headers refuse to build without UINT64_C,
|
||||
// which only gets defined in stdint.h in either C99 mode or with this macro
|
||||
// defined...
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#include <stdint.h>
|
||||
extern "C"
|
||||
{
|
||||
#include <avcodec.h>
|
||||
#include <avformat.h>
|
||||
}
|
||||
|
||||
#include "sound_decoder.hpp"
|
||||
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class FFmpeg_Decoder : public Sound_Decoder
|
||||
{
|
||||
AVFormatContext *mFormatCtx;
|
||||
|
||||
struct MyStream;
|
||||
std::vector<MyStream*> mStreams;
|
||||
|
||||
bool getNextPacket(int streamidx);
|
||||
|
||||
Ogre::DataStreamPtr mDataStream;
|
||||
static int readPacket(void *user_data, uint8_t *buf, int buf_size);
|
||||
static int writePacket(void *user_data, uint8_t *buf, int buf_size);
|
||||
static int64_t seek(void *user_data, int64_t offset, int whence);
|
||||
|
||||
virtual void open(const std::string &fname);
|
||||
virtual void close();
|
||||
|
||||
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
|
||||
|
||||
virtual size_t read(char *buffer, size_t bytes);
|
||||
virtual void readAll(std::vector<char> &output);
|
||||
virtual void rewind();
|
||||
|
||||
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
|
||||
FFmpeg_Decoder(const FFmpeg_Decoder &rhs);
|
||||
|
||||
FFmpeg_Decoder();
|
||||
public:
|
||||
virtual ~FFmpeg_Decoder();
|
||||
|
||||
friend class SoundManager;
|
||||
};
|
||||
#ifndef DEFAULT_DECODER
|
||||
#define DEFAULT_DECODER (::MWSound::FFmpeg_Decoder)
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
237
apps/openmw/mwsound/mpgsnd_decoder.cpp
Normal file
237
apps/openmw/mwsound/mpgsnd_decoder.cpp
Normal file
|
@ -0,0 +1,237 @@
|
|||
#ifdef OPENMW_USE_MPG123
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include "mpgsnd_decoder.hpp"
|
||||
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("MpgSnd exception: "+msg); }
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
|
||||
//
|
||||
// libSndFile io callbacks
|
||||
//
|
||||
sf_count_t MpgSnd_Decoder::ogresf_get_filelen(void *user_data)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
|
||||
return stream->size();
|
||||
}
|
||||
|
||||
sf_count_t MpgSnd_Decoder::ogresf_seek(sf_count_t offset, int whence, void *user_data)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
|
||||
|
||||
if(whence == SEEK_CUR)
|
||||
stream->seek(stream->tell()+offset);
|
||||
else if(whence == SEEK_SET)
|
||||
stream->seek(offset);
|
||||
else if(whence == SEEK_END)
|
||||
stream->seek(stream->size()+offset);
|
||||
else
|
||||
return -1;
|
||||
|
||||
return stream->tell();
|
||||
}
|
||||
|
||||
sf_count_t MpgSnd_Decoder::ogresf_read(void *ptr, sf_count_t count, void *user_data)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
|
||||
return stream->read(ptr, count);
|
||||
}
|
||||
|
||||
sf_count_t MpgSnd_Decoder::ogresf_write(const void*, sf_count_t, void*)
|
||||
{ return -1; }
|
||||
|
||||
sf_count_t MpgSnd_Decoder::ogresf_tell(void *user_data)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
|
||||
return stream->tell();
|
||||
}
|
||||
|
||||
//
|
||||
// libmpg13 io callbacks
|
||||
//
|
||||
ssize_t MpgSnd_Decoder::ogrempg_read(void *user_data, void *ptr, size_t count)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
|
||||
return stream->read(ptr, count);
|
||||
}
|
||||
|
||||
off_t MpgSnd_Decoder::ogrempg_lseek(void *user_data, off_t offset, int whence)
|
||||
{
|
||||
Ogre::DataStreamPtr stream = static_cast<MpgSnd_Decoder*>(user_data)->mDataStream;
|
||||
|
||||
if(whence == SEEK_CUR)
|
||||
stream->seek(stream->tell()+offset);
|
||||
else if(whence == SEEK_SET)
|
||||
stream->seek(offset);
|
||||
else if(whence == SEEK_END)
|
||||
stream->seek(stream->size()+offset);
|
||||
else
|
||||
return -1;
|
||||
|
||||
return stream->tell();
|
||||
}
|
||||
|
||||
|
||||
void MpgSnd_Decoder::open(const std::string &fname)
|
||||
{
|
||||
close();
|
||||
mDataStream = mResourceMgr.openResource(fname);
|
||||
|
||||
SF_VIRTUAL_IO streamIO = {
|
||||
ogresf_get_filelen, ogresf_seek,
|
||||
ogresf_read, ogresf_write, ogresf_tell
|
||||
};
|
||||
mSndFile = sf_open_virtual(&streamIO, SFM_READ, &mSndInfo, this);
|
||||
if(mSndFile)
|
||||
{
|
||||
if(mSndInfo.channels == 1)
|
||||
mChanConfig = ChannelConfig_Mono;
|
||||
else if(mSndInfo.channels == 2)
|
||||
mChanConfig = ChannelConfig_Stereo;
|
||||
else
|
||||
{
|
||||
sf_close(mSndFile);
|
||||
mSndFile = NULL;
|
||||
fail("Unsupported channel count in "+fname);
|
||||
}
|
||||
mSampleRate = mSndInfo.samplerate;
|
||||
return;
|
||||
}
|
||||
mDataStream->seek(0);
|
||||
|
||||
mMpgFile = mpg123_new(NULL, NULL);
|
||||
if(mMpgFile && mpg123_replace_reader_handle(mMpgFile, ogrempg_read, ogrempg_lseek, NULL) == MPG123_OK &&
|
||||
mpg123_open_handle(mMpgFile, this) == MPG123_OK)
|
||||
{
|
||||
try
|
||||
{
|
||||
int encoding, channels;
|
||||
long rate;
|
||||
if(mpg123_getformat(mMpgFile, &rate, &channels, &encoding) != MPG123_OK)
|
||||
fail("Failed to get audio format");
|
||||
if(encoding != MPG123_ENC_SIGNED_16)
|
||||
fail("Unsupported encoding in "+fname);
|
||||
if(channels != 1 && channels != 2)
|
||||
fail("Unsupported channel count in "+fname);
|
||||
mChanConfig = ((channels==2)?ChannelConfig_Stereo:ChannelConfig_Mono);
|
||||
mSampleRate = rate;
|
||||
return;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
mpg123_close(mMpgFile);
|
||||
mpg123_delete(mMpgFile);
|
||||
mMpgFile = NULL;
|
||||
throw;
|
||||
}
|
||||
mpg123_close(mMpgFile);
|
||||
}
|
||||
if(mMpgFile)
|
||||
mpg123_delete(mMpgFile);
|
||||
mMpgFile = NULL;
|
||||
|
||||
fail("Unsupported file type: "+fname);
|
||||
}
|
||||
|
||||
void MpgSnd_Decoder::close()
|
||||
{
|
||||
if(mSndFile)
|
||||
sf_close(mSndFile);
|
||||
mSndFile = NULL;
|
||||
|
||||
if(mMpgFile)
|
||||
{
|
||||
mpg123_close(mMpgFile);
|
||||
mpg123_delete(mMpgFile);
|
||||
mMpgFile = NULL;
|
||||
}
|
||||
|
||||
mDataStream.setNull();
|
||||
}
|
||||
|
||||
void MpgSnd_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
|
||||
{
|
||||
if(!mSndFile && !mMpgFile)
|
||||
fail("No open file");
|
||||
|
||||
*samplerate = mSampleRate;
|
||||
*chans = mChanConfig;
|
||||
*type = SampleType_Int16;
|
||||
}
|
||||
|
||||
size_t MpgSnd_Decoder::read(char *buffer, size_t bytes)
|
||||
{
|
||||
size_t got = 0;
|
||||
|
||||
if(mSndFile)
|
||||
{
|
||||
got = sf_read_short(mSndFile, (short*)buffer, bytes/2)*2;
|
||||
}
|
||||
else if(mMpgFile)
|
||||
{
|
||||
int err;
|
||||
err = mpg123_read(mMpgFile, (unsigned char*)buffer, bytes, &got);
|
||||
if(err != MPG123_OK && err != MPG123_DONE)
|
||||
fail("Failed to read from file");
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
void MpgSnd_Decoder::readAll(std::vector<char> &output)
|
||||
{
|
||||
if(mSndFile && mSndInfo.frames > 0)
|
||||
{
|
||||
size_t pos = output.size();
|
||||
output.resize(pos + mSndInfo.frames*mSndInfo.channels*2);
|
||||
sf_readf_short(mSndFile, (short*)(output.data()+pos), mSndInfo.frames);
|
||||
return;
|
||||
}
|
||||
// Fallback in case we don't know the total already
|
||||
Sound_Decoder::readAll(output);
|
||||
}
|
||||
|
||||
void MpgSnd_Decoder::rewind()
|
||||
{
|
||||
if(!mSndFile && !mMpgFile)
|
||||
fail("No open file");
|
||||
|
||||
if(mSndFile)
|
||||
{
|
||||
if(sf_seek(mSndFile, 0, SEEK_SET) == -1)
|
||||
fail("seek failed");
|
||||
}
|
||||
else if(mMpgFile)
|
||||
{
|
||||
if(mpg123_seek(mMpgFile, 0, SEEK_SET) < 0)
|
||||
fail("seek failed");
|
||||
}
|
||||
}
|
||||
|
||||
MpgSnd_Decoder::MpgSnd_Decoder()
|
||||
: mSndInfo()
|
||||
, mSndFile(NULL)
|
||||
, mMpgFile(NULL)
|
||||
, mDataStream()
|
||||
, mChanConfig(ChannelConfig_Stereo)
|
||||
, mSampleRate(0)
|
||||
{
|
||||
static bool initdone = false;
|
||||
if(!initdone)
|
||||
mpg123_init();
|
||||
initdone = true;
|
||||
}
|
||||
|
||||
MpgSnd_Decoder::~MpgSnd_Decoder()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
57
apps/openmw/mwsound/mpgsnd_decoder.hpp
Normal file
57
apps/openmw/mwsound/mpgsnd_decoder.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#ifndef GAME_SOUND_MPGSND_DECODER_H
|
||||
#define GAME_SOUND_MPGSND_DECODER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <OgreDataStream.h>
|
||||
|
||||
#include "mpg123.h"
|
||||
#include "sndfile.h"
|
||||
|
||||
#include "sound_decoder.hpp"
|
||||
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class MpgSnd_Decoder : public Sound_Decoder
|
||||
{
|
||||
SF_INFO mSndInfo;
|
||||
SNDFILE *mSndFile;
|
||||
mpg123_handle *mMpgFile;
|
||||
|
||||
Ogre::DataStreamPtr mDataStream;
|
||||
static sf_count_t ogresf_get_filelen(void *user_data);
|
||||
static sf_count_t ogresf_seek(sf_count_t offset, int whence, void *user_data);
|
||||
static sf_count_t ogresf_read(void *ptr, sf_count_t count, void *user_data);
|
||||
static sf_count_t ogresf_write(const void*, sf_count_t, void*);
|
||||
static sf_count_t ogresf_tell(void *user_data);
|
||||
static ssize_t ogrempg_read(void*, void*, size_t);
|
||||
static off_t ogrempg_lseek(void*, off_t, int);
|
||||
|
||||
ChannelConfig mChanConfig;
|
||||
int mSampleRate;
|
||||
|
||||
virtual void open(const std::string &fname);
|
||||
virtual void close();
|
||||
|
||||
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type);
|
||||
|
||||
virtual size_t read(char *buffer, size_t bytes);
|
||||
virtual void readAll(std::vector<char> &output);
|
||||
virtual void rewind();
|
||||
|
||||
MpgSnd_Decoder& operator=(const MpgSnd_Decoder &rhs);
|
||||
MpgSnd_Decoder(const MpgSnd_Decoder &rhs);
|
||||
|
||||
MpgSnd_Decoder();
|
||||
public:
|
||||
virtual ~MpgSnd_Decoder();
|
||||
|
||||
friend class SoundManager;
|
||||
};
|
||||
#ifndef DEFAULT_DECODER
|
||||
#define DEFAULT_DECODER (::MWSound::MpgSnd_Decoder)
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
793
apps/openmw/mwsound/openal_output.cpp
Normal file
793
apps/openmw/mwsound/openal_output.cpp
Normal file
|
@ -0,0 +1,793 @@
|
|||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "openal_output.hpp"
|
||||
#include "sound_decoder.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "soundmanager.hpp"
|
||||
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#define ALC_ALL_DEVICES_SPECIFIER 0x1013
|
||||
#endif
|
||||
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("OpenAL exception: " + msg); }
|
||||
|
||||
static void throwALCerror(ALCdevice *device)
|
||||
{
|
||||
ALCenum err = alcGetError(device);
|
||||
if(err != ALC_NO_ERROR)
|
||||
fail(alcGetString(device, err));
|
||||
}
|
||||
|
||||
static void throwALerror()
|
||||
{
|
||||
ALenum err = alGetError();
|
||||
if(err != AL_NO_ERROR)
|
||||
fail(alGetString(err));
|
||||
}
|
||||
|
||||
|
||||
static ALenum getALFormat(ChannelConfig chans, SampleType type)
|
||||
{
|
||||
static const struct {
|
||||
ALenum format;
|
||||
ChannelConfig chans;
|
||||
SampleType type;
|
||||
} fmtlist[] = {
|
||||
{ AL_FORMAT_MONO16, ChannelConfig_Mono, SampleType_Int16 },
|
||||
{ AL_FORMAT_MONO8, ChannelConfig_Mono, SampleType_UInt8 },
|
||||
{ AL_FORMAT_STEREO16, ChannelConfig_Stereo, SampleType_Int16 },
|
||||
{ AL_FORMAT_STEREO8, ChannelConfig_Stereo, SampleType_UInt8 },
|
||||
};
|
||||
static const size_t fmtlistsize = sizeof(fmtlist)/sizeof(fmtlist[0]);
|
||||
|
||||
for(size_t i = 0;i < fmtlistsize;i++)
|
||||
{
|
||||
if(fmtlist[i].chans == chans && fmtlist[i].type == type)
|
||||
return fmtlist[i].format;
|
||||
}
|
||||
fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")");
|
||||
return AL_NONE;
|
||||
}
|
||||
|
||||
//
|
||||
// A streaming OpenAL sound.
|
||||
//
|
||||
class OpenAL_SoundStream : public Sound
|
||||
{
|
||||
static const ALuint sNumBuffers = 6;
|
||||
static const ALfloat sBufferLength;
|
||||
|
||||
OpenAL_Output &mOutput;
|
||||
|
||||
ALuint mSource;
|
||||
ALuint mBuffers[sNumBuffers];
|
||||
|
||||
ALenum mFormat;
|
||||
ALsizei mSampleRate;
|
||||
ALuint mBufferSize;
|
||||
|
||||
DecoderPtr mDecoder;
|
||||
|
||||
volatile bool mIsFinished;
|
||||
|
||||
OpenAL_SoundStream(const OpenAL_SoundStream &rhs);
|
||||
OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs);
|
||||
|
||||
public:
|
||||
OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder);
|
||||
virtual ~OpenAL_SoundStream();
|
||||
|
||||
virtual void stop();
|
||||
virtual bool isPlaying();
|
||||
virtual void setVolume(float volume);
|
||||
virtual void update(const float *pos);
|
||||
|
||||
void play();
|
||||
bool process();
|
||||
};
|
||||
|
||||
const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f;
|
||||
|
||||
//
|
||||
// A background streaming thread (keeps active streams processed)
|
||||
//
|
||||
struct OpenAL_Output::StreamThread {
|
||||
typedef std::vector<OpenAL_SoundStream*> StreamVec;
|
||||
StreamVec mStreams;
|
||||
boost::mutex mMutex;
|
||||
boost::thread mThread;
|
||||
|
||||
StreamThread()
|
||||
: mThread(boost::ref(*this))
|
||||
{
|
||||
}
|
||||
~StreamThread()
|
||||
{
|
||||
mThread.interrupt();
|
||||
}
|
||||
|
||||
// boost::thread entry point
|
||||
void operator()()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
mMutex.lock();
|
||||
StreamVec::iterator iter = mStreams.begin();
|
||||
while(iter != mStreams.end())
|
||||
{
|
||||
if((*iter)->process() == false)
|
||||
iter = mStreams.erase(iter);
|
||||
else
|
||||
iter++;
|
||||
}
|
||||
mMutex.unlock();
|
||||
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
void add(OpenAL_SoundStream *stream)
|
||||
{
|
||||
mMutex.lock();
|
||||
if(std::find(mStreams.begin(), mStreams.end(), stream) == mStreams.end())
|
||||
mStreams.push_back(stream);
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
void remove(OpenAL_SoundStream *stream)
|
||||
{
|
||||
mMutex.lock();
|
||||
StreamVec::iterator iter = std::find(mStreams.begin(), mStreams.end(), stream);
|
||||
if(iter != mStreams.end())
|
||||
mStreams.erase(iter);
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
void removeAll()
|
||||
{
|
||||
mMutex.lock();
|
||||
mStreams.clear();
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
StreamThread(const StreamThread &rhs);
|
||||
StreamThread& operator=(const StreamThread &rhs);
|
||||
};
|
||||
|
||||
|
||||
OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder)
|
||||
: mOutput(output), mSource(src), mDecoder(decoder), mIsFinished(true)
|
||||
{
|
||||
throwALerror();
|
||||
|
||||
alGenBuffers(sNumBuffers, mBuffers);
|
||||
throwALerror();
|
||||
try
|
||||
{
|
||||
int srate;
|
||||
ChannelConfig chans;
|
||||
SampleType type;
|
||||
|
||||
mDecoder->getInfo(&srate, &chans, &type);
|
||||
mFormat = getALFormat(chans, type);
|
||||
mSampleRate = srate;
|
||||
|
||||
mBufferSize = static_cast<ALuint>(sBufferLength*srate);
|
||||
mBufferSize = framesToBytes(mBufferSize, chans, type);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
mOutput.mFreeSources.push_back(mSource);
|
||||
alDeleteBuffers(sNumBuffers, mBuffers);
|
||||
alGetError();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
OpenAL_SoundStream::~OpenAL_SoundStream()
|
||||
{
|
||||
mOutput.mStreamThread->remove(this);
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
mOutput.mFreeSources.push_back(mSource);
|
||||
alDeleteBuffers(sNumBuffers, mBuffers);
|
||||
alGetError();
|
||||
|
||||
mDecoder->close();
|
||||
}
|
||||
|
||||
void OpenAL_SoundStream::play()
|
||||
{
|
||||
std::vector<char> data(mBufferSize);
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
throwALerror();
|
||||
|
||||
for(ALuint i = 0;i < sNumBuffers;i++)
|
||||
{
|
||||
size_t got;
|
||||
got = mDecoder->read(data.data(), data.size());
|
||||
alBufferData(mBuffers[i], mFormat, data.data(), got, mSampleRate);
|
||||
}
|
||||
throwALerror();
|
||||
|
||||
alSourceQueueBuffers(mSource, sNumBuffers, mBuffers);
|
||||
alSourcePlay(mSource);
|
||||
throwALerror();
|
||||
|
||||
mIsFinished = false;
|
||||
mOutput.mStreamThread->add(this);
|
||||
}
|
||||
|
||||
void OpenAL_SoundStream::stop()
|
||||
{
|
||||
mOutput.mStreamThread->remove(this);
|
||||
mIsFinished = true;
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
throwALerror();
|
||||
|
||||
mDecoder->rewind();
|
||||
}
|
||||
|
||||
bool OpenAL_SoundStream::isPlaying()
|
||||
{
|
||||
ALint state;
|
||||
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
throwALerror();
|
||||
|
||||
if(state == AL_PLAYING)
|
||||
return true;
|
||||
return !mIsFinished;
|
||||
}
|
||||
|
||||
void OpenAL_SoundStream::setVolume(float volume)
|
||||
{
|
||||
alSourcef(mSource, AL_GAIN, volume*mBaseVolume);
|
||||
throwALerror();
|
||||
mVolume = volume;
|
||||
}
|
||||
|
||||
void OpenAL_SoundStream::update(const float *pos)
|
||||
{
|
||||
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
|
||||
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
throwALerror();
|
||||
}
|
||||
|
||||
bool OpenAL_SoundStream::process()
|
||||
{
|
||||
bool finished = mIsFinished;
|
||||
ALint processed, state;
|
||||
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
throwALerror();
|
||||
|
||||
if(processed > 0)
|
||||
{
|
||||
std::vector<char> data(mBufferSize);
|
||||
do {
|
||||
ALuint bufid;
|
||||
size_t got;
|
||||
|
||||
alSourceUnqueueBuffers(mSource, 1, &bufid);
|
||||
processed--;
|
||||
|
||||
if(finished)
|
||||
continue;
|
||||
|
||||
got = mDecoder->read(data.data(), data.size());
|
||||
finished = (got < data.size());
|
||||
if(got > 0)
|
||||
{
|
||||
alBufferData(bufid, mFormat, data.data(), got, mSampleRate);
|
||||
alSourceQueueBuffers(mSource, 1, &bufid);
|
||||
}
|
||||
} while(processed > 0);
|
||||
throwALerror();
|
||||
}
|
||||
|
||||
if(state != AL_PLAYING && state != AL_PAUSED)
|
||||
{
|
||||
ALint queued;
|
||||
|
||||
alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued);
|
||||
throwALerror();
|
||||
if(queued > 0)
|
||||
{
|
||||
alSourcePlay(mSource);
|
||||
throwALerror();
|
||||
}
|
||||
}
|
||||
|
||||
mIsFinished = finished;
|
||||
return !finished;
|
||||
}
|
||||
|
||||
//
|
||||
// A regular OpenAL sound
|
||||
//
|
||||
class OpenAL_Sound : public Sound
|
||||
{
|
||||
OpenAL_Output &mOutput;
|
||||
|
||||
ALuint mSource;
|
||||
ALuint mBuffer;
|
||||
|
||||
OpenAL_Sound(const OpenAL_Sound &rhs);
|
||||
OpenAL_Sound& operator=(const OpenAL_Sound &rhs);
|
||||
|
||||
public:
|
||||
OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf);
|
||||
virtual ~OpenAL_Sound();
|
||||
|
||||
virtual void stop();
|
||||
virtual bool isPlaying();
|
||||
virtual void setVolume(float volume);
|
||||
virtual void update(const float *pos);
|
||||
};
|
||||
|
||||
OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf)
|
||||
: mOutput(output), mSource(src), mBuffer(buf)
|
||||
{
|
||||
}
|
||||
OpenAL_Sound::~OpenAL_Sound()
|
||||
{
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
mOutput.mFreeSources.push_back(mSource);
|
||||
mOutput.bufferFinished(mBuffer);
|
||||
}
|
||||
|
||||
void OpenAL_Sound::stop()
|
||||
{
|
||||
alSourceStop(mSource);
|
||||
throwALerror();
|
||||
}
|
||||
|
||||
bool OpenAL_Sound::isPlaying()
|
||||
{
|
||||
ALint state;
|
||||
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
throwALerror();
|
||||
|
||||
return state==AL_PLAYING;
|
||||
}
|
||||
|
||||
void OpenAL_Sound::setVolume(float volume)
|
||||
{
|
||||
alSourcef(mSource, AL_GAIN, volume*mBaseVolume);
|
||||
throwALerror();
|
||||
mVolume = volume;
|
||||
}
|
||||
|
||||
void OpenAL_Sound::update(const float *pos)
|
||||
{
|
||||
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
|
||||
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
throwALerror();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// An OpenAL output device
|
||||
//
|
||||
std::vector<std::string> OpenAL_Output::enumerate()
|
||||
{
|
||||
std::vector<std::string> devlist;
|
||||
const ALCchar *devnames;
|
||||
|
||||
if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
|
||||
devnames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||
else
|
||||
devnames = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
while(devnames && *devnames)
|
||||
{
|
||||
devlist.push_back(devnames);
|
||||
devnames += strlen(devnames)+1;
|
||||
}
|
||||
return devlist;
|
||||
}
|
||||
|
||||
void OpenAL_Output::init(const std::string &devname)
|
||||
{
|
||||
if(mDevice || mContext)
|
||||
fail("Device already open");
|
||||
|
||||
mDevice = alcOpenDevice(devname.c_str());
|
||||
if(!mDevice)
|
||||
{
|
||||
if(devname.empty())
|
||||
fail("Failed to open default device");
|
||||
else
|
||||
fail("Failed to open \""+devname+"\"");
|
||||
}
|
||||
if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT"))
|
||||
std::cout << "Opened \""<<alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER)<<"\"" << std::endl;
|
||||
else
|
||||
std::cout << "Opened \""<<alcGetString(mDevice, ALC_DEVICE_SPECIFIER)<<"\"" << std::endl;
|
||||
|
||||
mContext = alcCreateContext(mDevice, NULL);
|
||||
if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)
|
||||
fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice)));
|
||||
|
||||
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
|
||||
throwALerror();
|
||||
|
||||
ALCint maxmono=0, maxstereo=0;
|
||||
alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);
|
||||
alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);
|
||||
throwALCerror(mDevice);
|
||||
|
||||
try
|
||||
{
|
||||
ALCuint maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256);
|
||||
for(size_t i = 0;i < maxtotal;i++)
|
||||
{
|
||||
ALuint src = 0;
|
||||
alGenSources(1, &src);
|
||||
throwALerror();
|
||||
mFreeSources.push_back(src);
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout <<"Error: "<<e.what()<<", trying to continue"<< std::endl;
|
||||
}
|
||||
if(mFreeSources.empty())
|
||||
fail("Could not allocate any sources");
|
||||
}
|
||||
|
||||
void OpenAL_Output::deinit()
|
||||
{
|
||||
mStreamThread->removeAll();
|
||||
|
||||
while(!mFreeSources.empty())
|
||||
{
|
||||
alDeleteSources(1, &mFreeSources.front());
|
||||
mFreeSources.pop_front();
|
||||
}
|
||||
|
||||
mBufferRefs.clear();
|
||||
mUnusedBuffers.clear();
|
||||
while(!mBufferCache.empty())
|
||||
{
|
||||
alDeleteBuffers(1, &mBufferCache.begin()->second);
|
||||
mBufferCache.erase(mBufferCache.begin());
|
||||
}
|
||||
|
||||
alcMakeContextCurrent(0);
|
||||
if(mContext)
|
||||
alcDestroyContext(mContext);
|
||||
mContext = 0;
|
||||
if(mDevice)
|
||||
alcCloseDevice(mDevice);
|
||||
mDevice = 0;
|
||||
}
|
||||
|
||||
|
||||
ALuint OpenAL_Output::getBuffer(const std::string &fname)
|
||||
{
|
||||
ALuint buf = 0;
|
||||
|
||||
NameMap::iterator iditer = mBufferCache.find(fname);
|
||||
if(iditer != mBufferCache.end())
|
||||
{
|
||||
buf = iditer->second;
|
||||
if(mBufferRefs[buf]++ == 0)
|
||||
{
|
||||
IDDq::iterator iter = std::find(mUnusedBuffers.begin(),
|
||||
mUnusedBuffers.end(), buf);
|
||||
if(iter != mUnusedBuffers.end())
|
||||
mUnusedBuffers.erase(iter);
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
throwALerror();
|
||||
|
||||
std::vector<char> data;
|
||||
ChannelConfig chans;
|
||||
SampleType type;
|
||||
ALenum format;
|
||||
int srate;
|
||||
|
||||
DecoderPtr decoder = mManager.getDecoder();
|
||||
try
|
||||
{
|
||||
decoder->open(fname);
|
||||
}
|
||||
catch(Ogre::FileNotFoundException &e)
|
||||
{
|
||||
std::string::size_type pos = fname.rfind('.');
|
||||
if(pos == std::string::npos)
|
||||
throw;
|
||||
decoder->open(fname.substr(0, pos)+".mp3");
|
||||
}
|
||||
|
||||
decoder->getInfo(&srate, &chans, &type);
|
||||
format = getALFormat(chans, type);
|
||||
|
||||
decoder->readAll(data);
|
||||
decoder->close();
|
||||
|
||||
alGenBuffers(1, &buf);
|
||||
throwALerror();
|
||||
|
||||
alBufferData(buf, format, data.data(), data.size(), srate);
|
||||
mBufferCache[fname] = buf;
|
||||
mBufferRefs[buf] = 1;
|
||||
|
||||
ALint bufsize = 0;
|
||||
alGetBufferi(buf, AL_SIZE, &bufsize);
|
||||
mBufferCacheMemSize += bufsize;
|
||||
|
||||
// NOTE: Max buffer cache: 15MB
|
||||
while(mBufferCacheMemSize > 15*1024*1024)
|
||||
{
|
||||
if(mUnusedBuffers.empty())
|
||||
{
|
||||
std::cout <<"No more unused buffers to clear!"<< std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
ALuint oldbuf = mUnusedBuffers.front();
|
||||
mUnusedBuffers.pop_front();
|
||||
|
||||
NameMap::iterator nameiter = mBufferCache.begin();
|
||||
while(nameiter != mBufferCache.end())
|
||||
{
|
||||
if(nameiter->second == oldbuf)
|
||||
mBufferCache.erase(nameiter++);
|
||||
else
|
||||
nameiter++;
|
||||
}
|
||||
|
||||
bufsize = 0;
|
||||
alGetBufferi(oldbuf, AL_SIZE, &bufsize);
|
||||
alDeleteBuffers(1, &oldbuf);
|
||||
mBufferCacheMemSize -= bufsize;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void OpenAL_Output::bufferFinished(ALuint buf)
|
||||
{
|
||||
if(mBufferRefs.at(buf)-- == 1)
|
||||
{
|
||||
mBufferRefs.erase(mBufferRefs.find(buf));
|
||||
mUnusedBuffers.push_back(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, bool loop)
|
||||
{
|
||||
throwALerror();
|
||||
|
||||
boost::shared_ptr<OpenAL_Sound> sound;
|
||||
ALuint src=0, buf=0;
|
||||
|
||||
if(mFreeSources.empty())
|
||||
fail("No free sources");
|
||||
src = mFreeSources.front();
|
||||
mFreeSources.pop_front();
|
||||
|
||||
try
|
||||
{
|
||||
buf = getBuffer(fname);
|
||||
sound.reset(new OpenAL_Sound(*this, src, buf));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
mFreeSources.push_back(src);
|
||||
if(buf && alIsBuffer(buf))
|
||||
bufferFinished(buf);
|
||||
alGetError();
|
||||
throw;
|
||||
}
|
||||
|
||||
alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f);
|
||||
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
|
||||
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
|
||||
|
||||
alSourcef(src, AL_GAIN, volume);
|
||||
alSourcef(src, AL_PITCH, pitch);
|
||||
|
||||
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
|
||||
throwALerror();
|
||||
|
||||
alSourcei(src, AL_BUFFER, buf);
|
||||
alSourcePlay(src);
|
||||
throwALerror();
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
||||
float min, float max, bool loop)
|
||||
{
|
||||
throwALerror();
|
||||
|
||||
boost::shared_ptr<OpenAL_Sound> sound;
|
||||
ALuint src=0, buf=0;
|
||||
|
||||
if(mFreeSources.empty())
|
||||
fail("No free sources");
|
||||
src = mFreeSources.front();
|
||||
mFreeSources.pop_front();
|
||||
|
||||
try
|
||||
{
|
||||
buf = getBuffer(fname);
|
||||
sound.reset(new OpenAL_Sound(*this, src, buf));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
mFreeSources.push_back(src);
|
||||
if(buf && alIsBuffer(buf))
|
||||
bufferFinished(buf);
|
||||
alGetError();
|
||||
throw;
|
||||
}
|
||||
|
||||
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
|
||||
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
alSourcef(src, AL_REFERENCE_DISTANCE, min);
|
||||
alSourcef(src, AL_MAX_DISTANCE, max);
|
||||
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
|
||||
|
||||
alSourcef(src, AL_GAIN, volume);
|
||||
alSourcef(src, AL_PITCH, pitch);
|
||||
|
||||
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
|
||||
throwALerror();
|
||||
|
||||
alSourcei(src, AL_BUFFER, buf);
|
||||
alSourcePlay(src);
|
||||
throwALerror();
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
|
||||
SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch)
|
||||
{
|
||||
throwALerror();
|
||||
|
||||
boost::shared_ptr<OpenAL_SoundStream> sound;
|
||||
ALuint src;
|
||||
|
||||
if(mFreeSources.empty())
|
||||
fail("No free sources");
|
||||
src = mFreeSources.front();
|
||||
mFreeSources.pop_front();
|
||||
|
||||
try
|
||||
{
|
||||
DecoderPtr decoder = mManager.getDecoder();
|
||||
decoder->open(fname);
|
||||
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
mFreeSources.push_back(src);
|
||||
throw;
|
||||
}
|
||||
|
||||
alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f);
|
||||
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
|
||||
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
|
||||
|
||||
alSourcef(src, AL_GAIN, volume);
|
||||
alSourcef(src, AL_PITCH, pitch);
|
||||
|
||||
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||
alSourcei(src, AL_LOOPING, AL_FALSE);
|
||||
throwALerror();
|
||||
|
||||
sound->play();
|
||||
return sound;
|
||||
}
|
||||
|
||||
SoundPtr OpenAL_Output::streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
||||
float min, float max)
|
||||
{
|
||||
throwALerror();
|
||||
|
||||
boost::shared_ptr<OpenAL_SoundStream> sound;
|
||||
ALuint src;
|
||||
|
||||
if(mFreeSources.empty())
|
||||
fail("No free sources");
|
||||
src = mFreeSources.front();
|
||||
mFreeSources.pop_front();
|
||||
|
||||
try
|
||||
{
|
||||
DecoderPtr decoder = mManager.getDecoder();
|
||||
decoder->open(fname);
|
||||
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
mFreeSources.push_back(src);
|
||||
throw;
|
||||
}
|
||||
|
||||
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
|
||||
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
alSourcef(src, AL_REFERENCE_DISTANCE, min);
|
||||
alSourcef(src, AL_MAX_DISTANCE, max);
|
||||
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
|
||||
|
||||
alSourcef(src, AL_GAIN, volume);
|
||||
alSourcef(src, AL_PITCH, pitch);
|
||||
|
||||
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||
alSourcei(src, AL_LOOPING, AL_FALSE);
|
||||
throwALerror();
|
||||
|
||||
sound->play();
|
||||
return sound;
|
||||
}
|
||||
|
||||
|
||||
void OpenAL_Output::updateListener(const float *pos, const float *atdir, const float *updir)
|
||||
{
|
||||
float orient[6] = {
|
||||
atdir[0], atdir[2], -atdir[1],
|
||||
updir[0], updir[2], -updir[1]
|
||||
};
|
||||
|
||||
alListener3f(AL_POSITION, pos[0], pos[2], -pos[1]);
|
||||
alListenerfv(AL_ORIENTATION, orient);
|
||||
throwALerror();
|
||||
}
|
||||
|
||||
|
||||
OpenAL_Output::OpenAL_Output(SoundManager &mgr)
|
||||
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0),
|
||||
mStreamThread(new StreamThread)
|
||||
{
|
||||
}
|
||||
|
||||
OpenAL_Output::~OpenAL_Output()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
}
|
71
apps/openmw/mwsound/openal_output.hpp
Normal file
71
apps/openmw/mwsound/openal_output.hpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
#ifndef GAME_SOUND_OPENAL_OUTPUT_H
|
||||
#define GAME_SOUND_OPENAL_OUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <deque>
|
||||
|
||||
#include "alc.h"
|
||||
#include "al.h"
|
||||
|
||||
#include "sound_output.hpp"
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
class Sound;
|
||||
|
||||
class OpenAL_Output : public Sound_Output
|
||||
{
|
||||
ALCdevice *mDevice;
|
||||
ALCcontext *mContext;
|
||||
|
||||
typedef std::deque<ALuint> IDDq;
|
||||
IDDq mFreeSources;
|
||||
IDDq mUnusedBuffers;
|
||||
|
||||
typedef std::map<std::string,ALuint> NameMap;
|
||||
NameMap mBufferCache;
|
||||
|
||||
typedef std::map<ALuint,ALuint> IDRefMap;
|
||||
IDRefMap mBufferRefs;
|
||||
|
||||
uint64_t mBufferCacheMemSize;
|
||||
|
||||
ALuint getBuffer(const std::string &fname);
|
||||
void bufferFinished(ALuint buffer);
|
||||
|
||||
virtual std::vector<std::string> enumerate();
|
||||
virtual void init(const std::string &devname="");
|
||||
virtual void deinit();
|
||||
|
||||
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, bool loop);
|
||||
virtual SoundPtr playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
||||
float min, float max, bool loop);
|
||||
|
||||
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch);
|
||||
virtual SoundPtr streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
||||
float min, float max);
|
||||
|
||||
virtual void updateListener(const float *pos, const float *atdir, const float *updir);
|
||||
|
||||
OpenAL_Output& operator=(const OpenAL_Output &rhs);
|
||||
OpenAL_Output(const OpenAL_Output &rhs);
|
||||
|
||||
OpenAL_Output(SoundManager &mgr);
|
||||
virtual ~OpenAL_Output();
|
||||
|
||||
class StreamThread;
|
||||
std::auto_ptr<StreamThread> mStreamThread;
|
||||
|
||||
friend class OpenAL_Sound;
|
||||
friend class OpenAL_SoundStream;
|
||||
friend class SoundManager;
|
||||
};
|
||||
#ifndef DEFAULT_OUTPUT
|
||||
#define DEFAULT_OUTPUT (::MWSound::OpenAL_Output)
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
36
apps/openmw/mwsound/sound.hpp
Normal file
36
apps/openmw/mwsound/sound.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef GAME_SOUND_SOUND_H
|
||||
#define GAME_SOUND_SOUND_H
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class Sound
|
||||
{
|
||||
virtual void update(const float *pos) = 0;
|
||||
|
||||
Sound& operator=(const Sound &rhs);
|
||||
Sound(const Sound &rhs);
|
||||
|
||||
protected:
|
||||
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
|
||||
float mBaseVolume;
|
||||
float mMinDistance;
|
||||
float mMaxDistance;
|
||||
|
||||
public:
|
||||
virtual void stop() = 0;
|
||||
virtual bool isPlaying() = 0;
|
||||
virtual void setVolume(float volume) = 0;
|
||||
|
||||
Sound() : mVolume(1.0f)
|
||||
, mBaseVolume(1.0f)
|
||||
, mMinDistance(20.0f) /* 1 * min_range_scale */
|
||||
, mMaxDistance(12750.0f) /* 255 * max_range_scale */
|
||||
{ }
|
||||
virtual ~Sound() { }
|
||||
|
||||
friend class OpenAL_Output;
|
||||
friend class SoundManager;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
48
apps/openmw/mwsound/sound_decoder.hpp
Normal file
48
apps/openmw/mwsound/sound_decoder.hpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef GAME_SOUND_SOUND_DECODER_H
|
||||
#define GAME_SOUND_SOUND_DECODER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <OgreResourceGroupManager.h>
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
enum SampleType {
|
||||
SampleType_UInt8,
|
||||
SampleType_Int16
|
||||
};
|
||||
const char *getSampleTypeName(SampleType type);
|
||||
|
||||
enum ChannelConfig {
|
||||
ChannelConfig_Mono,
|
||||
ChannelConfig_Stereo
|
||||
};
|
||||
const char *getChannelConfigName(ChannelConfig config);
|
||||
|
||||
size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type);
|
||||
size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type);
|
||||
|
||||
struct Sound_Decoder
|
||||
{
|
||||
Ogre::ResourceGroupManager &mResourceMgr;
|
||||
|
||||
virtual void open(const std::string &fname) = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0;
|
||||
|
||||
virtual size_t read(char *buffer, size_t bytes) = 0;
|
||||
virtual void readAll(std::vector<char> &output);
|
||||
virtual void rewind() = 0;
|
||||
|
||||
Sound_Decoder() : mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
|
||||
{ }
|
||||
virtual ~Sound_Decoder() { }
|
||||
|
||||
private:
|
||||
Sound_Decoder(const Sound_Decoder &rhs);
|
||||
Sound_Decoder& operator=(const Sound_Decoder &rhs);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
46
apps/openmw/mwsound/sound_output.hpp
Normal file
46
apps/openmw/mwsound/sound_output.hpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#ifndef GAME_SOUND_SOUND_OUTPUT_H
|
||||
#define GAME_SOUND_SOUND_OUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "soundmanager.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
class SoundManager;
|
||||
class Sound_Decoder;
|
||||
class Sound;
|
||||
|
||||
class Sound_Output
|
||||
{
|
||||
SoundManager &mManager;
|
||||
|
||||
virtual std::vector<std::string> enumerate() = 0;
|
||||
virtual void init(const std::string &devname="") = 0;
|
||||
virtual void deinit() = 0;
|
||||
|
||||
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, bool loop) = 0;
|
||||
virtual SoundPtr playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
||||
float min, float max, bool loop) = 0;
|
||||
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch) = 0;
|
||||
virtual SoundPtr streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
||||
float min, float max) = 0;
|
||||
|
||||
virtual void updateListener(const float *pos, const float *atdir, const float *updir) = 0;
|
||||
|
||||
Sound_Output& operator=(const Sound_Output &rhs);
|
||||
Sound_Output(const Sound_Output &rhs);
|
||||
|
||||
Sound_Output(SoundManager &mgr) : mManager(mgr) { }
|
||||
public:
|
||||
virtual ~Sound_Output() { }
|
||||
|
||||
friend class OpenAL_Output;
|
||||
friend class SoundManager;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -6,111 +6,78 @@
|
|||
|
||||
#include <OgreRoot.h>
|
||||
|
||||
#include <openengine/sound/sndmanager.hpp>
|
||||
#include <mangle/sound/clients/ogre_listener_mover.hpp>
|
||||
#include <mangle/sound/clients/ogre_output_updater.hpp>
|
||||
|
||||
#include <components/esm_store/store.hpp>
|
||||
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
/* Set up the sound manager to use Audiere, FFMPEG or
|
||||
MPG123/libsndfile for input. The OPENMW_USE_x macros are set in
|
||||
CMakeLists.txt.
|
||||
*/
|
||||
#ifdef OPENMW_USE_AUDIERE
|
||||
#include <mangle/sound/filters/openal_audiere.hpp>
|
||||
#define SOUND_FACTORY OpenAL_Audiere_Factory
|
||||
#define SOUND_OUT "OpenAL"
|
||||
#define SOUND_IN "Audiere"
|
||||
#endif
|
||||
#include "sound_output.hpp"
|
||||
#include "sound_decoder.hpp"
|
||||
#include "sound.hpp"
|
||||
|
||||
#ifdef OPENMW_USE_FFMPEG
|
||||
#include <mangle/sound/filters/openal_ffmpeg.hpp>
|
||||
#define SOUND_FACTORY OpenAL_FFMpeg_Factory
|
||||
#include "openal_output.hpp"
|
||||
#define SOUND_OUT "OpenAL"
|
||||
/* Set up the sound manager to use FFMPEG or MPG123+libsndfile for input. The
|
||||
* OPENMW_USE_x macros are set in CMakeLists.txt.
|
||||
*/
|
||||
#ifdef OPENMW_USE_FFMPEG
|
||||
#include "ffmpeg_decoder.hpp"
|
||||
#ifndef SOUND_IN
|
||||
#define SOUND_IN "FFmpeg"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef OPENMW_USE_MPG123
|
||||
#include <mangle/sound/filters/openal_sndfile_mpg123.hpp>
|
||||
#define SOUND_FACTORY OpenAL_SndFile_Mpg123_Factory
|
||||
#define SOUND_OUT "OpenAL"
|
||||
#include "mpgsnd_decoder.hpp"
|
||||
#ifndef SOUND_IN
|
||||
#define SOUND_IN "mpg123,sndfile"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace Mangle::Sound;
|
||||
typedef OEngine::Sound::SoundManager OEManager;
|
||||
|
||||
// Set the position on a sound based on a Ptr.
|
||||
static void setPos(SoundPtr &snd, const MWWorld::Ptr ref)
|
||||
{
|
||||
// Get sound position from the reference
|
||||
const float *pos = ref.getCellRef().pos.pos;
|
||||
|
||||
// Move the sound, converting from MW coordinates to Ogre
|
||||
// coordinates.
|
||||
snd->setPos(pos[0], pos[2], -pos[1]);
|
||||
}
|
||||
|
||||
namespace MWSound
|
||||
{
|
||||
|
||||
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
|
||||
const Files::PathContainer& dataDirs,
|
||||
bool useSound, bool fsstrict, MWWorld::Environment& environment)
|
||||
: mFSStrict(fsstrict)
|
||||
SoundManager::SoundManager(bool useSound, MWWorld::Environment& environment)
|
||||
: mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
|
||||
, mEnvironment(environment)
|
||||
, mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
|
||||
, updater(mgr)
|
||||
, cameraTracker(mgr)
|
||||
, mCurrentPlaylist(NULL)
|
||||
, mUsingSound(useSound)
|
||||
{
|
||||
if(useSound)
|
||||
if(!useSound)
|
||||
return;
|
||||
|
||||
std::cout << "Sound output: " << SOUND_OUT << std::endl;
|
||||
std::cout << "Sound decoder: " << SOUND_IN << std::endl;
|
||||
|
||||
try
|
||||
{
|
||||
// The music library will accept these filetypes
|
||||
// If none is given then it will accept all filetypes
|
||||
std::vector<std::string> acceptableExtensions;
|
||||
acceptableExtensions.push_back(".mp3");
|
||||
acceptableExtensions.push_back(".wav");
|
||||
acceptableExtensions.push_back(".ogg");
|
||||
acceptableExtensions.push_back(".flac");
|
||||
mOutput.reset(new DEFAULT_OUTPUT(*this));
|
||||
|
||||
// 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)
|
||||
{
|
||||
Files::FileLister(*it / std::string("Sound"), mSoundFiles, true);
|
||||
}
|
||||
std::vector<std::string> names = mOutput->enumerate();
|
||||
std::cout <<"Enumerated output devices:"<< std::endl;
|
||||
for(size_t i = 0;i < names.size();i++)
|
||||
std::cout <<" "<<names[i]<< std::endl;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
cameraTracker.followCamera(camera);
|
||||
|
||||
// Tell Ogre to update the sound system each frame
|
||||
root->addFrameListener(&updater);
|
||||
mOutput->init();
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout <<"Sound init failed: "<<e.what()<< std::endl;
|
||||
mOutput.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager()
|
||||
{
|
||||
if(mUsingSound)
|
||||
{
|
||||
Ogre::Root::getSingleton().removeFrameListener(&updater);
|
||||
cameraTracker.unfollowCamera();
|
||||
}
|
||||
mActiveSounds.clear();
|
||||
mMusic.reset();
|
||||
mOutput.reset();
|
||||
}
|
||||
|
||||
// Return a new decoder instance, used as needed by the output implementations
|
||||
DecoderPtr SoundManager::getDecoder()
|
||||
{
|
||||
return DecoderPtr(new DEFAULT_DECODER);
|
||||
}
|
||||
|
||||
// Convert a soundId to file name, and modify the volume
|
||||
|
@ -119,418 +86,408 @@ namespace MWSound
|
|||
std::string SoundManager::lookup(const std::string &soundId,
|
||||
float &volume, float &min, float &max)
|
||||
{
|
||||
const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId);
|
||||
if(snd == NULL) return "";
|
||||
const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId);
|
||||
if(snd == NULL)
|
||||
throw std::runtime_error(std::string("Failed to lookup sound ")+soundId);
|
||||
|
||||
if(snd->data.volume == 0)
|
||||
volume = 0.0f;
|
||||
else
|
||||
volume *= pow(10.0, (snd->data.volume/255.0f*3348.0 - 3348.0) / 2000.0);
|
||||
if(snd->data.volume == 0)
|
||||
volume = 0.0f;
|
||||
else
|
||||
volume *= pow(10.0, (snd->data.volume/255.0f*3348.0 - 3348.0) / 2000.0);
|
||||
|
||||
if(snd->data.minRange == 0 && snd->data.maxRange == 0)
|
||||
{
|
||||
min = 100.0f;
|
||||
max = 2000.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
min = snd->data.minRange * 20.0f;
|
||||
max = snd->data.maxRange * 50.0f;
|
||||
min = std::max(min, 1.0f);
|
||||
max = std::max(min, max);
|
||||
}
|
||||
|
||||
return Files::FileListLocator(mSoundFiles, snd->sound, mFSStrict, false);
|
||||
}
|
||||
|
||||
// Add a sound to the list and play it
|
||||
void SoundManager::add(const std::string &file,
|
||||
MWWorld::Ptr ptr,
|
||||
const std::string &id,
|
||||
float volume, float pitch,
|
||||
float min, float max,
|
||||
bool loop, bool untracked)
|
||||
{
|
||||
try
|
||||
if(snd->data.minRange == 0 && snd->data.maxRange == 0)
|
||||
{
|
||||
SoundPtr snd = mgr->load(file);
|
||||
snd->setRepeat(loop);
|
||||
snd->setVolume(volume);
|
||||
snd->setPitch(pitch);
|
||||
snd->setRange(min,max);
|
||||
setPos(snd, ptr);
|
||||
snd->play();
|
||||
|
||||
if (!untracked)
|
||||
{
|
||||
sounds[ptr][id] = WSoundPtr(snd);
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cout << "Error loading " << file << ", skipping.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Clears all the sub-elements of a given iterator, and then
|
||||
// removes it from 'sounds'.
|
||||
void SoundManager::clearAll(PtrMap::iterator& it)
|
||||
{
|
||||
IDMap::iterator sit = it->second.begin();
|
||||
|
||||
while(sit != it->second.end())
|
||||
{
|
||||
// Get sound pointer, if any
|
||||
SoundPtr snd = sit->second.lock();
|
||||
|
||||
// Stop the sound
|
||||
if(snd) snd->stop();
|
||||
|
||||
sit++;
|
||||
}
|
||||
|
||||
// Remove the ptr reference
|
||||
sounds.erase(it);
|
||||
}
|
||||
|
||||
// Stop a sound and remove it from the list. If id="" then
|
||||
// remove the entire object and stop all its sounds.
|
||||
void SoundManager::remove(MWWorld::Ptr ptr, const std::string &id)
|
||||
{
|
||||
PtrMap::iterator it = sounds.find(ptr);
|
||||
if(it != sounds.end())
|
||||
{
|
||||
if(id == "")
|
||||
// Kill all references to 'ptr'
|
||||
clearAll(it);
|
||||
else
|
||||
{
|
||||
// Only find the id we're looking for
|
||||
IDMap::iterator it2 = it->second.find(id);
|
||||
if(it2 != it->second.end())
|
||||
{
|
||||
// Stop the sound and remove it from the list
|
||||
SoundPtr snd = it2->second.lock();
|
||||
if(snd) snd->stop();
|
||||
it->second.erase(it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
|
||||
{
|
||||
PtrMap::const_iterator it = sounds.find(ptr);
|
||||
if(it != sounds.end())
|
||||
{
|
||||
IDMap::const_iterator it2 = it->second.find(id);
|
||||
if(it2 != it->second.end())
|
||||
{
|
||||
// Get a shared_ptr from the weak_ptr
|
||||
SoundPtr snd = it2->second.lock();;
|
||||
|
||||
// Is it still alive?
|
||||
if(snd)
|
||||
{
|
||||
// Then return its status!
|
||||
return snd->isPlaying();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Nothing found, sound is not playing
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all references to objects belonging to a given cell
|
||||
void SoundManager::removeCell(const MWWorld::Ptr::CellStore *cell)
|
||||
{
|
||||
PtrMap::iterator it2, it = sounds.begin();
|
||||
while(it != sounds.end())
|
||||
{
|
||||
// Make sure to increase the iterator before we erase it.
|
||||
it2 = it++;
|
||||
if(it2->first.getCell() == cell)
|
||||
clearAll(it2);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::updatePositions(MWWorld::Ptr ptr)
|
||||
{
|
||||
// Find the reference (if any)
|
||||
PtrMap::iterator it = sounds.find(ptr);
|
||||
if(it != sounds.end())
|
||||
{
|
||||
// Then find all sounds in it (if any)
|
||||
IDMap::iterator it2 = it->second.begin();
|
||||
for(;it2 != it->second.end(); it2++)
|
||||
{
|
||||
// Get the sound (if it still exists)
|
||||
SoundPtr snd = it2->second.lock();
|
||||
if(snd)
|
||||
// Update position
|
||||
setPos(snd, ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::stopMusic()
|
||||
{
|
||||
if (music)
|
||||
music->stop();
|
||||
setPlaylist();
|
||||
}
|
||||
|
||||
|
||||
void SoundManager::streamMusicFull(const std::string& filename)
|
||||
{
|
||||
// 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 (music)
|
||||
music->stop();
|
||||
music = mgr->load(filename);
|
||||
music->setStreaming(true);
|
||||
music->setVolume(0.4);
|
||||
music->play();
|
||||
|
||||
}
|
||||
|
||||
void SoundManager::streamMusic(const std::string& filename)
|
||||
{
|
||||
std::string filePath = mMusicLibrary.locate(filename, mFSStrict, true).string();
|
||||
if(!filePath.empty())
|
||||
{
|
||||
streamMusicFull(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::startRandomTitle()
|
||||
{
|
||||
if(mCurrentPlaylist && !mCurrentPlaylist->empty())
|
||||
{
|
||||
Files::PathContainer::const_iterator fileIter = mCurrentPlaylist->begin();
|
||||
srand( time(NULL) );
|
||||
int r = rand() % mCurrentPlaylist->size() + 1; //old random code
|
||||
|
||||
std::advance(fileIter, r - 1);
|
||||
std::string music = fileIter->string();
|
||||
std::cout << "Playing " << music << "\n";
|
||||
|
||||
try
|
||||
{
|
||||
streamMusicFull(music);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::cout << " Music Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundManager::isMusicPlaying()
|
||||
{
|
||||
bool test = false;
|
||||
if(music)
|
||||
{
|
||||
test = music->isPlaying();
|
||||
}
|
||||
return test;
|
||||
}
|
||||
|
||||
bool SoundManager::setPlaylist(std::string playlist)
|
||||
{
|
||||
const Files::PathContainer* previousPlaylist;
|
||||
previousPlaylist = mCurrentPlaylist;
|
||||
if (playlist == "")
|
||||
{
|
||||
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
|
||||
}
|
||||
else if(mMusicLibrary.containsSection(playlist, mFSStrict))
|
||||
{
|
||||
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
|
||||
min = 100.0f;
|
||||
max = 2000.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Warning: playlist named " << playlist << " does not exist.\n";
|
||||
min = snd->data.minRange * 20.0f;
|
||||
max = snd->data.maxRange * 50.0f;
|
||||
min = std::max(min, 1.0f);
|
||||
max = std::max(min, max);
|
||||
}
|
||||
return previousPlaylist == mCurrentPlaylist;
|
||||
|
||||
return std::string("Sound/")+snd->sound;
|
||||
}
|
||||
|
||||
void SoundManager::playPlaylist(std::string playlist)
|
||||
|
||||
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
|
||||
{
|
||||
if (!mUsingSound)
|
||||
SoundMap::const_iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
if(snditer->second.first == ptr && snditer->second.second == id)
|
||||
return snditer->first->isPlaying();
|
||||
snditer++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SoundManager::stopMusic()
|
||||
{
|
||||
if(mMusic)
|
||||
mMusic->stop();
|
||||
mMusic.reset();
|
||||
}
|
||||
|
||||
void SoundManager::streamMusicFull(const std::string& filename)
|
||||
{
|
||||
std::cout <<"Playing "<<filename<< std::endl;
|
||||
try
|
||||
{
|
||||
if(mMusic)
|
||||
mMusic->stop();
|
||||
mMusic = mOutput->streamSound(filename, 0.4f, 1.0f);
|
||||
mMusic->mBaseVolume = 0.4f;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << "Music Error: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::streamMusic(const std::string& filename)
|
||||
{
|
||||
streamMusicFull("Music/"+filename);
|
||||
}
|
||||
|
||||
void SoundManager::startRandomTitle()
|
||||
{
|
||||
Ogre::StringVectorPtr filelist;
|
||||
filelist = mResourceMgr.findResourceNames(Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"Music/"+mCurrentPlaylist+"/*");
|
||||
if(!filelist->size())
|
||||
return;
|
||||
|
||||
if (playlist == "")
|
||||
{
|
||||
if(!isMusicPlaying())
|
||||
{
|
||||
startRandomTitle();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!setPlaylist(playlist))
|
||||
{
|
||||
startRandomTitle();
|
||||
}
|
||||
else if (!isMusicPlaying())
|
||||
{
|
||||
startRandomTitle();
|
||||
}
|
||||
int i = rand()%filelist->size();
|
||||
streamMusicFull((*filelist)[i]);
|
||||
}
|
||||
|
||||
void SoundManager::say (MWWorld::Ptr ptr, const std::string& filename)
|
||||
{
|
||||
if (!mUsingSound)
|
||||
return;
|
||||
|
||||
// The range values are not tested
|
||||
std::string filePath = Files::FileListLocator(mSoundFiles, filename, mFSStrict, true);
|
||||
if(!filePath.empty())
|
||||
add(filePath, 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
|
||||
{
|
||||
return !isPlaying(ptr, "_say_sound");
|
||||
}
|
||||
|
||||
|
||||
void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop)
|
||||
{
|
||||
float min, max;
|
||||
const std::string &file = lookup(soundId, volume, min, max);
|
||||
if (file != "")
|
||||
bool SoundManager::isMusicPlaying()
|
||||
{
|
||||
SoundPtr snd = mgr->load(file);
|
||||
snd->setRepeat(loop);
|
||||
snd->setVolume(volume);
|
||||
snd->setRange(min,max);
|
||||
snd->setPitch(pitch);
|
||||
snd->setRelative(true);
|
||||
snd->play();
|
||||
return mMusic && mMusic->isPlaying();
|
||||
}
|
||||
|
||||
if (loop)
|
||||
void SoundManager::playPlaylist(const std::string &playlist)
|
||||
{
|
||||
mCurrentPlaylist = playlist;
|
||||
startRandomTitle();
|
||||
}
|
||||
|
||||
void SoundManager::say(MWWorld::Ptr ptr, const std::string& filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Only add the looping sound once
|
||||
IDMap::iterator it = mLoopedSounds.find(soundId);
|
||||
if(it == mLoopedSounds.end())
|
||||
{
|
||||
mLoopedSounds[soundId] = WSoundPtr(snd);
|
||||
}
|
||||
// The range values are not tested
|
||||
float basevol = 1.0f; /* TODO: volume settings */
|
||||
std::string filePath = std::string("Sound/")+filename;
|
||||
const ESM::Position &pos = ptr.getCellRef().pos;
|
||||
|
||||
SoundPtr sound = mOutput->playSound3D(filePath, pos.pos, basevol, 1.0f,
|
||||
20.0f, 12750.0f, false);
|
||||
sound->mBaseVolume = basevol;
|
||||
|
||||
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::playSound3D (MWWorld::Ptr ptr, const std::string& soundId,
|
||||
float volume, float pitch, bool loop, bool untracked)
|
||||
{
|
||||
// Look up the sound in the ESM data
|
||||
float min, max;
|
||||
const std::string &file = lookup(soundId, volume, min, max);
|
||||
if (file != "")
|
||||
add(file, ptr, soundId, volume, pitch, min, max, loop, untracked);
|
||||
}
|
||||
bool SoundManager::sayDone(MWWorld::Ptr ptr) const
|
||||
{
|
||||
return !isPlaying(ptr, "_say_sound");
|
||||
}
|
||||
|
||||
void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId)
|
||||
{
|
||||
remove(ptr, soundId);
|
||||
}
|
||||
|
||||
void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell)
|
||||
{
|
||||
removeCell(cell);
|
||||
}
|
||||
SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop)
|
||||
{
|
||||
SoundPtr sound;
|
||||
try
|
||||
{
|
||||
float basevol = 1.0f; /* TODO: volume settings */
|
||||
float min, max;
|
||||
std::string file = lookup(soundId, basevol, min, max);
|
||||
|
||||
sound = mOutput->playSound(file, volume*basevol, pitch, loop);
|
||||
sound->mVolume = volume;
|
||||
sound->mBaseVolume = basevol;
|
||||
sound->mMinDistance = min;
|
||||
sound->mMaxDistance = max;
|
||||
|
||||
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||
}
|
||||
return sound;
|
||||
}
|
||||
|
||||
SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
|
||||
float volume, float pitch, bool loop,
|
||||
bool untracked)
|
||||
{
|
||||
SoundPtr sound;
|
||||
try
|
||||
{
|
||||
// Look up the sound in the ESM data
|
||||
float basevol = 1.0f; /* TODO: volume settings */
|
||||
float min, max;
|
||||
std::string file = lookup(soundId, basevol, min, max);
|
||||
const ESM::Position &pos = ptr.getCellRef().pos;
|
||||
|
||||
sound = mOutput->playSound3D(file, pos.pos, volume*basevol, pitch, min, max, loop);
|
||||
sound->mVolume = volume;
|
||||
sound->mBaseVolume = basevol;
|
||||
sound->mMinDistance = min;
|
||||
sound->mMaxDistance = max;
|
||||
|
||||
mActiveSounds[sound] = (!untracked ? std::make_pair(ptr, soundId) :
|
||||
std::make_pair(MWWorld::Ptr(), soundId));
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||
}
|
||||
return sound;
|
||||
}
|
||||
|
||||
void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId)
|
||||
{
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
if(snditer->second.first == ptr && snditer->second.second == soundId)
|
||||
{
|
||||
snditer->first->stop();
|
||||
mActiveSounds.erase(snditer++);
|
||||
}
|
||||
else
|
||||
snditer++;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::stopSound3D(MWWorld::Ptr ptr)
|
||||
{
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
if(snditer->second.first == ptr)
|
||||
{
|
||||
snditer->first->stop();
|
||||
mActiveSounds.erase(snditer++);
|
||||
}
|
||||
else
|
||||
snditer++;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::stopSound(const MWWorld::Ptr::CellStore *cell)
|
||||
{
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
if(snditer->second.first != MWWorld::Ptr() &&
|
||||
snditer->second.first.getCell() == cell)
|
||||
{
|
||||
snditer->first->stop();
|
||||
mActiveSounds.erase(snditer++);
|
||||
}
|
||||
else
|
||||
snditer++;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::stopSound(const std::string& soundId)
|
||||
{
|
||||
IDMap::iterator it = mLoopedSounds.find(soundId);
|
||||
if(it != mLoopedSounds.end())
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
SoundPtr snd = it->second.lock();
|
||||
if(snd) snd->stop();
|
||||
mLoopedSounds.erase(it);
|
||||
if(snditer->second.first == MWWorld::Ptr() &&
|
||||
snditer->second.second == soundId)
|
||||
{
|
||||
snditer->first->stop();
|
||||
mActiveSounds.erase(snditer++);
|
||||
}
|
||||
else
|
||||
snditer++;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
bool SoundManager::getSoundPlaying(MWWorld::Ptr ptr, const std::string& soundId) const
|
||||
{
|
||||
return isPlaying(ptr, soundId);
|
||||
}
|
||||
|
||||
return isPlaying(ptr, soundId);
|
||||
}
|
||||
void SoundManager::updateObject(MWWorld::Ptr ptr)
|
||||
{
|
||||
const ESM::Position &pos = ptr.getCellRef().pos;
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
if(snditer->second.first == ptr)
|
||||
snditer->first->update(pos.pos);
|
||||
snditer++;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::updateObject(MWWorld::Ptr ptr)
|
||||
{
|
||||
updatePositions(ptr);
|
||||
}
|
||||
|
||||
void SoundManager::update (float duration)
|
||||
{
|
||||
void SoundManager::updateRegionSound(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) && timePassed >= 10)
|
||||
timePassed += duration;
|
||||
if((current->cell->data.flags & current->cell->Interior) || timePassed < 10)
|
||||
return;
|
||||
timePassed = 0;
|
||||
|
||||
if(regionName != 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;
|
||||
}
|
||||
|
||||
if(test.soundList.size() > 0)
|
||||
{
|
||||
std::vector<ESM::Region::SoundRef>::iterator soundIter = test.soundList.begin();
|
||||
//mEnvironment.mSoundManager
|
||||
if(total == 0)
|
||||
{
|
||||
while (soundIter != test.soundList.end())
|
||||
{
|
||||
int chance = (int) soundIter->chance;
|
||||
//ESM::NAME32 go = soundIter->sound;
|
||||
//std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
|
||||
soundIter++;
|
||||
total += chance;
|
||||
}
|
||||
}
|
||||
|
||||
int r = rand() % total; //old random code
|
||||
int pos = 0;
|
||||
soundIter = test.soundList.begin();
|
||||
while (soundIter != test.soundList.end())
|
||||
{
|
||||
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 <<" Chance:" << chance << "\n";
|
||||
mEnvironment.mSoundManager->playSound(go, 20.0, 1.0);
|
||||
|
||||
break;
|
||||
}
|
||||
pos += chance;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current->cell->data.flags & current->cell->Interior)
|
||||
{
|
||||
regionName = "";
|
||||
regionName = current->cell->region;
|
||||
total = 0;
|
||||
}
|
||||
|
||||
}
|
||||
const ESM::Region *regn = mEnvironment.mWorld->getStore().regions.find(regionName);
|
||||
std::vector<ESM::Region::SoundRef>::const_iterator soundIter;
|
||||
if(total == 0)
|
||||
{
|
||||
soundIter = regn->soundList.begin();
|
||||
while(soundIter != regn->soundList.end())
|
||||
{
|
||||
total += (int)soundIter->chance;
|
||||
soundIter++;
|
||||
}
|
||||
if(total == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
int r = (int)(rand()/((double)RAND_MAX+1) * total);
|
||||
int pos = 0;
|
||||
|
||||
soundIter = regn->soundList.begin();
|
||||
while(soundIter != regn->soundList.end())
|
||||
{
|
||||
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 <<" Chance:" << chance << "\n";
|
||||
playSound(go, 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
pos += chance;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::updateSounds(float duration)
|
||||
{
|
||||
static float timePassed = 0.0;
|
||||
|
||||
timePassed += duration;
|
||||
if(timePassed < (1.0f/30.0f))
|
||||
return;
|
||||
timePassed = 0.0f;
|
||||
|
||||
// Make sure music is still playing
|
||||
if(!isMusicPlaying())
|
||||
startRandomTitle();
|
||||
|
||||
Ogre::Camera *cam = mEnvironment.mWorld->getPlayer().getRenderer()->getCamera();
|
||||
Ogre::Vector3 nPos, nDir, nUp;
|
||||
nPos = cam->getRealPosition();
|
||||
nDir = cam->getRealDirection();
|
||||
nUp = cam->getRealUp();
|
||||
|
||||
// The output handler is expecting vectors oriented like the game
|
||||
// (that is, -Z goes down, +Y goes forward), but that's not what we
|
||||
// get from Ogre's camera, so we have to convert.
|
||||
float pos[3] = { nPos[0], -nPos[2], nPos[1] };
|
||||
float at[3] = { nDir[0], -nDir[2], nDir[1] };
|
||||
float up[3] = { nUp[0], -nUp[2], nUp[1] };
|
||||
mOutput->updateListener(pos, at, up);
|
||||
|
||||
// Check if any sounds are finished playing, and trash them
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
if(!snditer->first->isPlaying())
|
||||
mActiveSounds.erase(snditer++);
|
||||
else
|
||||
snditer++;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::update(float duration)
|
||||
{
|
||||
updateSounds(duration);
|
||||
updateRegionSound(duration);
|
||||
}
|
||||
|
||||
|
||||
// Default readAll implementation, for decoders that can't do anything
|
||||
// better
|
||||
void Sound_Decoder::readAll(std::vector<char> &output)
|
||||
{
|
||||
size_t total = output.size();
|
||||
size_t got;
|
||||
|
||||
output.resize(total+32768);
|
||||
while((got=read(&output[total], output.size()-total)) > 0)
|
||||
{
|
||||
total += got;
|
||||
output.resize(total*2);
|
||||
}
|
||||
output.resize(total);
|
||||
}
|
||||
|
||||
|
||||
const char *getSampleTypeName(SampleType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case SampleType_UInt8: return "U8";
|
||||
case SampleType_Int16: return "S16";
|
||||
}
|
||||
return "(unknown sample type)";
|
||||
}
|
||||
|
||||
const char *getChannelConfigName(ChannelConfig config)
|
||||
{
|
||||
switch(config)
|
||||
{
|
||||
case ChannelConfig_Mono: return "Mono";
|
||||
case ChannelConfig_Stereo: return "Stereo";
|
||||
}
|
||||
return "(unknown channel config)";
|
||||
}
|
||||
|
||||
size_t framesToBytes(size_t frames, ChannelConfig config, SampleType type)
|
||||
{
|
||||
switch(config)
|
||||
{
|
||||
case ChannelConfig_Mono: frames *= 1; break;
|
||||
case ChannelConfig_Stereo: frames *= 2; break;
|
||||
}
|
||||
switch(type)
|
||||
{
|
||||
case SampleType_UInt8: frames *= 1; break;
|
||||
case SampleType_Int16: frames *= 2; break;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
size_t bytesToFrames(size_t bytes, ChannelConfig config, SampleType type)
|
||||
{
|
||||
return bytes / framesToBytes(1, config, type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
#define GAME_SOUND_SOUNDMANAGER_H
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
|
||||
#include <mangle/sound/clients/ogre_output_updater.hpp>
|
||||
#include <mangle/sound/clients/ogre_listener_mover.hpp>
|
||||
|
||||
#include <openengine/sound/sndmanager.hpp>
|
||||
|
||||
#include <components/files/filelibrary.hpp>
|
||||
#include <OgreResourceGroupManager.h>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
|
@ -19,16 +16,6 @@ namespace Ogre
|
|||
class Camera;
|
||||
}
|
||||
|
||||
namespace Mangle
|
||||
{
|
||||
namespace Sound
|
||||
{
|
||||
typedef boost::shared_ptr<Sound> SoundPtr;
|
||||
}
|
||||
}
|
||||
|
||||
typedef OEngine::Sound::SoundManagerPtr OEManagerPtr;
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct Environment;
|
||||
|
@ -36,128 +23,97 @@ namespace MWWorld
|
|||
|
||||
namespace MWSound
|
||||
{
|
||||
class Sound_Output;
|
||||
struct Sound_Decoder;
|
||||
class Sound;
|
||||
|
||||
typedef boost::shared_ptr<Sound_Decoder> DecoderPtr;
|
||||
typedef boost::shared_ptr<Sound> SoundPtr;
|
||||
|
||||
class SoundManager
|
||||
{
|
||||
Ogre::ResourceGroupManager& mResourceMgr;
|
||||
|
||||
// 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;
|
||||
|
||||
MWWorld::Environment& mEnvironment;
|
||||
std::auto_ptr<Sound_Output> mOutput;
|
||||
|
||||
void streamMusicFull (const std::string& filename);
|
||||
///< Play a soundifle
|
||||
/// \param absolute filename
|
||||
boost::shared_ptr<Sound> mMusic;
|
||||
std::string mCurrentPlaylist;
|
||||
|
||||
/* 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;
|
||||
typedef std::pair<MWWorld::Ptr,std::string> PtrIDPair;
|
||||
typedef std::map<SoundPtr,PtrIDPair> SoundMap;
|
||||
SoundMap mActiveSounds;
|
||||
|
||||
/* This class calls update() on the sound manager each frame
|
||||
using and Ogre::FrameListener
|
||||
*/
|
||||
Mangle::Sound::OgreOutputUpdater updater;
|
||||
std::string lookup(const std::string &soundId,
|
||||
float &volume, float &min, float &max);
|
||||
void streamMusicFull(const std::string& filename);
|
||||
bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const;
|
||||
void updateSounds(float duration);
|
||||
void updateRegionSound(float duration);
|
||||
|
||||
/* This class tracks the movement of an Ogre::Camera and moves
|
||||
a sound listener automatically to follow it.
|
||||
*/
|
||||
Mangle::Sound::OgreListenerMover cameraTracker;
|
||||
SoundManager(const SoundManager &rhs);
|
||||
SoundManager& operator=(const SoundManager &rhs);
|
||||
|
||||
typedef std::map<std::string,Mangle::Sound::WSoundPtr> IDMap;
|
||||
typedef std::map<MWWorld::Ptr,IDMap> PtrMap;
|
||||
PtrMap sounds;
|
||||
protected:
|
||||
DecoderPtr getDecoder();
|
||||
friend class OpenAL_Output;
|
||||
|
||||
// A list of all sound files used to lookup paths
|
||||
Files::PathContainer mSoundFiles;
|
||||
public:
|
||||
SoundManager(bool useSound, MWWorld::Environment& environment);
|
||||
~SoundManager();
|
||||
|
||||
// A library of all Music file paths stored by the folder they are contained in
|
||||
Files::FileLibrary mMusicLibrary;
|
||||
void stopMusic();
|
||||
///< Stops music if it's playing
|
||||
|
||||
// Points to the current playlist of music files stored in the music library
|
||||
const Files::PathContainer* mCurrentPlaylist;
|
||||
void streamMusic(const std::string& filename);
|
||||
///< Play a soundifle
|
||||
/// \param filename name of a sound file in "Music/" in the data directory.
|
||||
|
||||
IDMap mLoopedSounds;
|
||||
void startRandomTitle();
|
||||
///< Starts a random track from the current playlist
|
||||
|
||||
bool mUsingSound;
|
||||
bool isMusicPlaying();
|
||||
///< Returns true if music is playing
|
||||
|
||||
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, bool untracked=false);
|
||||
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);
|
||||
void playPlaylist(const std::string &playlist);
|
||||
///< Start playing music from the selected folder
|
||||
/// \param name of the folder that contains the playlist
|
||||
|
||||
public:
|
||||
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.
|
||||
|
||||
SoundManager(Ogre::Root*, Ogre::Camera*,
|
||||
const Files::PathContainer& dataDir, bool useSound, bool fsstrict,
|
||||
MWWorld::Environment& environment);
|
||||
~SoundManager();
|
||||
bool sayDone(MWWorld::Ptr reference) const;
|
||||
///< Is actor not speaking?
|
||||
|
||||
void stopMusic();
|
||||
///< Stops music if it's playing
|
||||
SoundPtr playSound(const std::string& soundId, float volume, float pitch, bool loop=false);
|
||||
///< Play a sound, independently of 3D-position
|
||||
|
||||
void streamMusic(const std::string& filename);
|
||||
///< Play a soundifle
|
||||
/// \param filename name of a sound file in "Music/" in the data directory.
|
||||
SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId,
|
||||
float volume, float pitch, bool loop,
|
||||
bool untracked=false);
|
||||
///< Play a sound from an object
|
||||
|
||||
void startRandomTitle();
|
||||
///< Starts a random track from the current playlist
|
||||
void stopSound3D(MWWorld::Ptr reference, const std::string& soundId);
|
||||
///< Stop the given object from playing the given sound,
|
||||
|
||||
bool isMusicPlaying();
|
||||
///< Returns true if music is playing
|
||||
void stopSound3D(MWWorld::Ptr reference);
|
||||
///< Stop the given object from playing all sounds.
|
||||
|
||||
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 stopSound(const MWWorld::Ptr::CellStore *cell);
|
||||
///< Stop all sounds for the given cell.
|
||||
|
||||
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 stopSound(const std::string& soundId);
|
||||
///< Stop a non-3d looping sound
|
||||
|
||||
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.
|
||||
bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const;
|
||||
///< Is the given sound currently playing on the given object?
|
||||
|
||||
bool sayDone (MWWorld::Ptr reference) const;
|
||||
///< Is actor not speaking?
|
||||
void updateObject(MWWorld::Ptr reference);
|
||||
///< Update the position of all sounds connected to the given object.
|
||||
|
||||
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,
|
||||
float volume, float pitch, bool loop, bool untracked=false);
|
||||
///< Play a sound from an object
|
||||
|
||||
void stopSound3D (MWWorld::Ptr reference, const std::string& soundId = "");
|
||||
///< Stop the given object from playing the given sound, If no soundId is given,
|
||||
/// all sounds for this reference will stop.
|
||||
|
||||
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?
|
||||
|
||||
void updateObject(MWWorld::Ptr reference);
|
||||
///< Update the position of all sounds connected to the given object.
|
||||
|
||||
void update (float duration);
|
||||
void update(float duration);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "../mwsound/soundmanager.hpp"
|
||||
|
||||
#include "../mwgui/window_manager.hpp"
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "environment.hpp"
|
||||
#include "player.hpp"
|
||||
|
@ -105,7 +107,8 @@ namespace MWWorld
|
|||
insertCell(*cell, mEnvironment);
|
||||
mRendering.cellAdded(cell);
|
||||
mRendering.configureAmbient(*cell);
|
||||
|
||||
mRendering.requestMap(cell);
|
||||
mRendering.configureAmbient(*cell);
|
||||
}
|
||||
|
||||
|
||||
|
@ -121,10 +124,14 @@ namespace MWWorld
|
|||
// TODO orientation
|
||||
mEnvironment.mMechanicsManager->addActor (mWorld->getPlayer().getPlayer());
|
||||
mEnvironment.mMechanicsManager->watchActor (mWorld->getPlayer().getPlayer());
|
||||
|
||||
mEnvironment.mWindowManager->changeCell( mCurrentCell );
|
||||
}
|
||||
|
||||
void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos)
|
||||
{
|
||||
mRendering.preCellChange(mCurrentCell);
|
||||
|
||||
// remove active
|
||||
mEnvironment.mMechanicsManager->removeActor (mWorld->getPlayer().getPlayer());
|
||||
|
||||
|
|
|
@ -9,9 +9,23 @@
|
|||
|
||||
SET( FFMPEG_FOUND "NO" )
|
||||
|
||||
FIND_PATH( FFMPEG_general_INCLUDE_DIR libavcodec/avcodec.h libavformat/avformat.h
|
||||
HINTS
|
||||
PATHS
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/usr/include/ffmpeg
|
||||
/usr/local/include/ffmpeg
|
||||
/usr/include/ffmpeg/libavcodec
|
||||
/usr/local/include/ffmpeg/libavcodec
|
||||
/usr/include/libavcodec
|
||||
/usr/local/include/libavcodec
|
||||
)
|
||||
|
||||
FIND_PATH( FFMPEG_avcodec_INCLUDE_DIR avcodec.h
|
||||
HINTS
|
||||
PATHS
|
||||
${FFMPEG_general_INCLUDE_DIR}/libavcodec
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/usr/include/ffmpeg
|
||||
|
@ -25,6 +39,7 @@ FIND_PATH( FFMPEG_avcodec_INCLUDE_DIR avcodec.h
|
|||
FIND_PATH( FFMPEG_avformat_INCLUDE_DIR avformat.h
|
||||
HINTS
|
||||
PATHS
|
||||
${FFMPEG_general_INCLUDE_DIR}/libavformat
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/usr/include/ffmpeg
|
||||
|
@ -35,7 +50,7 @@ FIND_PATH( FFMPEG_avformat_INCLUDE_DIR avformat.h
|
|||
/usr/local/include/libavformat
|
||||
)
|
||||
|
||||
set(FFMPEG_INCLUDE_DIR ${FFMPEG_avcodec_INCLUDE_DIR} ${FFMPEG_avformat_INCLUDE_DIR})
|
||||
set(FFMPEG_INCLUDE_DIR ${FFMPEG_general_INCLUDE_DIR} ${FFMPEG_avcodec_INCLUDE_DIR} ${FFMPEG_avformat_INCLUDE_DIR})
|
||||
|
||||
IF( FFMPEG_INCLUDE_DIR )
|
||||
|
||||
|
|
|
@ -19,57 +19,57 @@ include(FindPkgMacros)
|
|||
|
||||
IF (WIN32) #Windows
|
||||
MESSAGE(STATUS "Looking for MyGUI")
|
||||
SET(MYGUISDK $ENV{MYGUI_HOME})
|
||||
SET(MYGUISDK $ENV{MYGUI_HOME})
|
||||
IF (MYGUISDK)
|
||||
findpkg_begin ( "MYGUI" )
|
||||
findpkg_begin ( "MYGUI" )
|
||||
MESSAGE(STATUS "Using MyGUI in OGRE SDK")
|
||||
STRING(REGEX REPLACE "[\\]" "/" MYGUISDK "${MYGUISDK}" )
|
||||
STRING(REGEX REPLACE "[\\]" "/" MYGUISDK "${MYGUISDK}" )
|
||||
|
||||
find_path ( MYGUI_INCLUDE_DIRS
|
||||
MyGUI.h
|
||||
"${MYGUISDK}/MyGUIEngine/include"
|
||||
NO_DEFAULT_PATH )
|
||||
|
||||
find_path ( MYGUI_PLATFORM_INCLUDE_DIRS
|
||||
MyGUI_OgrePlatform.h
|
||||
"${MYGUISDK}/Platforms/Ogre/OgrePlatform/include"
|
||||
NO_DEFAULT_PATH )
|
||||
find_path ( MYGUI_INCLUDE_DIRS
|
||||
MyGUI.h
|
||||
"${MYGUISDK}/MyGUIEngine/include"
|
||||
NO_DEFAULT_PATH )
|
||||
|
||||
SET ( MYGUI_LIB_DIR ${MYGUISDK}/*/lib )
|
||||
|
||||
find_library ( MYGUI_LIBRARIES_REL NAMES
|
||||
MyGUIEngine.lib
|
||||
MyGUI.OgrePlatform.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" release relwithdebinfo minsizerel )
|
||||
find_path ( MYGUI_PLATFORM_INCLUDE_DIRS
|
||||
MyGUI_OgrePlatform.h
|
||||
"${MYGUISDK}/Platforms/Ogre/OgrePlatform/include"
|
||||
NO_DEFAULT_PATH )
|
||||
|
||||
find_library ( MYGUI_LIBRARIES_DBG NAMES
|
||||
MyGUIEngine_d.lib
|
||||
MyGUI.OgrePlatform_d.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" debug )
|
||||
SET ( MYGUI_LIB_DIR ${MYGUISDK}/*/lib )
|
||||
|
||||
find_library ( MYGUI_PLATFORM_LIBRARIES_REL NAMES
|
||||
MyGUI.OgrePlatform.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" release relwithdebinfo minsizerel )
|
||||
find_library ( MYGUI_LIBRARIES_REL NAMES
|
||||
MyGUIEngine.lib
|
||||
MyGUI.OgrePlatform.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" release relwithdebinfo minsizerel )
|
||||
|
||||
find_library ( MYGUI_PLATFORM_LIBRARIES_DBG NAMES
|
||||
MyGUI.OgrePlatform_d.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" debug )
|
||||
|
||||
make_library_set ( MYGUI_LIBRARIES )
|
||||
make_library_set ( MYGUI_PLATFORM_LIBRARIES )
|
||||
|
||||
MESSAGE ("${MYGUI_LIBRARIES}")
|
||||
MESSAGE ("${MYGUI_PLATFORM_LIBRARIES}")
|
||||
find_library ( MYGUI_LIBRARIES_DBG NAMES
|
||||
MyGUIEngine_d.lib
|
||||
MyGUI.OgrePlatform_d.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" debug )
|
||||
|
||||
findpkg_finish ( "MYGUI" )
|
||||
find_library ( MYGUI_PLATFORM_LIBRARIES_REL NAMES
|
||||
MyGUI.OgrePlatform.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" release relwithdebinfo minsizerel )
|
||||
|
||||
find_library ( MYGUI_PLATFORM_LIBRARIES_DBG NAMES
|
||||
MyGUI.OgrePlatform_d.lib
|
||||
HINTS
|
||||
${MYGUI_LIB_DIR}
|
||||
PATH_SUFFIXES "" debug )
|
||||
|
||||
make_library_set ( MYGUI_LIBRARIES )
|
||||
make_library_set ( MYGUI_PLATFORM_LIBRARIES )
|
||||
|
||||
MESSAGE ("${MYGUI_LIBRARIES}")
|
||||
MESSAGE ("${MYGUI_PLATFORM_LIBRARIES}")
|
||||
|
||||
findpkg_finish ( "MYGUI" )
|
||||
|
||||
ENDIF (MYGUISDK)
|
||||
IF (OGRESOURCE)
|
||||
|
@ -87,9 +87,11 @@ ELSE (WIN32) #Unix
|
|||
SET(MYGUI_INCLUDE_DIRS ${MYGUI_INCLUDE_DIRS})
|
||||
SET(MYGUI_LIB_DIR ${MYGUI_LIBDIR})
|
||||
SET(MYGUI_LIBRARIES ${MYGUI_LIBRARIES} CACHE STRING "")
|
||||
SET(MYGUI_PLATFORM_LIBRARIES "MyGUI.OgrePlatform")
|
||||
ELSE (MYGUI_INCLUDE_DIRS)
|
||||
FIND_PATH(MYGUI_INCLUDE_DIRS MyGUI.h PATHS /usr/local/include /usr/include PATH_SUFFIXES MyGUI MYGUI)
|
||||
FIND_LIBRARY(MYGUI_LIBRARIES mygui PATHS /usr/lib /usr/local/lib)
|
||||
SET(MYGUI_PLATFORM_LIBRARIES "MyGUI.OgrePlatform")
|
||||
SET(MYGUI_LIB_DIR ${MYGUI_LIBRARIES})
|
||||
STRING(REGEX REPLACE "(.*)/.*" "\\1" MYGUI_LIB_DIR "${MYGUI_LIB_DIR}")
|
||||
STRING(REGEX REPLACE ".*/" "" MYGUI_LIBRARIES "${MYGUI_LIBRARIES}")
|
||||
|
@ -103,7 +105,7 @@ SEPARATE_ARGUMENTS(MYGUI_PLATFORM_LIBRARIES)
|
|||
|
||||
SET(MYGUI_INCLUDE_DIRS ${MYGUI_INCLUDE_DIRS} CACHE PATH "")
|
||||
SET(MYGUI_LIBRARIES ${MYGUI_LIBRARIES} CACHE STRING "")
|
||||
SET(MYGUI_LIBRARIES ${MYGUI_PLATFORM_LIBRARIES} CACHE STRING "")
|
||||
SET(MYGUI_PLATFORM_LIBRARIES ${MYGUI_PLATFORM_LIBRARIES} CACHE STRING "")
|
||||
SET(MYGUI_LIB_DIR ${MYGUI_LIB_DIR} CACHE PATH "")
|
||||
|
||||
IF (MYGUI_INCLUDE_DIRS AND MYGUI_LIBRARIES)
|
||||
|
@ -111,7 +113,7 @@ IF (MYGUI_INCLUDE_DIRS AND MYGUI_LIBRARIES)
|
|||
ENDIF (MYGUI_INCLUDE_DIRS AND MYGUI_LIBRARIES)
|
||||
|
||||
IF (MYGUI_FOUND)
|
||||
MARK_AS_ADVANCED(MYGUI_LIB_DIR)
|
||||
MARK_AS_ADVANCED(MYGUI_LIB_DIR)
|
||||
IF (NOT MYGUI_FIND_QUIETLY)
|
||||
MESSAGE(STATUS " libraries : ${MYGUI_LIBRARIES} from ${MYGUI_LIB_DIR}")
|
||||
MESSAGE(STATUS " includes : ${MYGUI_INCLUDE_DIRS}")
|
||||
|
@ -122,4 +124,4 @@ ELSE (MYGUI_FOUND)
|
|||
ENDIF (MYGUI_FIND_REQUIRED)
|
||||
ENDIF (MYGUI_FOUND)
|
||||
|
||||
CMAKE_POLICY(POP)
|
||||
CMAKE_POLICY(POP)
|
||||
|
|
|
@ -256,8 +256,12 @@ public:
|
|||
return DataStreamPtr(new Mangle2OgreStream(strm));
|
||||
}
|
||||
|
||||
bool exists(const String& filename) {
|
||||
return cexists(filename);
|
||||
}
|
||||
|
||||
// Check if the file exists.
|
||||
bool exists(const String& filename) {
|
||||
bool cexists(const String& filename) const {
|
||||
String passed = filename;
|
||||
if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<'
|
||||
|| filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':'
|
||||
|
@ -308,6 +312,29 @@ return arc.exists(passed.c_str());
|
|||
located in BSAs. So instead we channel it through exists() and
|
||||
set up a single-element result list if the file is found.
|
||||
*/
|
||||
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
|
||||
bool dirs = false) const
|
||||
{
|
||||
FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList());
|
||||
|
||||
// Check if the file exists (only works for single files - wild
|
||||
// cards and recursive search isn't implemented.)
|
||||
if(cexists(pattern))
|
||||
{
|
||||
FileInfo fi;
|
||||
fi.archive = this;
|
||||
fi.filename = pattern;
|
||||
// It apparently doesn't matter that we return bogus
|
||||
// information
|
||||
fi.path = "";
|
||||
fi.compressedSize = fi.uncompressedSize = 0;
|
||||
|
||||
ptr->push_back(fi);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
|
||||
bool dirs = false)
|
||||
{
|
||||
|
@ -315,7 +342,7 @@ return arc.exists(passed.c_str());
|
|||
|
||||
// Check if the file exists (only works for single files - wild
|
||||
// cards and recursive search isn't implemented.)
|
||||
if(exists(pattern))
|
||||
if(cexists(pattern))
|
||||
{
|
||||
FileInfo fi;
|
||||
fi.archive = this;
|
||||
|
|
|
@ -148,9 +148,9 @@ void BSAFile::readHeader()
|
|||
}
|
||||
|
||||
/// Get the index of a given file name, or -1 if not found
|
||||
int BSAFile::getIndex(const char *str)
|
||||
int BSAFile::getIndex(const char *str) const
|
||||
{
|
||||
Lookup::iterator it;
|
||||
Lookup::const_iterator it;
|
||||
it = lookup.find(str);
|
||||
|
||||
if(it == lookup.end()) return -1;
|
||||
|
|
|
@ -93,7 +93,7 @@ class BSAFile
|
|||
void readHeader();
|
||||
|
||||
/// Get the index of a given file name, or -1 if not found
|
||||
int getIndex(const char *str);
|
||||
int getIndex(const char *str) const;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -119,7 +119,7 @@ class BSAFile
|
|||
*/
|
||||
|
||||
/// Check if a file exists
|
||||
bool exists(const char *file) { return getIndex(file) != -1; }
|
||||
bool exists(const char *file) const { return getIndex(file) != -1; }
|
||||
|
||||
/** Open a file contained in the archive. Throws an exception if the
|
||||
file doesn't exist.
|
||||
|
|
|
@ -162,6 +162,15 @@ void NIFFile::parse()
|
|||
r->recName = rec;
|
||||
records[i] = r;
|
||||
r->read(this);
|
||||
|
||||
// Discard tranformations for the root node, otherwise some meshes
|
||||
// occasionally get wrong orientation. Only for NiNode-s for now, but
|
||||
// can be expanded if needed.
|
||||
// This should be rewritten when the method is cleaned up.
|
||||
if (0 == i && rec == "NiNode")
|
||||
{
|
||||
static_cast<Nif::Node*>(r)->trafo = Nif::Transformation::getIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
/* After the data, the nif contains an int N and then a list of N
|
||||
|
|
|
@ -55,10 +55,26 @@ struct Matrix
|
|||
|
||||
struct Transformation
|
||||
{
|
||||
Vector pos;
|
||||
Matrix rotation;
|
||||
float scale;
|
||||
Vector velocity;
|
||||
Vector pos;
|
||||
Matrix rotation;
|
||||
float scale;
|
||||
Vector velocity;
|
||||
|
||||
static const Transformation* getIdentity()
|
||||
{
|
||||
static Transformation identity;
|
||||
static bool iset = false;
|
||||
if (!iset)
|
||||
{
|
||||
identity.scale = 1.0f;
|
||||
identity.rotation.v[0].array[0] = 1.0f;
|
||||
identity.rotation.v[1].array[1] = 1.0f;
|
||||
identity.rotation.v[2].array[2] = 1.0f;
|
||||
iset = true;
|
||||
}
|
||||
|
||||
return &identity;
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
|
|
@ -51,7 +51,11 @@ using namespace Mangle::VFS;
|
|||
|
||||
using namespace NifBullet;
|
||||
|
||||
//====================================================================================================
|
||||
ManualBulletShapeLoader::~ManualBulletShapeLoader()
|
||||
{
|
||||
delete vfs;
|
||||
}
|
||||
|
||||
Ogre::Matrix3 ManualBulletShapeLoader::getMatrix(Nif::Transformation* tr)
|
||||
{
|
||||
Ogre::Matrix3 rot(tr->rotation.v[0].array[0],tr->rotation.v[0].array[1],tr->rotation.v[0].array[2],
|
||||
|
@ -135,7 +139,21 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource)
|
|||
handleNode(node,0,Ogre::Matrix3::IDENTITY,Ogre::Vector3::ZERO,1,hasCollisionNode,false,true);
|
||||
}
|
||||
|
||||
currentShape = new btBvhTriangleMeshShape(mTriMesh,true);
|
||||
struct TriangleMeshShape : public btBvhTriangleMeshShape
|
||||
{
|
||||
TriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression)
|
||||
: btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TriangleMeshShape()
|
||||
{
|
||||
delete getTriangleInfoMap();
|
||||
delete m_meshInterface;
|
||||
}
|
||||
};
|
||||
|
||||
currentShape = new TriangleMeshShape(mTriMesh,true);
|
||||
cShape->Shape = currentShape;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class ManualBulletShapeLoader : public BulletShapeLoader
|
|||
public:
|
||||
|
||||
ManualBulletShapeLoader():resourceGroup("General"){vfs = 0;}
|
||||
virtual ~ManualBulletShapeLoader() {}
|
||||
virtual ~ManualBulletShapeLoader();
|
||||
|
||||
void warn(std::string msg)
|
||||
{
|
||||
|
|
|
@ -299,7 +299,138 @@ void NIFLoader::createMaterial(const String &name,
|
|||
material->setSelfIllumination(emissive.array[0], emissive.array[1], emissive.array[2]);
|
||||
material->setShininess(glossiness);
|
||||
|
||||
// Create shader for the material
|
||||
// vertex
|
||||
HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
|
||||
|
||||
HighLevelGpuProgramPtr vertex;
|
||||
if (mgr.getByName("main_vp").isNull())
|
||||
{
|
||||
vertex = mgr.createProgram("main_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"cg", GPT_VERTEX_PROGRAM);
|
||||
vertex->setParameter("profiles", "vs_4_0 vs_2_x vp40 arbvp1");
|
||||
vertex->setParameter("entry_point", "main_vp");
|
||||
StringUtil::StrStreamType outStream;
|
||||
outStream <<
|
||||
"void main_vp( \n"
|
||||
" float4 position : POSITION, \n"
|
||||
" float4 normal : NORMAL, \n"
|
||||
" float4 colour : COLOR, \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float2 oUV : TEXCOORD0, \n"
|
||||
" out float4 oPosition : POSITION, \n"
|
||||
" out float4 oPositionObjSpace : TEXCOORD1, \n"
|
||||
" out float4 oNormal : TEXCOORD2, \n"
|
||||
" out float oFogValue : TEXCOORD3, \n"
|
||||
" out float4 oVertexColour : TEXCOORD4, \n"
|
||||
" uniform float4 fogParams, \n"
|
||||
" uniform float4x4 worldViewProj \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" oVertexColour = colour; \n"
|
||||
" oUV = uv; \n"
|
||||
" oNormal = normal; \n"
|
||||
" oPosition = mul( worldViewProj, position ); \n"
|
||||
" oFogValue = saturate((oPosition.z - fogParams.y) * fogParams.w); \n"
|
||||
" oPositionObjSpace = position; \n"
|
||||
"}";
|
||||
vertex->setSource(outStream.str());
|
||||
vertex->load();
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS);
|
||||
}
|
||||
else
|
||||
vertex = mgr.getByName("main_vp");
|
||||
material->getTechnique(0)->getPass(0)->setVertexProgram(vertex->getName());
|
||||
|
||||
// the number of lights to support.
|
||||
// when rendering an object, OGRE automatically picks the lights that are
|
||||
// closest to the object being rendered. unfortunately this mechanism does
|
||||
// not work perfectly for objects batched together (they will all use the same
|
||||
// lights). to work around this, we are simply pushing the maximum number
|
||||
// of lights here in order to minimize disappearing lights.
|
||||
float num_lights;
|
||||
if (GpuProgramManager::getSingleton().isSyntaxSupported("fp40") ||
|
||||
GpuProgramManager::getSingleton().isSyntaxSupported("ps_4_0"))
|
||||
num_lights = 8 /* 32 */;
|
||||
else
|
||||
num_lights = 8;
|
||||
|
||||
// fragment
|
||||
HighLevelGpuProgramPtr fragment;
|
||||
if (mgr.getByName("main_fp").isNull())
|
||||
{
|
||||
fragment = mgr.createProgram("main_fp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"cg", GPT_FRAGMENT_PROGRAM);
|
||||
fragment->setParameter("profiles", "ps_4_0 ps_2_x fp40 arbfp1");
|
||||
fragment->setParameter("entry_point", "main_fp");
|
||||
StringUtil::StrStreamType outStream;
|
||||
outStream <<
|
||||
"void main_fp( \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n"
|
||||
" uniform sampler2D texture : TEXUNIT0, \n"
|
||||
" float4 positionObjSpace : TEXCOORD1, \n"
|
||||
" float4 normal : TEXCOORD2, \n"
|
||||
" float fogValue : TEXCOORD3, \n"
|
||||
" float4 vertexColour : TEXCOORD4, \n"
|
||||
" uniform float4 fogColour, \n";
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" uniform float4 lightDiffuse"<<i<<", \n"
|
||||
" uniform float4 lightPositionObjSpace"<<i<<", \n"
|
||||
" uniform float4 lightAttenuation"<<i<<", \n";
|
||||
}
|
||||
outStream <<
|
||||
" uniform float4 lightAmbient, \n"
|
||||
" uniform float4 ambient, \n"
|
||||
" uniform float4 diffuse, \n"
|
||||
" uniform float4 emissive \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" float4 tex = tex2D(texture, uv); \n"
|
||||
" float d; \n"
|
||||
" float attn; \n"
|
||||
" float3 lightColour = float3(0, 0, 0); \n";
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" float3 lightDir"<<i<<" = lightPositionObjSpace"<<i<<".xyz - (positionObjSpace.xyz * lightPositionObjSpace"<<i<<".w); \n"
|
||||
|
||||
// pre-multiply light color with attenuation factor
|
||||
" d = length( lightDir"<<i<<" ); \n"
|
||||
" attn = ( 1.0 / (( lightAttenuation"<<i<<".y ) + ( lightAttenuation"<<i<<".z * d ) + ( lightAttenuation"<<i<<".w * d * d ))); \n"
|
||||
" lightDiffuse"<<i<<" *= attn; \n"
|
||||
|
||||
" lightColour.xyz += lit(dot(normalize(lightDir"<<i<<"), normalize(normal)), 0, 0).y * lightDiffuse"<<i<<".xyz;\n";
|
||||
}
|
||||
|
||||
outStream <<
|
||||
" float3 lightingFinal = lightColour.xyz * diffuse.xyz * vertexColour.xyz + ambient.xyz * lightAmbient.xyz + emissive.xyz; \n"
|
||||
" oColor.xyz = lerp(lightingFinal * tex.xyz, fogColour, fogValue); \n"
|
||||
" oColor.a = tex.a * diffuse.a * vertexColour.a; \n"
|
||||
"}";
|
||||
fragment->setSource(outStream.str());
|
||||
fragment->load();
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightPositionObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightDiffuse"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i);
|
||||
}
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_SURFACE_AMBIENT_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR);
|
||||
}
|
||||
else
|
||||
fragment = mgr.getByName("main_fp");
|
||||
material->getTechnique(0)->getPass(0)->setFragmentProgram(fragment->getName());
|
||||
}
|
||||
|
||||
// Takes a name and adds a unique part to it. This is just used to
|
||||
|
@ -378,7 +509,8 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std
|
|||
datamod[index+1] = original.y;
|
||||
datamod[index+2] = original.z;
|
||||
}
|
||||
vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false);
|
||||
vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false);
|
||||
delete [] datamod;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -419,6 +551,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std
|
|||
datamod[index+2] = original.z;
|
||||
}
|
||||
vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false);
|
||||
delete [] datamod;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -470,6 +603,7 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std
|
|||
datamod[i + 1] =y;
|
||||
}
|
||||
vbuf->writeData(0, vbuf->getSizeInBytes(), datamod, false);
|
||||
delete [] datamod;
|
||||
}
|
||||
else
|
||||
vbuf->writeData(0, vbuf->getSizeInBytes(), data->uvlist.ptr, false);
|
||||
|
@ -513,15 +647,13 @@ void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material, std
|
|||
index += 3;
|
||||
}
|
||||
|
||||
ibuf->writeData(0, ibuf->getSizeInBytes(), datamod, false);
|
||||
ibuf->writeData(0, ibuf->getSizeInBytes(), datamod, false);
|
||||
delete [] datamod;
|
||||
|
||||
}
|
||||
else
|
||||
ibuf->writeData(0, ibuf->getSizeInBytes(), data->triangles.ptr, false);
|
||||
ibuf->writeData(0, ibuf->getSizeInBytes(), data->triangles.ptr, false);
|
||||
sub->indexData->indexBuffer = ibuf;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Set material if one was given
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
stop:0.9 rgba(0, 0, 0, 55),
|
||||
stop:1 rgba(0, 0, 0, 100));
|
||||
|
||||
font: 24pt "Trebuchet MS";
|
||||
font: 26pt "EB Garamond";
|
||||
color: black;
|
||||
|
||||
border-right: 1px solid rgba(0, 0, 0, 155);
|
||||
|
@ -54,7 +54,7 @@
|
|||
}
|
||||
|
||||
#ProfileLabel {
|
||||
font: 14pt "Trebuchet MS";
|
||||
font: 18pt "EB Garamond";
|
||||
}
|
||||
|
||||
#ProfilesComboBox {
|
||||
|
@ -82,7 +82,7 @@
|
|||
padding-top: 3px;
|
||||
padding-left: 4px;
|
||||
|
||||
font: 11pt "Trebuchet MS";
|
||||
font: 12pt "EB Garamond";
|
||||
}
|
||||
|
||||
#ProfilesComboBox::drop-down {
|
||||
|
|
|
@ -6,7 +6,6 @@ set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui)
|
|||
|
||||
configure_file("${SDIR}/bigbars.png" "${DDIR}/bigbars.png" COPYONLY)
|
||||
configure_file("${SDIR}/black.png" "${DDIR}/black.png" COPYONLY)
|
||||
configure_file("${SDIR}/Comic.TTF" "${DDIR}/Comic.TTF" COPYONLY)
|
||||
configure_file("${SDIR}/core.skin" "${DDIR}/core.skin" COPYONLY)
|
||||
configure_file("${SDIR}/core.xml" "${DDIR}/core.xml" COPYONLY)
|
||||
configure_file("${SDIR}/mwpointer.png" "${DDIR}/mwpointer.png" COPYONLY)
|
||||
|
@ -42,6 +41,7 @@ configure_file("${SDIR}/openmw_layers.xml" "${DDIR}/openmw_layers.xml" COPYONLY)
|
|||
configure_file("${SDIR}/openmw_mainmenu_layout.xml" "${DDIR}/openmw_mainmenu_layout.xml" COPYONLY)
|
||||
configure_file("${SDIR}/openmw_mainmenu_skin.xml" "${DDIR}/openmw_mainmenu_skin.xml" COPYONLY)
|
||||
configure_file("${SDIR}/openmw_map_window_layout.xml" "${DDIR}/openmw_map_window_layout.xml" COPYONLY)
|
||||
configure_file("${SDIR}/openmw_map_window_skin.xml" "${DDIR}/openmw_map_window_skin.xml" COPYONLY)
|
||||
configure_file("${SDIR}/openmw.pointer.xml" "${DDIR}/openmw.pointer.xml" COPYONLY)
|
||||
configure_file("${SDIR}/openmw_progress.skin.xml" "${DDIR}/openmw_progress.skin.xml" COPYONLY)
|
||||
configure_file("${SDIR}/openmw_stats_window_layout.xml" "${DDIR}/openmw_stats_window_layout.xml" COPYONLY)
|
||||
|
@ -53,4 +53,5 @@ configure_file("${SDIR}/openmw_journal_layout.xml" "${DDIR}/openmw_journal_layou
|
|||
configure_file("${SDIR}/openmw_journal_skin.xml" "${DDIR}/openmw_journal_skin.xml" COPYONLY)
|
||||
configure_file("${SDIR}/smallbars.png" "${DDIR}/smallbars.png" COPYONLY)
|
||||
configure_file("${SDIR}/transparent.png" "${DDIR}/transparent.png" COPYONLY)
|
||||
configure_file("${SDIR}/EBGaramond-Regular.ttf" "${DDIR}/EBGaramond-Regular.ttf" COPYONLY)
|
||||
configure_file("${SDIR}/VeraMono.ttf" "${DDIR}/VeraMono.ttf" COPYONLY)
|
||||
|
|
Binary file not shown.
BIN
files/mygui/EBGaramond-Regular.ttf
Normal file
BIN
files/mygui/EBGaramond-Regular.ttf
Normal file
Binary file not shown.
|
@ -2,11 +2,11 @@
|
|||
|
||||
<MyGUI type="Skin">
|
||||
<Skin name = "TextBox" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "EB Garamond" />
|
||||
<Property key="FontHeight" value = "16" />
|
||||
<Property key="TextAlign" value = "ALIGN_DEFAULT" />
|
||||
<Property key="TextColour" value = "0.7 0.7 0.7" />
|
||||
|
||||
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
|
||||
</Skin>
|
||||
|
||||
|
@ -14,4 +14,7 @@
|
|||
<BasisSkin type="MainSkin" offset = "0 0 16 16"/>
|
||||
</Skin>
|
||||
|
||||
<Skin name = "RotatingSkin" size = "16 16">
|
||||
<BasisSkin type="RotatingSkin" offset="0 0 16 16" align="Stretch"/>
|
||||
</Skin>
|
||||
</MyGUI>
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<MyGUI>
|
||||
|
||||
<MyGUI type="List">
|
||||
<List file="core.skin" group="General"/>
|
||||
<List file="openmw_images.xml" group="General"/>
|
||||
<List file="openmw_layers.xml" group="General"/>
|
||||
<List file="openmw.pointer.xml" group="General"/>
|
||||
<List file="openmw.font.xml" group="General"/>
|
||||
<List file="openmw_text.skin.xml" group="General"/>
|
||||
<List file="openmw_windows.skin.xml" group="General"/>
|
||||
<List file="openmw_button.skin.xml" group="General"/>
|
||||
<List file="openmw_list.skin.xml" group="General"/>
|
||||
<List file="openmw_edit.skin.xml" group="General"/>
|
||||
<List file="openmw_box.skin.xml" group="General"/>
|
||||
<List file="openmw_progress.skin.xml" group="General"/>
|
||||
<List file="openmw_hud_energybar.skin.xml" group="General"/>
|
||||
<List file="openmw_hud_box.skin.xml" group="General"/>
|
||||
<List file="openmw_mainmenu_skin.xml" group="General"/>
|
||||
<List file="openmw_console.skin.xml" group="General"/>
|
||||
<List file="openmw_journal_skin.xml" group="General"/>
|
||||
<List file="openmw_dialogue_window_skin.xml" group="General"/>
|
||||
<List file="openmw_settings.xml" group="General"/>
|
||||
</MyGUI>
|
||||
|
||||
<MyGUI type="List">
|
||||
<List file="core.skin" />
|
||||
<List file="openmw_images.xml" />
|
||||
<List file="openmw_layers.xml" />
|
||||
<List file="openmw.pointer.xml" />
|
||||
<List file="openmw.font.xml" />
|
||||
<List file="openmw_text.skin.xml" />
|
||||
<List file="openmw_windows.skin.xml" />
|
||||
<List file="openmw_button.skin.xml" />
|
||||
<List file="openmw_list.skin.xml" />
|
||||
<List file="openmw_edit.skin.xml" />
|
||||
<List file="openmw_box.skin.xml" />
|
||||
<List file="openmw_progress.skin.xml" />
|
||||
<List file="openmw_hud_energybar.skin.xml" />
|
||||
<List file="openmw_hud_box.skin.xml" />
|
||||
<List file="openmw_mainmenu_skin.xml" />
|
||||
<List file="openmw_console.skin.xml" />
|
||||
<List file="openmw_journal_skin.xml" />
|
||||
<List file="openmw_map_window_skin.xml" />
|
||||
<List file="openmw_dialogue_window_skin.xml" />
|
||||
<List file="openmw_settings.xml" />
|
||||
</MyGUI>
|
||||
</MyGUI>
|
||||
|
||||
|
|
|
@ -1,95 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MyGUI type="Resource" version="1.1">
|
||||
<Resource type="ResourceTrueTypeFont" name="EB Garamond">
|
||||
<Property key="Source" value="EBGaramond-Regular.ttf"/>
|
||||
<Property key="Size" value="16"/>
|
||||
<Property key="Resolution" value="72"/>
|
||||
<Property key="Antialias" value="false"/>
|
||||
<Property key="TabWidth" value="8"/>
|
||||
<Property key="CursorWidth" value="2"/>
|
||||
<Property key="OffsetHeight" value="0"/>
|
||||
<Codes>
|
||||
<Code range="33 126"/>
|
||||
<Code range="192 382"/> <!-- Central and Eastern European languages glyphs -->
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="8470"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Codes>
|
||||
</Resource>
|
||||
|
||||
<MyGUI type="Font">
|
||||
|
||||
<Font name="MyGUI_CoreFont.18" source="Comic.TTF" size="13" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5" distance="10">
|
||||
<Code range="33 126"/>
|
||||
<Code range="192 382"/><!-- Central and Eastern European languages glyphs -->
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="8470 8470" help="№"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Font>
|
||||
|
||||
<Font name="MonoFont" default_height="17" source="VeraMono.ttf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_width="8" cursor_width="2" distance="10" offset_height="0">
|
||||
<Code range="33 126"/>
|
||||
<Code range="192 382"/><!-- Central and Eastern European glyphs -->
|
||||
<Code range="1025 1105"/>
|
||||
</Font>
|
||||
|
||||
<!-- Useful for youtube videos :) -->
|
||||
<Font name="youtube" default_height="34" source="VeraMono.ttf" size="36" resolution="50" antialias_colour="false" space_width="8" tab_width="16" cursor_width="4" distance="15" offset_height="0">
|
||||
<Code range="33 126"/>
|
||||
<Code range="192 382"/><!-- Central and Eastern European languages glyphs -->
|
||||
<Code range="1025 1105"/>
|
||||
</Font>
|
||||
|
||||
<!--
|
||||
<Font name="gothic" source="gothic.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
||||
<Code range="33 126"/>
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="8470 8470" help="№"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Font>
|
||||
|
||||
<Font name="daedric" source="daedric.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
||||
<Code range="33 98"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Font>
|
||||
|
||||
<Font name="daedric_orig" source="daedric_orig.ttf" size="18" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
||||
<Code range="33 98"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Font>
|
||||
|
||||
<Font name="daedric36" source="daedric.ttf" size="36" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
||||
<Code range="33 98"/>
|
||||
</Font>
|
||||
|
||||
<Font name="daedric_orig36" source="daedric_orig.ttf" size="36" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
||||
<Code range="33 98"/>
|
||||
</Font>
|
||||
|
||||
<Font name="cards" default_height="17" source="magiccards.ttf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_width="8" cursor_width="2" distance="5" offset_height="0">
|
||||
<Code range="33 98"/>
|
||||
</Font>
|
||||
|
||||
<Font name="cards2" default_height="17" source="magiccards2.ttf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_width="8" cursor_width="2" distance="5" offset_height="0">
|
||||
<Code range="33 98"/>
|
||||
</Font>
|
||||
|
||||
<Font name="dorcla" source="dorcla.ttf" size="20" resolution="72" antialias_colour="false" space_width="5" tab_count="4" spacer="5">
|
||||
<Code range="33 126"/>
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="8470 8470" help="№"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Font>
|
||||
|
||||
<Font name="perrygot" source="perrygot.ttf" size="16" resolution="72" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
||||
<Code range="33 126"/>
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="8470 8470" help="№"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Font>
|
||||
<Font name="albertis" source="AlbertisADF.otf" size="18" resolution="50" antialias_colour="false" space_width="4" tab_count="4" spacer="5">
|
||||
<Code range="33 126"/>
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="8470 8470" help="№"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Font>
|
||||
-->
|
||||
|
||||
</MyGUI>
|
||||
<Resource type="ResourceTrueTypeFont" name="MonoFont">
|
||||
<Property key="Source" value="VeraMono.ttf"/>
|
||||
<Property key="Size" value="18"/>
|
||||
<Property key="Resolution" value="50"/>
|
||||
<Property key="Antialias" value="false"/>
|
||||
<Property key="TabWidth" value="8"/>
|
||||
<Property key="CursorWidth" value="2"/>
|
||||
<Property key="OffsetHeight" value="0"/>
|
||||
<Codes>
|
||||
<Code range="33 126"/>
|
||||
<Code range="192 382"/>
|
||||
<Code range="1025 1105"/>
|
||||
<Code range="8470"/>
|
||||
<Code hide="128"/>
|
||||
<Code hide="1026 1039"/>
|
||||
<Code hide="1104"/>
|
||||
</Codes>
|
||||
</Resource>
|
||||
</MyGUI>
|
|
@ -45,10 +45,7 @@
|
|||
|
||||
<!-- Button widget -->
|
||||
<Skin name="MW_Button" size="136 24">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
|
||||
<!--Property key="FontName" value = "cards" /-->
|
||||
<!--Property key="FontName" value = "albertis"/-->
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default"/>
|
||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.6 0.6 0.6" />
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
<MyGUI type="Skin">
|
||||
<Skin name = "MW_ConsoleWindow" size = "256 54">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "17" />
|
||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.8 0.8 0.8" />
|
||||
<Property key="Snap" value = "true" />
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="8 39 400 375" name="History" align="ALIGN_LEFT ALIGN_TOP STRETCH">
|
||||
<Property key="Static" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="FontHeight" value="18"/>
|
||||
<Property key="MultiLine" value="1" />
|
||||
<Property key="VisibleVScroll" value="1" />
|
||||
<!-- invisible box for receiving mouse events -->
|
||||
|
@ -21,8 +20,8 @@
|
|||
|
||||
<!-- The disposition bar-->
|
||||
<Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="432 39 132 18"
|
||||
align="Right Top" name="Disposition">
|
||||
<Widget type="EditBox" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>
|
||||
align="Right Top" name="Disposition">
|
||||
<Widget type="EditBox" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>
|
||||
</Widget>
|
||||
<!-- The list of topics -->
|
||||
<Widget type="ListBox" skin="MW_List" position="432 62 132 318" name="TopicsList">
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
</Skin>
|
||||
|
||||
<Skin name="MW_TextEdit" size="512 20" texture="mwgui.png">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default"/>
|
||||
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_VCENTER" />
|
||||
<Property key="TextColour" value = "0.6 0.6 0.6" />
|
||||
|
||||
|
@ -33,8 +32,7 @@
|
|||
</Skin>
|
||||
|
||||
<Skin name="MW_TextBoxEdit" size="512 20" texture="mwgui.png">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default"/>
|
||||
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_TOP" />
|
||||
<Property key="TextColour" value = "0.6 0.6 0.6" />
|
||||
|
||||
|
@ -51,7 +49,7 @@
|
|||
<State name="normal" offset = "2 20 512 2"/>
|
||||
</BasisSkin>
|
||||
|
||||
<Child type="TextBox" skin="MW_TextBoxEditClient" offset = "2 2 490 18" align = "Stretch" name = "Client"/>
|
||||
<Child type="TextBox" skin="MW_TextBoxEditClient" offset = "2 2 490 18" align = "Stretch" name = "Client"/>
|
||||
<Child type="ScrollBar" skin="MW_VScroll" offset = "494 3 14 14" align = "Right VStretch" name = "VScroll"/>
|
||||
</Skin>
|
||||
</MyGUI>
|
||||
|
|
|
@ -38,10 +38,48 @@
|
|||
<!-- Map box -->
|
||||
<Widget type="Widget" skin="HUD_Box" position="223 123 65 65"
|
||||
align="Right Bottom">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="2 2 61 61" align="Left Bottom"
|
||||
name="MiniMap"/>
|
||||
<Widget type="ImageBox" skin="ImageBox" position="17 18 32 32" align="Left Bottom"
|
||||
name="Compass"/>
|
||||
|
||||
<Widget type="ScrollView" skin="MW_MapView" position="2 2 61 61" align="Left Bottom" name="MiniMap">
|
||||
<Property key="CanvasSize" value="1536 1536"/>
|
||||
<!-- 3x3 maps, 1024x1024 each, but we will downsample to 512 to antialias them -->
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_0">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_0_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="512 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_0">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_0_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="1024 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_0">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_0_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 512 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_1">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_1_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="512 512 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_1">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_1_fog">
|
||||
<Widget type="ImageBox" skin="RotatingSkin" position="0 0 32 32" align="Left Bottom" name="Compass"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="1024 512 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_1">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_1_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 1024 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_2">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_2_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="512 1024 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_2">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_2_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="1024 1024 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_2">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_2_fog"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<!-- Crosshair -->
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
<MyGUI type="Layout">
|
||||
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 500 400" name="_Main">
|
||||
<Widget type="EditBox" skin="MW_TextEditClient" position="10 10 490 20" align="ALIGN_LEFT ALIGN_TOP STRETCH" name="message">
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value="ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.7 0.7 0.7" />
|
||||
<Property key="Static" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="FontHeight" value="18"/>
|
||||
<Property key="MultiLine" value="1" />
|
||||
<Property key="VisibleVScroll" value="1" />
|
||||
<Property key="TextAlign" value="ALIGN_CENTER" />
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="TextColour" value = "0.7 0.7 0.7" />
|
||||
</Widget>
|
||||
<Widget type="Widget" skin="" position="0 0 500 400" align="ALIGN_STRETCH" name="buttons">
|
||||
<!-- Widget type="Button" skin="MW_Button" position="0 0 30 18" name="somefunnybutton"/ -->
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
</Skin>
|
||||
|
||||
<Skin name = "MW_BookClient" size = "10 10">
|
||||
<Property key="FontName" value = "MonoFont" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "Left Top" />
|
||||
<Property key="TextColour" value = "0 0 0" />
|
||||
<!--Property key="Pointer" value = "beam" /-->
|
||||
<BasisSkin type="EditText" offset = "0 0 10 10" align = "Stretch"/>
|
||||
</Skin>
|
||||
|
||||
|
||||
<Skin name="MW_BookPage" size="0 0 50 50">
|
||||
<Property key="WordWrap" value = "true" />
|
||||
<Child type="TextBox" skin="MW_BookClient" offset="0 0 35 10" align = "ALIGN_STRETCH" name = "Client"/>
|
||||
|
|
|
@ -167,8 +167,7 @@
|
|||
</Skin>
|
||||
|
||||
<Skin name = "HeaderText" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default"/>
|
||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.82 0.74 0.58" />
|
||||
|
||||
|
@ -177,10 +176,9 @@
|
|||
|
||||
<!-- list and multilist skins -->
|
||||
<Skin name = "MW_ListLine" size = "5 5" texture="mwgui.png">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_VCENTER" />
|
||||
|
||||
|
||||
<BasisSkin type="SimpleText" offset = "2 0 1 5" align = "ALIGN_STRETCH">
|
||||
<State name="normal" colour = "0.70 0.57 0.33"/>
|
||||
<State name="active" colour = "0.70 0.57 0.33"/>
|
||||
|
@ -197,7 +195,7 @@
|
|||
<Child type="ScrollBar" skin="MW_VScroll" offset = "498 3 14 509" align = "ALIGN_RIGHT ALIGN_VSTRETCH" name = "VScroll">
|
||||
</Child>
|
||||
|
||||
<Child type="Widget" skin="ClientDefaultSkin" offset = "3 3 493 509" align = "ALIGN_STRETCH" name = "Client">
|
||||
<Child type="Widget" skin="" offset = "3 3 493 509" align = "ALIGN_STRETCH" name = "Client">
|
||||
</Child>
|
||||
|
||||
<Child type="Widget" skin="IB_T" offset="2 0 512 2" align="ALIGN_TOP ALIGN_HSTRETCH"/>
|
||||
|
@ -233,7 +231,7 @@
|
|||
|
||||
<Property key="SkinList" value = "MW_MultiSubList" />
|
||||
|
||||
<Child type="Widget" skin="ClientDefaultSkin" offset = "3 3 516 516" align = "ALIGN_STRETCH" name = "Client">
|
||||
<Child type="Widget" skin="" offset = "3 3 516 516" align = "ALIGN_STRETCH" name = "Client">
|
||||
</Child>
|
||||
|
||||
<Child type="Widget" skin="IB_T" offset="2 0 512 2" align="ALIGN_TOP ALIGN_HSTRETCH"/>
|
||||
|
|
|
@ -2,8 +2,60 @@
|
|||
|
||||
<MyGUI type="Layout">
|
||||
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 300 300" name="_Main">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 284 264" align="ALIGN_STRETCH" name="Map"/>
|
||||
<Widget type="ImageBox" skin="ImageBox" position="126 116 32 32" align="Center" name="Compass"/>
|
||||
|
||||
<!-- Global map -->
|
||||
<Widget type="ScrollView" skin="MW_MapView" position="0 0 284 264" align="ALIGN_STRETCH" name="GlobalMap">
|
||||
</Widget>
|
||||
|
||||
<!-- Local map -->
|
||||
<Widget type="ScrollView" skin="MW_MapView" position="0 0 284 264" align="ALIGN_STRETCH" name="LocalMap">
|
||||
<Property key="CanvasSize" value="1536 1536"/>
|
||||
|
||||
<!-- 3x3 maps, 1024x1024 each, but we will downsample to 512 to antialias them -->
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_0">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_0_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="512 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_0">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_0_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="1024 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_0">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_0_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 512 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_1">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_1_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="512 512 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_1">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_1_fog">
|
||||
<!-- Player arrow -->
|
||||
<Widget type="ImageBox" skin="RotatingSkin" position="0 0 32 32" align="ALIGN_TOP ALIGN_LEFT" name="Compass"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="1024 512 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_1">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_1_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 1024 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_2">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_0_2_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="512 1024 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_2">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_1_2_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="ImageBox" skin="ImageBox" position="1024 1024 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_2">
|
||||
<Widget type="ImageBox" skin="ImageBox" position="0 0 512 512" align="ALIGN_TOP ALIGN_LEFT" name="Map_2_2_fog"/>
|
||||
</Widget>
|
||||
|
||||
<Widget type="Button" skin="" position="0 0 1536 1536" name="EventBox" align="ALIGN_STRETCH"/>
|
||||
</Widget>
|
||||
|
||||
<!-- World button -->
|
||||
<Widget type="Button" skin="MW_Button" position="213 233 61 22" align="ALIGN_BOTTOM ALIGN_RIGHT" name="WorldButton"/>
|
||||
|
||||
</Widget>
|
||||
</MyGUI>
|
||||
|
|
11
files/mygui/openmw_map_window_skin.xml
Normal file
11
files/mygui/openmw_map_window_skin.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<MyGUI type="Skin">
|
||||
<Skin name="MW_MapView" size="516 516" texture="mwgui.png">
|
||||
<Child type="Widget" skin="" offset="0 0 516 516" align="Stretch" name="Client"/>
|
||||
|
||||
<!-- invisible scroll bars, needed for setting the view offset -->
|
||||
<Child type="ScrollBar" skin="" offset="498 3 14 509" align="ALIGN_RIGHT ALIGN_VSTRETCH" name="VScroll"/>
|
||||
<Child type="ScrollBar" skin="" offset="3 498 489 14" align="ALIGN_BOTTOM ALIGN_HSTRETCH" name="HScroll"/>
|
||||
</Skin>
|
||||
</MyGUI>
|
|
@ -6,14 +6,13 @@
|
|||
</Widget-->
|
||||
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 0 0" name="_Main">
|
||||
<Widget type="EditBox" skin="MW_TextEditClient" position="5 -5 0 0" name="message" align="ALIGN_LEFT ALIGN_TOP STRETCH">
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value="ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.7 0.7 0.7" />
|
||||
<Property key="Static" value="true"/>
|
||||
<Property key="WordWrap" value="true"/>
|
||||
<Property key="FontHeight" value="18"/>
|
||||
<Property key="MultiLine" value="1" />
|
||||
<Property key="VisibleVScroll" value="1" />
|
||||
<Property key="TextAlign" value="ALIGN_CENTER" />
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="TextColour" value = "0.7 0.7 0.7" />
|
||||
</Widget>
|
||||
</Widget>
|
||||
</MyGUI>
|
||||
|
|
|
@ -19,11 +19,10 @@
|
|||
</Skin>
|
||||
|
||||
<Skin name = "ProgressText" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18"/>
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default"/>
|
||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.75 0.6 0.35" />
|
||||
|
||||
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
|
||||
</Skin>
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<MyGUI>
|
||||
<MyGUI type="Font">
|
||||
<Property key="Default" value="MyGUI_CoreFont.18"/>
|
||||
</MyGUI>
|
||||
<Property key="Default" value="EB Garamond"/>
|
||||
|
||||
</MyGUI>
|
||||
<MyGUI type="Pointer">
|
||||
<Property key="Default" value="arrow"/>
|
||||
<Property key="Layer" value="Pointer"/>
|
||||
|
|
|
@ -2,40 +2,35 @@
|
|||
|
||||
<MyGUI type="Skin">
|
||||
<Skin name = "NormalText" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_BOTTOM" />
|
||||
<Property key="TextColour" value = "0.87 0.78 0.62" />
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
|
||||
</Skin>
|
||||
|
||||
<Skin name = "NumFPS" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_HCENTER ALIGN_BOTTOM" />
|
||||
<Property key="TextColour" value = "1 1 1" />
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
|
||||
</Skin>
|
||||
|
||||
<Skin name = "SandText" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_BOTTOM" />
|
||||
<Property key="TextColour" value = "0.75 0.6 0.35" />
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
|
||||
</Skin>
|
||||
|
||||
<Skin name = "SandTextC" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "TOP HCENTER" />
|
||||
<Property key="TextColour" value = "0.75 0.6 0.35" />
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
|
||||
</Skin>
|
||||
|
||||
<Skin name = "SandTextRight" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_RIGHT ALIGN_BOTTOM" />
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH">
|
||||
<State name="normal" colour = "0.75 0.6 0.35"/>
|
||||
|
@ -45,8 +40,7 @@
|
|||
</Skin>
|
||||
|
||||
<Skin name = "SandBrightText" size = "16 16">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_LEFT ALIGN_BOTTOM" />
|
||||
<Property key="TextColour" value = "0.85 0.76 0.60" />
|
||||
<BasisSkin type="SimpleText" offset = "0 0 16 16" align = "ALIGN_STRETCH"/>
|
||||
|
|
|
@ -189,14 +189,7 @@
|
|||
<!-- The actual caption. It contains the edges of the blocks on
|
||||
its sides as well -->
|
||||
<Skin name = "MW_Caption" size = "88 20" texture = "black.png" >
|
||||
<!--
|
||||
<Property key="FontName" value = "dorcla" />
|
||||
<Property key="FontHeight" value = "20" />
|
||||
<Property key="FontName" value = "albertis" />
|
||||
<Property key="FontName" value = "cards" />
|
||||
-->
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.75 0.62 0.36" />
|
||||
|
||||
|
@ -221,8 +214,7 @@
|
|||
------------------------------------------------------ -->
|
||||
|
||||
<Skin name = "MW_Window" size = "256 54">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.8 0.8 0.8" />
|
||||
<Property key="Snap" value = "true" />
|
||||
|
@ -298,8 +290,7 @@
|
|||
</Skin>
|
||||
|
||||
<Skin name = "MW_Dialog" size = "256 54">
|
||||
<Property key="FontName" value = "MyGUI_CoreFont.18" />
|
||||
<Property key="FontHeight" value = "18" />
|
||||
<Property key="FontName" value = "Default" />
|
||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||
<Property key="TextColour" value = "0.8 0.8 0.8" />
|
||||
|
||||
|
|
1
libs/mangle/sound/.gitignore
vendored
1
libs/mangle/sound/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_OGRELISTENERMOVER_H
|
||||
#define MANGLE_SOUND_OGRELISTENERMOVER_H
|
||||
|
||||
#include <OgreCamera.h>
|
||||
#include <assert.h>
|
||||
#include "../output.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/** This class lets a sound listener (ie. the SoundFactory) track a
|
||||
given camera in Ogre3D. The position 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);
|
||||
}
|
||||
|
||||
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;
|
||||
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->getRealPosition();
|
||||
nDir = camera->getRealDirection();
|
||||
nUp = camera->getRealUp();
|
||||
|
||||
// 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
|
|
@ -1,31 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_OGREUPDATER_H
|
||||
#define MANGLE_SOUND_OGREUPDATER_H
|
||||
|
||||
/*
|
||||
This Ogre FrameListener calls update on a SoundFactory
|
||||
*/
|
||||
|
||||
#include <OgreFrameListener.h>
|
||||
#include "../output.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
struct OgreOutputUpdater : Ogre::FrameListener
|
||||
{
|
||||
Mangle::Sound::SoundFactoryPtr driver;
|
||||
|
||||
OgreOutputUpdater(Mangle::Sound::SoundFactoryPtr drv)
|
||||
: driver(drv)
|
||||
{ assert(drv->needsUpdate); }
|
||||
|
||||
bool frameStarted(const Ogre::FrameEvent &evt)
|
||||
{
|
||||
driver->update();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}}
|
||||
|
||||
#endif
|
|
@ -1,68 +0,0 @@
|
|||
#ifndef MANGLE_INPUT_FILTER_H
|
||||
#define MANGLE_INPUT_FILTER_H
|
||||
|
||||
#include "../output.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/**
|
||||
@brief This filter class adds file loading capabilities to a
|
||||
Sound::SoundFactory class, by associating a SampleSourceLoader with
|
||||
it.
|
||||
|
||||
The class takes an existing SoundFactory able to load streams, and
|
||||
associates a SampleSourceLoader with it. The combined class is able
|
||||
to load files directly. */
|
||||
class InputFilter : public SoundFactory
|
||||
{
|
||||
protected:
|
||||
SoundFactoryPtr snd;
|
||||
SampleSourceLoaderPtr inp;
|
||||
|
||||
public:
|
||||
/// Empty constructor
|
||||
InputFilter() {}
|
||||
|
||||
/// Assign an input manager and a sound manager to this object
|
||||
InputFilter(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
|
||||
{ set(_snd, _inp); }
|
||||
|
||||
/// Assign an input manager and a sound manager to this object
|
||||
void set(SoundFactoryPtr _snd, SampleSourceLoaderPtr _inp)
|
||||
{
|
||||
inp = _inp;
|
||||
snd = _snd;
|
||||
|
||||
// Set capabilities
|
||||
needsUpdate = snd->needsUpdate;
|
||||
has3D = snd->has3D;
|
||||
canLoadStream = inp->canLoadStream;
|
||||
|
||||
// Both these should be true, or the use of this class is pretty
|
||||
// pointless
|
||||
canLoadSource = snd->canLoadSource;
|
||||
canLoadFile = inp->canLoadFile;
|
||||
assert(canLoadSource && canLoadFile);
|
||||
}
|
||||
|
||||
virtual SoundPtr load(const std::string &file)
|
||||
{ return loadRaw(inp->load(file)); }
|
||||
|
||||
virtual SoundPtr load(Stream::StreamPtr input)
|
||||
{ return loadRaw(inp->load(input)); }
|
||||
|
||||
virtual SoundPtr loadRaw(SampleSourcePtr input)
|
||||
{ return snd->loadRaw(input); }
|
||||
|
||||
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
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef MANGLE_AUDIERE_OPENAL_H
|
||||
#define MANGLE_AUDIERE_OPENAL_H
|
||||
|
||||
#include "input_filter.hpp"
|
||||
#include "../sources/audiere_source.hpp"
|
||||
#include "../outputs/openal_out.hpp"
|
||||
|
||||
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_Factory : public InputFilter
|
||||
{
|
||||
public:
|
||||
OpenAL_Audiere_Factory()
|
||||
{
|
||||
set(SoundFactoryPtr(new OpenAL_Factory),
|
||||
SampleSourceLoaderPtr(new AudiereLoader));
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
|
@ -1,23 +0,0 @@
|
|||
#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
|
|
@ -1,24 +0,0 @@
|
|||
#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
|
|
@ -1,24 +0,0 @@
|
|||
#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
|
|
@ -1,33 +0,0 @@
|
|||
#ifndef MANGLE_SNDFILE_MPG123_OPENAL_H
|
||||
#define MANGLE_SNDFILE_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
|
|
@ -1,39 +0,0 @@
|
|||
#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. 3rd party decoder
|
||||
libraries like 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
|
|
@ -1,73 +0,0 @@
|
|||
#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
|
||||
{
|
||||
protected:
|
||||
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 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); }
|
||||
void setRelative(bool b) { client->setRelative(b); }
|
||||
|
||||
// The clone() function is not implemented here, as you will
|
||||
// almost certainly want to override it yourself
|
||||
};
|
||||
|
||||
class FactoryFilter : public SoundFactory
|
||||
{
|
||||
protected:
|
||||
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
|
|
@ -1,90 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_SOURCE_SPLICE_H
|
||||
#define MANGLE_SOUND_SOURCE_SPLICE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Mangle
|
||||
{
|
||||
namespace Sound
|
||||
{
|
||||
class SourceSplicer : public SampleSourceLoader
|
||||
{
|
||||
struct SourceType
|
||||
{
|
||||
std::string type;
|
||||
SampleSourceLoaderPtr loader;
|
||||
};
|
||||
|
||||
typedef std::list<SourceType> 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(unsigned i=0; i<t.size(); i++)
|
||||
if(!isMatch(t[i], file[i+diff]))
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Got something! We're done.
|
||||
if(match)
|
||||
return it->loader->load(file);
|
||||
}
|
||||
// If not found, use the catch-all
|
||||
if(catchAll)
|
||||
return catchAll->load(file);
|
||||
|
||||
throw std::runtime_error("No handler for sound file " + file);
|
||||
}
|
||||
|
||||
SampleSourcePtr load(Stream::StreamPtr input) { assert(0); }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,183 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_OUTPUT_H
|
||||
#define MANGLE_SOUND_OUTPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
|
||||
#include "source.hpp"
|
||||
#include "../stream/stream.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// 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.
|
||||
|
||||
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;
|
||||
typedef boost::shared_ptr<Sound> SoundPtr;
|
||||
typedef boost::weak_ptr <Sound> WSoundPtr;
|
||||
|
||||
class Sound
|
||||
{
|
||||
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() const = 0;
|
||||
|
||||
/// Set the volume. The parameter must be between 0.0 and 1.0.
|
||||
virtual void setVolume(float) = 0;
|
||||
|
||||
/// 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 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;
|
||||
|
||||
/// Set loop mode
|
||||
virtual void setRepeat(bool) = 0;
|
||||
|
||||
/// If set to true the sound will not be affected by player movement
|
||||
virtual void setRelative(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 SoundPtr clone() = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~Sound() {}
|
||||
};
|
||||
|
||||
/// 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 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.
|
||||
*/
|
||||
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;
|
||||
|
||||
/// true if we can load sounds directly from file (containing encoded data)
|
||||
bool canLoadFile;
|
||||
|
||||
/// 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 a sample source. Only valid if
|
||||
canLoadSource is true.
|
||||
|
||||
This function loads a sound from a given stream as defined by
|
||||
SampleSource.
|
||||
|
||||
@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 SoundPtr loadRaw(SampleSourcePtr input) = 0;
|
||||
|
||||
/**
|
||||
@brief Load a sound file from stream. 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 SoundPtr load(Stream::StreamPtr input) = 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 SoundPtr load(const std::string &file) = 0;
|
||||
|
||||
/// Call this every frame if needsUpdate is true
|
||||
/**
|
||||
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() { assert(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;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SoundFactory> SoundFactoryPtr;
|
||||
|
||||
}} // Namespaces
|
||||
|
||||
#endif
|
|
@ -1,500 +0,0 @@
|
|||
#include "openal_out.hpp"
|
||||
#include <assert.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../../stream/filters/buffer_stream.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <al.h>
|
||||
#include <alc.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <OpenAL/alc.h>
|
||||
#include <OpenAL/al.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
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.
|
||||
// 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); }
|
||||
|
||||
/*
|
||||
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);
|
||||
else
|
||||
fail("non-specified error while " + msg + " (did you forget to initialize OpenAL?)");
|
||||
}
|
||||
}
|
||||
|
||||
static void getALFormat(SampleSourcePtr inp, int &fmt, int &rate)
|
||||
{
|
||||
boost::int32_t rate_, ch, bits;
|
||||
inp->getInfo(&rate_, &ch, &bits);
|
||||
rate = rate_;
|
||||
|
||||
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 output
|
||||
class Mangle::Sound::OpenAL_Sound : public Sound
|
||||
{
|
||||
ALuint inst;
|
||||
|
||||
// Buffers. Only the first is used for non-streaming sounds.
|
||||
ALuint bufferID[STREAM_BUF_NUM];
|
||||
|
||||
// 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.
|
||||
int *refCnt;
|
||||
|
||||
bool streaming;
|
||||
|
||||
// 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_Factory *fact);
|
||||
|
||||
/// Play an existing buffer, with a given ref counter. Used
|
||||
/// internally for cloning.
|
||||
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; i<count; i++)
|
||||
{
|
||||
ALuint buf;
|
||||
// Unqueue one of the processed buffer
|
||||
alSourceUnqueueBuffers(inst, 1, &buf);
|
||||
|
||||
// Then reload it with data (if any) and queue it up
|
||||
queueBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
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 setRelative(bool);
|
||||
|
||||
void notifyOwnerDeath()
|
||||
{ ownerAlive = false; }
|
||||
|
||||
// We can enable streaming, but never disable it.
|
||||
void setStreaming(bool s)
|
||||
{ if(s) streaming = true; }
|
||||
|
||||
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 ----
|
||||
|
||||
SoundPtr OpenAL_Factory::loadRaw(SampleSourcePtr input)
|
||||
{
|
||||
return SoundPtr(new OpenAL_Sound(input, this));
|
||||
}
|
||||
|
||||
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
|
||||
Device = alcOpenDevice(NULL);
|
||||
Context = alcCreateContext(Device, NULL);
|
||||
|
||||
if(!Device || !Context)
|
||||
fail("Failed to initialize context or device");
|
||||
|
||||
alcMakeContextCurrent(Context);
|
||||
|
||||
device = Device;
|
||||
context = Context;
|
||||
alDistanceModel(AL_LINEAR_DISTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenAL_Factory::update()
|
||||
{
|
||||
// Loop through all streaming sounds and update them
|
||||
StreamList::iterator it = streaming.begin();
|
||||
for(;it != streaming.end(); it++)
|
||||
(*it)->update();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
alcMakeContextCurrent(NULL);
|
||||
if(context) alcDestroyContext((ALCcontext*)context);
|
||||
if(device) alcCloseDevice((ALCdevice*)device);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- OpenAL_Sound ----
|
||||
|
||||
void OpenAL_Sound::play()
|
||||
{
|
||||
setupBuffer();
|
||||
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() const
|
||||
{
|
||||
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::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);
|
||||
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);
|
||||
}
|
||||
|
||||
void OpenAL_Sound::setRelative(bool rel)
|
||||
{
|
||||
alSourcei(inst, AL_SOURCE_RELATIVE, rel?AL_TRUE:AL_FALSE);
|
||||
checkALError("setting relative");
|
||||
}
|
||||
|
||||
SoundPtr OpenAL_Sound::clone()
|
||||
{
|
||||
setupBuffer();
|
||||
assert(!streaming && "cloning streamed sounds not supported");
|
||||
return SoundPtr(new OpenAL_Sound(bufferID[0], refCnt, owner));
|
||||
}
|
||||
|
||||
// Constructor used for cloned sounds
|
||||
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[0]);
|
||||
checkALError("assigning buffer (clone)");
|
||||
}
|
||||
|
||||
// Constructor used for original (non-cloned) sounds
|
||||
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);
|
||||
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
|
||||
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)
|
||||
{
|
||||
// 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; i<bufNum; i++)
|
||||
queueBuffer(bufferID[i]);
|
||||
|
||||
// Notify the manager what we're doing
|
||||
owner->notifyStreaming(this);
|
||||
ownerAlive = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 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[0], fmt, input->getPtr(), input->size(), rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the entire stream into a temporary buffer first
|
||||
Mangle::Stream::BufferStream buf(input, 128*1024);
|
||||
|
||||
// Then copy that into OpenAL
|
||||
alBufferData(bufferID[0], fmt, buf.getPtr(), buf.size(), rate);
|
||||
}
|
||||
checkALError("loading sound data");
|
||||
|
||||
// We're done with the input stream, release the pointer
|
||||
input.reset();
|
||||
|
||||
alSourcei(inst, AL_BUFFER, bufferID[0]);
|
||||
checkALError("assigning buffer");
|
||||
}
|
||||
|
||||
OpenAL_Sound::~OpenAL_Sound()
|
||||
{
|
||||
// Stop
|
||||
alSourceStop(inst);
|
||||
|
||||
// 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(s) and the counter
|
||||
// itself.
|
||||
alDeleteBuffers(bufNum, bufferID);
|
||||
checkALError("deleting buffer");
|
||||
delete refCnt;
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_OPENAL_OUT_H
|
||||
#define MANGLE_SOUND_OPENAL_OUT_H
|
||||
|
||||
#include "../output.hpp"
|
||||
#include <list>
|
||||
|
||||
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<OpenAL_Sound*> 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
|
||||
/// you.
|
||||
OpenAL_Factory(bool doSetup = true);
|
||||
~OpenAL_Factory();
|
||||
|
||||
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();
|
||||
void setListenerPos(float x, float y, float z,
|
||||
float fx, float fy, float fz,
|
||||
float ux, float uy, float uz);
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
|
@ -1,62 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_SOURCE_H
|
||||
#define MANGLE_SOUND_SOURCE_H
|
||||
|
||||
#include <string>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <assert.h>
|
||||
|
||||
#include "../stream/stream.hpp"
|
||||
|
||||
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
|
||||
{
|
||||
protected:
|
||||
bool isEof;
|
||||
|
||||
public:
|
||||
SampleSource() : 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) = 0;
|
||||
|
||||
bool eof() const { return isEof; }
|
||||
|
||||
// Disabled functions by default. You can still override them in
|
||||
// subclasses.
|
||||
void seek(size_t pos) { assert(0); }
|
||||
size_t tell() const { assert(0); return 0; }
|
||||
size_t size() const { assert(0); return 0; }
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SampleSource> SampleSourcePtr;
|
||||
|
||||
/// 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 SampleSourcePtr load(const std::string &file) = 0;
|
||||
|
||||
/// Load a sound input source from stream (if canLoadStream is true)
|
||||
virtual SampleSourcePtr load(Stream::StreamPtr input) = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~SampleSourceLoader() {}
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SampleSourceLoader> SampleSourceLoaderPtr;
|
||||
|
||||
}} // namespaces
|
||||
#endif
|
|
@ -1,77 +0,0 @@
|
|||
#include "audiere_source.hpp"
|
||||
|
||||
#include "../../stream/clients/audiere_file.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace Mangle::Stream;
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("Audiere exception: " + msg); }
|
||||
|
||||
using namespace audiere;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
// --- SampleSource ---
|
||||
|
||||
void AudiereSource::getInfo(Mangle::Sound::int32_t *rate,
|
||||
Mangle::Sound::int32_t *channels, Mangle::Sound::int32_t *bits)
|
||||
{
|
||||
SampleFormat fmt;
|
||||
int channels_, rate_;
|
||||
sample->getFormat(channels_, rate_, fmt);
|
||||
*channels = channels_;
|
||||
*rate = rate_;
|
||||
if(bits)
|
||||
{
|
||||
if(fmt == SF_U8)
|
||||
*bits = 8;
|
||||
else if(fmt == SF_S16)
|
||||
*bits = 16;
|
||||
else assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Constructors ---
|
||||
|
||||
AudiereSource::AudiereSource(const std::string &file)
|
||||
{
|
||||
sample = OpenSampleSource(file.c_str());
|
||||
|
||||
if(!sample)
|
||||
fail("Couldn't load file " + file);
|
||||
|
||||
doSetup();
|
||||
}
|
||||
|
||||
AudiereSource::AudiereSource(StreamPtr input)
|
||||
{
|
||||
// Use our Stream::AudiereFile implementation to convert a Mangle
|
||||
// 'Stream' to an Audiere 'File'
|
||||
sample = OpenSampleSource(new AudiereFile(input));
|
||||
if(!sample)
|
||||
fail("Couldn't load stream");
|
||||
|
||||
doSetup();
|
||||
}
|
||||
|
||||
AudiereSource::AudiereSource(audiere::SampleSourcePtr src)
|
||||
: sample(src)
|
||||
{ assert(sample); doSetup(); }
|
||||
|
||||
// Common function called from all constructors
|
||||
void AudiereSource::doSetup()
|
||||
{
|
||||
assert(sample);
|
||||
|
||||
SampleFormat fmt;
|
||||
int channels, rate;
|
||||
sample->getFormat(channels, rate, fmt);
|
||||
|
||||
// Calculate the size of one frame, and pass it to SampleReader.
|
||||
setup(GetSampleSize(fmt) * channels);
|
||||
|
||||
isSeekable = sample->isSeekable();
|
||||
hasPosition = true;
|
||||
hasSize = true;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_AUDIERE_SOURCE_H
|
||||
#define MANGLE_SOUND_AUDIERE_SOURCE_H
|
||||
|
||||
#include "sample_reader.hpp"
|
||||
|
||||
// audiere.h from 1.9.4 (latest) release uses
|
||||
// cstring routines like strchr() and strlen() without
|
||||
// including cstring itself.
|
||||
#include <cstring>
|
||||
#include <audiere.h>
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A sample source that decodes files using Audiere
|
||||
class AudiereSource : public SampleReader
|
||||
{
|
||||
audiere::SampleSourcePtr sample;
|
||||
|
||||
size_t readSamples(void *data, size_t length)
|
||||
{ return sample->read(length, data); }
|
||||
|
||||
void doSetup();
|
||||
|
||||
public:
|
||||
/// Decode the given sound file
|
||||
AudiereSource(const std::string &file);
|
||||
|
||||
/// Decode the given sound stream
|
||||
AudiereSource(Mangle::Stream::StreamPtr src);
|
||||
|
||||
/// Read directly from an existing audiere::SampleSource
|
||||
AudiereSource(audiere::SampleSourcePtr src);
|
||||
|
||||
void getInfo(int32_t *rate, int32_t *channels, int32_t *bits);
|
||||
|
||||
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.hpp"
|
||||
|
||||
/// A factory that loads AudiereSources from file and stream
|
||||
typedef SSL_Template<AudiereSource,true,true> AudiereLoader;
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
|
@ -1,189 +0,0 @@
|
|||
#include "ffmpeg_source.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
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];
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("FFMpeg exception: " + msg); }
|
||||
|
||||
// --- Loader ---
|
||||
|
||||
static bool init = false;
|
||||
|
||||
FFMpegLoader::FFMpegLoader(bool setup)
|
||||
{
|
||||
if(setup && !init)
|
||||
{
|
||||
av_register_all();
|
||||
av_log_set_level(AV_LOG_ERROR);
|
||||
init = true;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Source ---
|
||||
|
||||
FFMpegSource::FFMpegSource(const std::string &file)
|
||||
{
|
||||
std::string msg;
|
||||
AVCodec *codec;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
FFMpegSource::~FFMpegSource()
|
||||
{
|
||||
avcodec_close(CodecCtx);
|
||||
av_close_input_file(FmtCtx);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t FFMpegSource::read(void *data, size_t length)
|
||||
{
|
||||
if(isEof) return 0;
|
||||
|
||||
size_t left = length;
|
||||
uint8_t *outPtr = (uint8_t*)data;
|
||||
|
||||
// First, copy over any stored data we might be sitting on
|
||||
{
|
||||
size_t s = storage.size();
|
||||
size_t 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?
|
||||
assert(s>= copy);
|
||||
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((int)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
|
||||
size_t 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. This loop repeats as long left is > 0
|
||||
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)
|
||||
isEof = true;
|
||||
|
||||
return length - left;
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_FFMPEG_H
|
||||
#define MANGLE_SOUND_FFMPEG_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <avcodec.h>
|
||||
#include <avformat.h>
|
||||
}
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
class FFMpegSource : public SampleSource
|
||||
{
|
||||
AVFormatContext *FmtCtx;
|
||||
AVCodecContext *CodecCtx;
|
||||
unsigned int StreamNum;
|
||||
|
||||
std::vector<uint8_t> storage;
|
||||
|
||||
public:
|
||||
/// Decode the given sound file
|
||||
FFMpegSource(const std::string &file);
|
||||
|
||||
/// Decode the given sound stream (not supported by FFmpeg)
|
||||
FFMpegSource(Mangle::Stream::StreamPtr 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.hpp"
|
||||
|
||||
/// A factory that loads FFMpegSources from file
|
||||
class FFMpegLoader : public SSL_Template<FFMpegSource,false,true>
|
||||
{
|
||||
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
|
|
@ -1,48 +0,0 @@
|
|||
#include "libsndfile.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sndfile.h>
|
||||
|
||||
using namespace Mangle::Stream;
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("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)
|
||||
{
|
||||
// readf_* reads entire frames, including channels
|
||||
return sf_readf_short((SNDFILE*)handle, (short*)data, length);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
#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<SndFileSource,false,true> SndFileLoader;
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
|
@ -1,28 +0,0 @@
|
|||
#ifndef SSL_TEMPL_H
|
||||
#define SSL_TEMPL_H
|
||||
|
||||
template <class SourceT, bool stream, bool file>
|
||||
class SSL_Template : public SampleSourceLoader
|
||||
{
|
||||
public:
|
||||
|
||||
SSL_Template()
|
||||
{
|
||||
canLoadStream = stream;
|
||||
canLoadFile = file;
|
||||
}
|
||||
|
||||
SampleSourcePtr load(const std::string &filename)
|
||||
{
|
||||
assert(canLoadFile);
|
||||
return SampleSourcePtr(new SourceT(filename));
|
||||
}
|
||||
|
||||
SampleSourcePtr load(Stream::StreamPtr input)
|
||||
{
|
||||
assert(canLoadStream);
|
||||
return SampleSourcePtr(new SourceT(input));
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,115 +0,0 @@
|
|||
#include "mpg123_source.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <mpg123.h>
|
||||
|
||||
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 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.
|
||||
|
||||
- we could implement seek(), tell() and size(), but they aren't
|
||||
really necessary. Furthermore, since the returned size is only a
|
||||
guess, it is not safe to rely on it.
|
||||
*/
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("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 (hack around constness)
|
||||
err = mpg123_open(mhh, (char*)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("Unsupported encoding in " + file);
|
||||
|
||||
// This is the only bit size we support.
|
||||
bits = 16;
|
||||
}
|
||||
|
||||
Mpg123Source::~Mpg123Source()
|
||||
{
|
||||
mpg123_close((mpg123_handle*)mh);
|
||||
mpg123_delete((mpg123_handle*)mh);
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_MPG123_SOURCE_H
|
||||
#define MANGLE_SOUND_MPG123_SOURCE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
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<Mpg123Source,false,true>
|
||||
{
|
||||
/** 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
|
|
@ -1,99 +0,0 @@
|
|||
#include "sample_reader.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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
|
||||
size_t frames = length / frameSize;
|
||||
|
||||
// Read the data
|
||||
size_t res = readSamples(data, frames);
|
||||
assert(res <= 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;
|
||||
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
|
||||
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;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
#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.
|
||||
|
||||
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
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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() : pullSize(0), pullOver(NULL) {}
|
||||
~SampleReader();
|
||||
size_t read(void *data, size_t length);
|
||||
};
|
||||
}} // Namespace
|
||||
#endif
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_STREAMSOURCE_H
|
||||
#define MANGLE_SOUND_STREAMSOURCE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
|
||||
namespace Mangle {
|
||||
namespace Sound {
|
||||
|
||||
/// A class for reading raw samples directly from a stream.
|
||||
class Stream2Samples : public SampleSource
|
||||
{
|
||||
Mangle::Stream::StreamPtr inp;
|
||||
int32_t rate, channels, bits;
|
||||
|
||||
public:
|
||||
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;
|
||||
hasPosition = inp->hasPosition;
|
||||
hasSize = inp->hasSize;
|
||||
hasPtr = inp->hasPtr;
|
||||
}
|
||||
|
||||
/// 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)
|
||||
{
|
||||
if(_rate) *_rate = rate;
|
||||
if(_channels) *_channels = channels;
|
||||
if(_bits) *_bits = bits;
|
||||
}
|
||||
|
||||
size_t read(void *out, size_t count)
|
||||
{ return inp->read(out, 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(); }
|
||||
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
|
||||
#endif
|
|
@ -1,99 +0,0 @@
|
|||
#include "wav_source.hpp"
|
||||
|
||||
#include "../../stream/servers/file_stream.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace Mangle::Stream;
|
||||
using namespace Mangle::Sound;
|
||||
|
||||
static void fail(const std::string &msg)
|
||||
{ throw std::runtime_error("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;
|
||||
size_t read = input->read(data, length);
|
||||
if(read < length)
|
||||
// Something went wrong
|
||||
fail("WAV read error");
|
||||
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))); }
|
|
@ -1,49 +0,0 @@
|
|||
#ifndef MANGLE_SOUND_WAV_SOURCE_H
|
||||
#define MANGLE_SOUND_WAV_SOURCE_H
|
||||
|
||||
#include "../source.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
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<WavSource,true,true> WavLoader;
|
||||
|
||||
}} // Namespace
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue