Merge remote-tracking branch 'upstream/master'

actorid
Jason Hooks 13 years ago
commit 0021ccf862

@ -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)
@ -124,52 +120,31 @@ set(OENGINE_BULLET
${LIBDIR}/openengine/bullet/trace.cpp
)
# 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)
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)
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)
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)
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)
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)
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})
source_group(libs\\openengine FILES ${OENGINE_ALL})
set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL})
set(OPENMW_LIBS_HEADER)
# Platform specific
if (WIN32)
set(PLATFORM_INCLUDE_DIR "platform")
@ -181,7 +156,6 @@ include_directories(${UUID_INCLUDE_DIR})
endif (WIN32)
if (MSVC10)
set(PLATFORM_INCLUDE_DIR "")
add_definitions(-DMYGUI_DONT_REPLACE_NULLPTR)
endif()
if (APPLE)
@ -190,7 +164,13 @@ endif (APPLE)
# Dependencies
# Fix for not visible pthreads functions for linker with glibc 2.15
if (UNIX AND NOT APPLE)
find_package (Threads)
endif()
find_package(OGRE REQUIRED)
find_package(MyGUI REQUIRED)
find_package(Boost REQUIRED COMPONENTS system filesystem program_options thread)
find_package(OIS REQUIRED)
find_package(OpenAL REQUIRED)
@ -205,16 +185,17 @@ ENDIF(WIN32)
ENDIF(OGRE_STATIC)
include_directories("."
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
${OGRE_Terrain_INCLUDE_DIR}
${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}
${PLATFORM_INCLUDE_DIR}
${CMAKE_HOME_DIRECTORY}/extern/mygui_3.0.1/MyGUIEngine/include
${CMAKE_HOME_DIRECTORY}/extern/mygui_3.0.1/OgrePlatform/include
${MYGUI_INCLUDE_DIRS}
${MYGUI_PLATFORM_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR}
${UUID_INCLUDE_DIR}
${LIBDIR}
)
link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR})
link_directories(${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MYGUI_LIB_DIR})
if(APPLE)
# List used Ogre plugins
@ -224,14 +205,8 @@ if(APPLE)
"Plugin_ParticleFX")
endif(APPLE)
add_subdirectory( extern/mygui_3.0.1 )
# Make sure that certain libraries are used as static libraries
# This is in effect turns off __declspec (dllexport) for windows
# Each library will also need to be configured to build as a static lib
# MyGUI: extern/mygui_3.0.0/
add_definitions(-DMYGUI_STATIC)
add_subdirectory( files/)
add_subdirectory( files/mygui )
# Specify build paths
@ -286,7 +261,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)
@ -326,7 +310,7 @@ if(DPKG_PROGRAM)
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libogre-1.7.3 (>= 1.7.3), libbullet0 (>= 2.77), libboost-filesystem1.46.1 (>= 1.46.1), libboost-program-options1.46.1 (>= 1.46.1), libboost-system1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "nvidia-cg-toolkit (>= 2.1), libboost-filesystem1.46.1 (>= 1.46.1), libboost-program-options1.46.1 (>= 1.46.1), libboost-system1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
@ -346,6 +330,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")
@ -356,6 +341,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 ".")
@ -498,6 +484,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})

@ -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.

@ -95,5 +95,5 @@ else()
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg")
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
endif()

@ -222,10 +222,10 @@ void DataFilesPage::setupDataFiles()
QMessageBox msgBox;
msgBox.setWindowTitle("Error detecting Morrowind installation");
msgBox.setIcon(QMessageBox::Critical);
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 =
@ -279,72 +279,79 @@ void DataFilesPage::setupDataFiles()
const Files::MultiDirCollection &esp = fileCollections.getCollection(".esp");
for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) {
ESMReader fileReader;
QStringList availableMasters; // Will contain all found masters
fileReader.setEncoding(variables["encoding"].as<std::string>());
fileReader.open(iter->second.string());
try {
ESMReader fileReader;
QStringList availableMasters; // Will contain all found masters
fileReader.setEncoding(variables["encoding"].as<std::string>());
fileReader.open(iter->second.string());
// First we fill the availableMasters and the mMastersWidget
ESMReader::MasterList mlist = fileReader.getMasters();
// First we fill the availableMasters and the mMastersWidget
ESMReader::MasterList mlist = fileReader.getMasters();
for (unsigned int i = 0; i < mlist.size(); ++i) {
const QString currentMaster = QString::fromStdString(mlist[i].name);
availableMasters.append(currentMaster);
for (unsigned int i = 0; i < mlist.size(); ++i) {
const QString currentMaster = QString::fromStdString(mlist[i].name);
availableMasters.append(currentMaster);
const QList<QTableWidgetItem*> itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly);
const QList<QTableWidgetItem*> itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly);
if (itemList.isEmpty()) { // Master is not yet in the widget
mMastersWidget->insertRow(i);
if (itemList.isEmpty()) { // Master is not yet in the widget
mMastersWidget->insertRow(i);
QTableWidgetItem *item = new QTableWidgetItem(currentMaster);
item->setForeground(Qt::red);
item->setFlags(item->flags() & ~(Qt::ItemIsSelectable));
QTableWidgetItem *item = new QTableWidgetItem(currentMaster);
item->setForeground(Qt::red);
item->setFlags(item->flags() & ~(Qt::ItemIsSelectable));
mMastersWidget->setItem(i, 0, item);
mMastersWidget->setItem(i, 0, item);
}
}
}
availableMasters.sort(); // Sort the masters alphabetically
availableMasters.sort(); // Sort the masters alphabetically
// Now we put the current plugin in the mDataFilesModel under its masters
QStandardItem *parent = new QStandardItem(availableMasters.join(","));
// Now we put the current plugin in the mDataFilesModel under its masters
QStandardItem *parent = new QStandardItem(availableMasters.join(","));
QString fileName = QString::fromStdString(boost::filesystem::path (iter->second.filename()).string());
QStandardItem *child = new QStandardItem(fileName);
QString fileName = QString::fromStdString(boost::filesystem::path (iter->second.filename()).string());
QStandardItem *child = new QStandardItem(fileName);
// Tooltip information
QString author = QString::fromStdString(fileReader.getAuthor());
float version = fileReader.getFVer();
QString description = QString::fromStdString(fileReader.getDesc());
// Tooltip information
QString author = QString::fromStdString(fileReader.getAuthor());
float version = fileReader.getFVer();
QString description = QString::fromStdString(fileReader.getDesc());
// For the date created/modified
QFileInfo fi(QString::fromStdString(iter->second.string()));
// For the date created/modified
QFileInfo fi(QString::fromStdString(iter->second.string()));
QString toolTip= QString("<b>Author:</b> %1<br/> \
<b>Version:</b> %2<br/><br/> \
<b>Description:</b><br/> \
%3<br/><br/> \
<b>Created on:</b> %4<br/> \
<b>Last modified:</b> %5")
.arg(author)
.arg(version)
.arg(description)
.arg(fi.created().toString(Qt::TextDate))
.arg(fi.lastModified().toString(Qt::TextDate));
QString toolTip= QString("<b>Author:</b> %1<br/> \
<b>Version:</b> %2<br/><br/> \
<b>Description:</b><br/> \
%3<br/><br/> \
<b>Created on:</b> %4<br/> \
<b>Last modified:</b> %5")
.arg(author)
.arg(version)
.arg(description)
.arg(fi.created().toString(Qt::TextDate))
.arg(fi.lastModified().toString(Qt::TextDate));
child->setToolTip(toolTip);
child->setToolTip(toolTip);
const QList<QStandardItem*> masterList = mDataFilesModel->findItems(availableMasters.join(","));
const QList<QStandardItem*> masterList = mDataFilesModel->findItems(availableMasters.join(","));
if (masterList.isEmpty()) { // Masters node not yet in the mDataFilesModel
parent->appendRow(child);
mDataFilesModel->appendRow(parent);
} else {
// Masters node exists, append current plugin
foreach (QStandardItem *currentItem, masterList) {
currentItem->appendRow(child);
if (masterList.isEmpty()) { // Masters node not yet in the mDataFilesModel
parent->appendRow(child);
mDataFilesModel->appendRow(parent);
} else {
// Masters node exists, append current plugin
foreach (QStandardItem *currentItem, masterList) {
currentItem->appendRow(child);
}
}
} catch(std::runtime_error &e) {
// An error occurred while reading the .esp
continue;
}
}
@ -1050,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,7 +14,8 @@ set(GAME_HEADER
source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap occlusionquery terrain terrainmaterial water
)
add_openmw_dir (mwinput
@ -38,13 +39,13 @@ add_openmw_dir (mwscript
)
add_openmw_dir (mwsound
soundmanager
soundmanager openal_output mpgsnd_decoder ffmpeg_decoder
)
add_openmw_dir (mwworld
refdata world physicssystem scene environment globals class action nullaction actionteleport
containerstore actiontalk actiontake manualref player cellfunctors
cells localscripts customdata weather inventorystore
cells localscripts customdata weather inventorystore ptr
)
add_openmw_dir (mwclass
@ -81,17 +82,23 @@ add_definitions(${SOUND_DEFINE})
target_link_libraries(openmw
${OGRE_LIBRARIES}
${OGRE_STATIC_PLUGINS}
${OGRE_Terrain_LIBRARY}
${OGRE_STATIC_PLUGINS}
${OIS_LIBRARIES}
${Boost_LIBRARIES}
${OPENAL_LIBRARY}
${SOUND_INPUT_LIBRARY}
${BULLET_LIBRARIES}
${MYGUI_LIBRARIES}
${MYGUI_PLATFORM_LIBRARIES}
components
MyGUIEngine
MyGUIOgrePlatform
)
# Fix for not visible pthreads functions for linker with glibc 2.15
if (UNIX AND NOT APPLE)
target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT})
endif()
if(APPLE)
find_library(CARBON_FRAMEWORK Carbon)
target_link_libraries(openmw ${CARBON_FRAMEWORK})

@ -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();
@ -208,13 +204,18 @@ OMW::Engine::~Engine()
void OMW::Engine::loadBSA()
{
const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
std::string dataDirectory;
for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter)
{
std::cout << "Adding " << iter->second.string() << std::endl;
Bsa::addBSA(iter->second.string());
}
dataDirectory = iter->second.parent_path().string();
const Files::PathContainer& dataDirs = mFileCollections.getPaths();
std::string dataDirectory;
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
dataDirectory = iter->string();
std::cout << "Data dir " << dataDirectory << std::endl;
Bsa::addDir(dataDirectory, mFSStrict);
}
@ -319,7 +320,11 @@ void OMW::Engine::go()
// This has to be added BEFORE MyGUI is initialized, as it needs
// to find core.xml here.
//addResourcesDirectory(mResDir);
addResourcesDirectory(mResDir / "mygui");
addResourcesDirectory(mResDir / "water");
// Create the window
mOgre->createWindow("OpenMW");
@ -337,10 +342,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,

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -60,7 +60,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -58,7 +58,7 @@ namespace MWClass
{
// TODO implement reading
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -57,7 +57,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -85,7 +85,7 @@ namespace MWClass
{
// TODO check for key
std::cout << "Locked container" << std::endl;
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
else
@ -100,7 +100,7 @@ namespace MWClass
{
// Trap activation goes here
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0);
ptr.getCellRef().trap = "";
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}

@ -73,7 +73,7 @@ namespace MWClass
// TODO check for key
// TODO report failure to player (message, sound?). Look up behaviour of original MW.
std::cout << "Locked!" << std::endl;
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
@ -81,7 +81,7 @@ namespace MWClass
{
// Trap activation
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0);
ptr.getCellRef().trap = "";
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
@ -110,7 +110,7 @@ namespace MWClass
// TODO return action for rotating the door
// This is a little pointless, but helps with testing
environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0, false);
environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0);
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
}
}

@ -54,7 +54,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -59,7 +59,7 @@ namespace MWClass
if (!ref->base->sound.empty())
{
environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, true);
environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop);
}
}
@ -83,7 +83,7 @@ namespace MWClass
if (!(ref->base->data.flags & ESM::Light::Carry))
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -58,7 +58,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -57,7 +57,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -56,7 +56,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -57,7 +57,7 @@ namespace MWClass
boost::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));

@ -145,8 +145,6 @@ namespace MWDialogue
bool DialogueManager::functionFilter(const MWWorld::Ptr& actor, const ESM::DialInfo& info,bool choice)
{
bool isAChoice = false;//is there any choice in the filters?
bool isFunction = false;
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.selects.begin());
iter != info.selects.end(); ++iter)
{
@ -154,7 +152,6 @@ namespace MWDialogue
char type = select.selectRule[1];
if(type == '1')
{
isFunction = true;
char comp = select.selectRule[4];
std::string name = select.selectRule.substr (5);
std::string function = select.selectRule.substr(2,2);
@ -193,7 +190,7 @@ namespace MWDialogue
break;
case 50://choice
isAChoice = true;
if(choice)
{
if(!selectCompare<int,int>(comp,mChoice,select.i)) return false;
@ -516,7 +513,6 @@ namespace MWDialogue
return false;
// TODO check DATAstruct
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator iter (info.selects.begin());
iter != info.selects.end(); ++iter)
if (!isMatching (actor, *iter))
@ -680,7 +676,8 @@ namespace MWDialogue
void DialogueManager::updateTopics()
{
std::list<std::string> keywordList;
int choice = mChoice;
mChoice = -1;
actorKnownTopics.clear();
MWGui::DialogueWindow* win = mEnvironment.mWindowManager->getDialogueWindow();
ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
@ -692,7 +689,7 @@ namespace MWDialogue
for (std::vector<ESM::DialInfo>::const_iterator iter (it->second.mInfo.begin());
iter!=it->second.mInfo.end(); ++iter)
{
if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,false))
if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true))
{
actorKnownTopics.push_back(it->first);
//does the player know the topic?
@ -706,6 +703,7 @@ namespace MWDialogue
}
}
win->setKeywords(keywordList);
mChoice = choice;
}
void DialogueManager::keywordSelected(std::string keyword)
@ -715,10 +713,9 @@ namespace MWDialogue
if(mDialogueMap.find(keyword) != mDialogueMap.end())
{
ESM::Dialogue ndialogue = mDialogueMap[keyword];
std::vector<ESM::DialInfo>::const_iterator iter;
if(ndialogue.type == ESM::Dialogue::Topic)
{
for (iter = ndialogue.mInfo.begin();
for (std::vector<ESM::DialInfo>::const_iterator iter = ndialogue.mInfo.begin();
iter!=ndialogue.mInfo.end(); ++iter)
{
if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true))
@ -742,6 +739,7 @@ namespace MWDialogue
}
}
}
updateTopics();
}

@ -21,18 +21,18 @@ BirthDialog::BirthDialog(WindowManager& parWindowManager)
getWidget(birthList, "BirthsignList");
birthList->setScrollVisible(true);
birthList->eventListSelectAccept = MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
birthList->eventListMouseItemActivate = MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
birthList->eventListChangePosition = MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
birthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
birthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
birthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &BirthDialog::onBackClicked);
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &BirthDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &BirthDialog::onOkClicked);
updateBirths();
updateSpells();
@ -100,7 +100,7 @@ void BirthDialog::onBackClicked(MyGUI::Widget* _sender)
eventBack();
}
void BirthDialog::onSelectBirth(MyGUI::List* _sender, size_t _index)
void BirthDialog::onSelectBirth(MyGUI::ListBox* _sender, size_t _index)
{
if (_index == MyGUI::ITEM_NONE)
return;
@ -188,7 +188,7 @@ void BirthDialog::updateSpells()
{
if (!categories[category].spells.empty())
{
MyGUI::StaticTextPtr label = spellArea->createWidget<MyGUI::StaticText>("SandBrightText", coord, MyGUI::Align::Default, std::string("Label"));
MyGUI::TextBox* label = spellArea->createWidget<MyGUI::TextBox>("SandBrightText", coord, MyGUI::Align::Default, std::string("Label"));
label->setCaption(mWindowManager.getGameSettingString(categories[category].label, ""));
spellItems.push_back(label);
coord.top += lineHeight;

@ -32,7 +32,7 @@ namespace MWGui
void open();
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Back button clicked.\n
signature : void method()\n
@ -40,7 +40,7 @@ namespace MWGui
EventHandle_Void eventBack;
protected:
void onSelectBirth(MyGUI::List* _sender, size_t _index);
void onSelectBirth(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);
@ -49,9 +49,9 @@ namespace MWGui
void updateBirths();
void updateSpells();
MyGUI::ListPtr birthList;
MyGUI::ListBox* birthList;
MyGUI::WidgetPtr spellArea;
MyGUI::StaticImagePtr birthImage;
MyGUI::ImageBox* birthImage;
std::vector<MyGUI::WidgetPtr> spellItems;
std::string currentBirthId;

@ -121,7 +121,7 @@ void CharacterCreation::spawnDialog(const char id)
mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name"));
mNameDialog->setTextInput(mPlayerName);
mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen);
mNameDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone);
mNameDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone);
mNameDialog->open();
break;
@ -131,8 +131,8 @@ void CharacterCreation::spawnDialog(const char id)
mRaceDialog = new RaceDialog(*mWM);
mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen);
mRaceDialog->setRaceId(mPlayerRaceId);
mRaceDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone);
mRaceDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack);
mRaceDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone);
mRaceDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack);
mRaceDialog->open();
break;
@ -140,7 +140,7 @@ void CharacterCreation::spawnDialog(const char id)
if (mClassChoiceDialog)
mWM->removeDialog(mClassChoiceDialog);
mClassChoiceDialog = new ClassChoiceDialog(*mWM);
mClassChoiceDialog->eventButtonSelected = MyGUI::newDelegate(this, &CharacterCreation::onClassChoice);
mClassChoiceDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassChoice);
mClassChoiceDialog->open();
break;
@ -150,8 +150,8 @@ void CharacterCreation::spawnDialog(const char id)
mPickClassDialog = new PickClassDialog(*mWM);
mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen);
mPickClassDialog->setClassId(mPlayerClass.name);
mPickClassDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone);
mPickClassDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack);
mPickClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone);
mPickClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack);
mPickClassDialog->open();
break;
@ -161,8 +161,8 @@ void CharacterCreation::spawnDialog(const char id)
mBirthSignDialog = new BirthDialog(*mWM);
mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen);
mBirthSignDialog->setBirthId(mPlayerBirthSignId);
mBirthSignDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone);
mBirthSignDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack);
mBirthSignDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone);
mBirthSignDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack);
mBirthSignDialog->open();
break;
@ -170,8 +170,8 @@ void CharacterCreation::spawnDialog(const char id)
if (mCreateClassDialog)
mWM->removeDialog(mCreateClassDialog);
mCreateClassDialog = new CreateClassDialog(*mWM);
mCreateClassDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone);
mCreateClassDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack);
mCreateClassDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone);
mCreateClassDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack);
mCreateClassDialog->open();
break;
case GM_ClassGenerate:
@ -212,9 +212,9 @@ void CharacterCreation::spawnDialog(const char id)
mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills);
}
mReviewDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone);
mReviewDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack);
mReviewDialog->eventActivateDialog = MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog);
mReviewDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone);
mReviewDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack);
mReviewDialog->eventActivateDialog += MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog);
mReviewDialog->open();
break;
}
@ -559,8 +559,8 @@ void CharacterCreation::showClassQuestionDialog()
mWM->removeDialog(mGenerateClassResultDialog);
mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM);
mGenerateClassResultDialog->setClassId(mGenerateClass);
mGenerateClassResultDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack);
mGenerateClassResultDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone);
mGenerateClassResultDialog->eventBack += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack);
mGenerateClassResultDialog->eventDone += MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone);
mGenerateClassResultDialog->open();
return;
}
@ -581,7 +581,7 @@ void CharacterCreation::showClassQuestionDialog()
buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[1]);
buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[2]);
mGenerateClassQuestionDialog->setButtons(buttons);
mGenerateClassQuestionDialog->eventButtonSelected = MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen);
mGenerateClassQuestionDialog->eventButtonSelected += MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen);
mGenerateClassQuestionDialog->open();
}

@ -29,11 +29,11 @@ GenerateClassResultDialog::GenerateClassResultDialog(WindowManager& parWindowMan
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &GenerateClassResultDialog::onOkClicked);
}
void GenerateClassResultDialog::open()
@ -96,20 +96,20 @@ PickClassDialog::PickClassDialog(WindowManager& parWindowManager)
getWidget(classList, "ClassList");
classList->setScrollVisible(true);
classList->eventListSelectAccept = MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
classList->eventListMouseItemActivate = MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
classList->eventListChangePosition = MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
classList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
classList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
classList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
getWidget(classImage, "ClassImage");
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &PickClassDialog::onBackClicked);
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &PickClassDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &PickClassDialog::onOkClicked);
updateClasses();
updateStats();
@ -177,7 +177,7 @@ void PickClassDialog::onBackClicked(MyGUI::Widget* _sender)
eventBack();
}
void PickClassDialog::onSelectClass(MyGUI::List* _sender, size_t _index)
void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index)
{
if (_index == MyGUI::ITEM_NONE)
return;
@ -248,7 +248,7 @@ void PickClassDialog::updateStats()
/* InfoBoxDialog */
void InfoBoxDialog::fitToText(MyGUI::StaticTextPtr widget)
void InfoBoxDialog::fitToText(MyGUI::TextBox* widget)
{
MyGUI::IntCoord inner = widget->getTextRegion();
MyGUI::IntCoord outer = widget->getCoord();
@ -267,7 +267,7 @@ void InfoBoxDialog::layoutVertically(MyGUI::WidgetPtr widget, int margin)
for (unsigned i = 0; i < count; ++i)
{
MyGUI::WidgetPtr child = widget->getChildAt(i);
if (!child->isVisible())
if (!child->getVisible())
continue;
child->setPosition(child->getLeft(), pos);
@ -322,7 +322,7 @@ void InfoBoxDialog::setButtons(ButtonList &buttons)
button->getSubWidgetText()->setWordWrap(true);
button->setCaption(text);
fitToText(button);
button->eventMouseButtonClick = MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &InfoBoxDialog::onButtonClicked);
coord.top += button->getHeight();
this->buttons.push_back(button);
}
@ -389,15 +389,15 @@ CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager)
setText("SpecializationT", mWindowManager.getGameSettingString("sChooseClassMenu1", "Specialization"));
getWidget(specializationName, "SpecializationName");
specializationName->setCaption(mWindowManager.getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Combat], ""));
specializationName->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked);
specializationName->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationClicked);
setText("FavoriteAttributesT", mWindowManager.getGameSettingString("sChooseClassMenu2", "Favorite Attributes:"));
getWidget(favoriteAttribute0, "FavoriteAttribute0");
getWidget(favoriteAttribute1, "FavoriteAttribute1");
favoriteAttribute0->setWindowManager(&mWindowManager);
favoriteAttribute1->setWindowManager(&mWindowManager);
favoriteAttribute0->eventClicked = MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked);
favoriteAttribute1->eventClicked = MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked);
favoriteAttribute0->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked);
favoriteAttribute1->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeClicked);
setText("MajorSkillT", mWindowManager.getGameSettingString("sSkillClassMajor", ""));
setText("MinorSkillT", mWindowManager.getGameSettingString("sSkillClassMinor", ""));
@ -414,7 +414,7 @@ CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager)
for (std::vector<Widgets::MWSkillPtr>::const_iterator it = skills.begin(); it != end; ++it)
{
(*it)->setWindowManager(&mWindowManager);
(*it)->eventClicked = MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked);
(*it)->eventClicked += MyGUI::newDelegate(this, &CreateClassDialog::onSkillClicked);
}
setText("LabelT", mWindowManager.getGameSettingString("sName", ""));
@ -426,15 +426,15 @@ CreateClassDialog::CreateClassDialog(WindowManager& parWindowManager)
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr descriptionButton;
getWidget(descriptionButton, "DescriptionButton");
descriptionButton->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked);
descriptionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionClicked);
MyGUI::ButtonPtr backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked);
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &CreateClassDialog::onOkClicked);
// Set default skills, attributes
@ -560,8 +560,8 @@ void CreateClassDialog::onSpecializationClicked(MyGUI::WidgetPtr _sender)
if (specDialog)
delete specDialog;
specDialog = new SelectSpecializationDialog(mWindowManager);
specDialog->eventCancel = MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);
specDialog->eventItemSelected = MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected);
specDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);
specDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSpecializationSelected);
specDialog->setVisible(true);
}
@ -578,8 +578,8 @@ void CreateClassDialog::onAttributeClicked(Widgets::MWAttributePtr _sender)
delete attribDialog;
attribDialog = new SelectAttributeDialog(mWindowManager);
attribDialog->setAffectedWidget(_sender);
attribDialog->eventCancel = MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);
attribDialog->eventItemSelected = MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected);
attribDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);
attribDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onAttributeSelected);
attribDialog->setVisible(true);
}
@ -607,8 +607,8 @@ void CreateClassDialog::onSkillClicked(Widgets::MWSkillPtr _sender)
delete skillDialog;
skillDialog = new SelectSkillDialog(mWindowManager);
skillDialog->setAffectedWidget(_sender);
skillDialog->eventCancel = MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);
skillDialog->eventItemSelected = MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected);
skillDialog->eventCancel += MyGUI::newDelegate(this, &CreateClassDialog::onDialogCancel);
skillDialog->eventItemSelected += MyGUI::newDelegate(this, &CreateClassDialog::onSkillSelected);
skillDialog->setVisible(true);
}
@ -638,7 +638,7 @@ void CreateClassDialog::onDescriptionClicked(MyGUI::Widget* _sender)
{
descDialog = new DescriptionDialog(mWindowManager);
descDialog->setTextInput(description);
descDialog->eventDone = MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered);
descDialog->eventDone += MyGUI::newDelegate(this, &CreateClassDialog::onDescriptionEntered);
descDialog->setVisible(true);
}
@ -672,18 +672,18 @@ SelectSpecializationDialog::SelectSpecializationDialog(WindowManager& parWindowM
getWidget(specialization1, "Specialization1");
getWidget(specialization2, "Specialization2");
specialization0->setCaption(mWindowManager.getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Combat], ""));
specialization0->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);
specialization0->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);
specialization1->setCaption(mWindowManager.getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Magic], ""));
specialization1->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);
specialization1->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);
specialization2->setCaption(mWindowManager.getGameSettingString(ESM::Class::gmstSpecializationIds[ESM::Class::Stealth], ""));
specialization2->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);
specialization2->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onSpecializationClicked);
specializationId = ESM::Class::Combat;
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", ""));
cancelButton->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked);
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSpecializationDialog::onCancelClicked);
}
// widget controls
@ -725,14 +725,14 @@ SelectAttributeDialog::SelectAttributeDialog(WindowManager& parWindowManager)
getWidget(attribute, std::string("Attribute").append(1, theIndex));
attribute->setWindowManager(&parWindowManager);
attribute->setAttributeId(ESM::Attribute::attributeIds[i]);
attribute->eventClicked = MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked);
attribute->eventClicked += MyGUI::newDelegate(this, &SelectAttributeDialog::onAttributeClicked);
}
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", ""));
cancelButton->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked);
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectAttributeDialog::onCancelClicked);
}
// widget controls
@ -813,7 +813,7 @@ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager)
{
skills[spec][i].widget->setWindowManager(&mWindowManager);
skills[spec][i].widget->setSkillId(skills[spec][i].skillId);
skills[spec][i].widget->eventClicked = MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked);
skills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked);
}
}
@ -821,7 +821,7 @@ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager)
MyGUI::ButtonPtr cancelButton;
getWidget(cancelButton, "CancelButton");
cancelButton->setCaption(mWindowManager.getGameSettingString("sCancel", ""));
cancelButton->eventMouseButtonClick = MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked);
cancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SelectSkillDialog::onCancelClicked);
}
// widget controls
@ -850,7 +850,7 @@ DescriptionDialog::DescriptionDialog(WindowManager& parWindowManager)
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DescriptionDialog::onOkClicked);
okButton->setCaption(mWindowManager.getGameSettingString("sInputMenu1", ""));
// Make sure the edit box has focus

@ -31,7 +31,7 @@ namespace MWGui
int getChosenButton() const;
// Events
typedef delegates::CDelegate1<int> EventHandle_Int;
typedef delegates::CMultiDelegate1<int> EventHandle_Int;
/** Event : Button was clicked.\n
signature : void method(MyGUI::WidgetPtr widget, int index)\n
@ -43,11 +43,11 @@ namespace MWGui
private:
void fitToText(MyGUI::StaticTextPtr widget);
void fitToText(MyGUI::TextBox* widget);
void layoutVertically(MyGUI::WidgetPtr widget, int margin);
int currentButton;
MyGUI::WidgetPtr textBox;
MyGUI::StaticTextPtr text;
MyGUI::TextBox* text;
MyGUI::WidgetPtr buttonBar;
std::vector<MyGUI::ButtonPtr> buttons;
};
@ -78,7 +78,7 @@ namespace MWGui
void open();
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Back button clicked.\n
signature : void method()\n
@ -90,8 +90,8 @@ namespace MWGui
void onBackClicked(MyGUI::Widget* _sender);
private:
MyGUI::StaticImagePtr classImage;
MyGUI::StaticTextPtr className;
MyGUI::ImageBox* classImage;
MyGUI::TextBox* className;
std::string currentClassId;
};
@ -108,7 +108,7 @@ namespace MWGui
void open();
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Back button clicked.\n
signature : void method()\n
@ -116,7 +116,7 @@ namespace MWGui
EventHandle_Void eventBack;
protected:
void onSelectClass(MyGUI::List* _sender, size_t _index);
void onSelectClass(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);
@ -125,9 +125,9 @@ namespace MWGui
void updateClasses();
void updateStats();
MyGUI::StaticImagePtr classImage;
MyGUI::ListPtr classList;
MyGUI::StaticTextPtr specializationName;
MyGUI::ImageBox* classImage;
MyGUI::ListBox* classList;
MyGUI::TextBox* specializationName;
Widgets::MWAttributePtr favoriteAttribute[2];
Widgets::MWSkillPtr majorSkill[5];
Widgets::MWSkillPtr minorSkill[5];
@ -143,7 +143,7 @@ namespace MWGui
ESM::Class::Specialization getSpecializationId() const { return specializationId; }
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Cancel button clicked.\n
signature : void method()\n
@ -160,7 +160,7 @@ namespace MWGui
void onCancelClicked(MyGUI::Widget* _sender);
private:
MyGUI::WidgetPtr specialization0, specialization1, specialization2;
MyGUI::TextBox *specialization0, *specialization1, *specialization2;
ESM::Class::Specialization specializationId;
};
@ -175,7 +175,7 @@ namespace MWGui
void setAffectedWidget(Widgets::MWAttributePtr widget) { affectedWidget = widget; }
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Cancel button clicked.\n
signature : void method()\n
@ -207,7 +207,7 @@ namespace MWGui
void setAffectedWidget(Widgets::MWSkillPtr widget) { affectedWidget = widget; }
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Cancel button clicked.\n
signature : void method()\n
@ -264,7 +264,7 @@ namespace MWGui
void open();
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Back button clicked.\n
signature : void method()\n
@ -287,7 +287,7 @@ namespace MWGui
private:
MyGUI::EditPtr editName;
MyGUI::WidgetPtr specializationName;
MyGUI::TextBox* specializationName;
Widgets::MWAttributePtr favoriteAttribute0, favoriteAttribute1;
Widgets::MWSkillPtr majorSkill[5];
Widgets::MWSkillPtr minorSkill[5];

@ -113,9 +113,9 @@ namespace MWGui
getWidget(history, "list_History");
// Set up the command line box
command->eventEditSelectAccept =
command->eventEditSelectAccept +=
newDelegate(this, &Console::acceptCommand);
command->eventKeyButtonPressed =
command->eventKeyButtonPressed +=
newDelegate(this, &Console::keyPress);
// Set up the log window
@ -139,6 +139,9 @@ namespace MWGui
void Console::disable()
{
setVisible(false);
// Remove keyboard focus from the console input whenever the
// console is turned off
MyGUI::InputManager::getInstance().setKeyFocusWidget(NULL);
}
void Console::setFont(const std::string &fntName)

@ -43,24 +43,28 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager,MWWorld::Environm
// Centre dialog
center();
//WindowManager *wm = environment.mWindowManager;
setText("NpcName", "Name of character");
//History view
getWidget(history, "History");
history->setOverflowToTheLeft(true);
history->getClient()->eventMouseButtonClick = MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked);
history->setMaxTextLength(1000000);
Widget* eventbox;
//An EditBox cannot receive mouse click events, so we use an
//invisible widget on top of the editbox to receive them
/// \todo scrolling the dialogue history with the mouse wheel doesn't work using this solution
getWidget(eventbox, "EventBox");
eventbox->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onHistoryClicked);
//Topics list
getWidget(topicsList, "TopicsList");
topicsList->setScrollVisible(true);
//topicsList->eventListSelectAccept = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
topicsList->eventListMouseItemActivate = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
//topicsList->eventListChangePosition = MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
//topicsList->eventListSelectAccept += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
topicsList->eventListMouseItemActivate += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
//topicsList->eventListChangePosition += MyGUI::newDelegate(this, &DialogueWindow::onSelectTopic);
MyGUI::ButtonPtr byeButton;
getWidget(byeButton, "ByeButton");
byeButton->eventMouseButtonClick = MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
byeButton->eventMouseButtonClick += MyGUI::newDelegate(this, &DialogueWindow::onByeClicked);
getWidget(pDispositionBar, "Disposition");
getWidget(pDispositionText,"DispositionText");
@ -68,11 +72,11 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager,MWWorld::Environm
void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender)
{
ISubWidgetText* t = history->getSubWidgetText();
ISubWidgetText* t = history->getClient()->getSubWidgetText();
if(t == nullptr)
return;
const IntPoint& lastPressed = InputManager::getInstance().getLastLeftPressed();
const IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left);
size_t cursorPosition = t->getCursorPosition(lastPressed);
MyGUI::UString color = history->getColorAtPos(cursorPosition);
@ -99,7 +103,7 @@ void DialogueWindow::onByeClicked(MyGUI::Widget* _sender)
mEnvironment.mDialogueManager->goodbyeSelected();
}
void DialogueWindow::onSelectTopic(MyGUI::List* _sender, size_t _index)
void DialogueWindow::onSelectTopic(MyGUI::ListBox* _sender, size_t _index)
{
if (_index == MyGUI::ITEM_NONE)
return;
@ -109,7 +113,8 @@ void DialogueWindow::onSelectTopic(MyGUI::List* _sender, size_t _index)
void DialogueWindow::startDialogue(std::string npcName)
{
setText("NpcName", npcName);
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(npcName);
adjustWindowCaption();
}
void DialogueWindow::setKeywords(std::list<std::string> keyWords)

@ -21,7 +21,7 @@ namespace MWWorld
namespace MWGui
{
class DialogeHistory;
class DialogueHistory;
using namespace MyGUI;
@ -33,7 +33,7 @@ namespace MWGui
void open();
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Dialog finished, OK button clicked.\n
signature : void method()\n
@ -49,7 +49,7 @@ namespace MWGui
void askQuestion(std::string question);
protected:
void onSelectTopic(MyGUI::List* _sender, size_t _index);
void onSelectTopic(MyGUI::ListBox* _sender, size_t _index);
void onByeClicked(MyGUI::Widget* _sender);
void onHistoryClicked(MyGUI::Widget* _sender);
@ -60,8 +60,8 @@ namespace MWGui
*/
std::string parseText(std::string text);
DialogeHistory* history;
MyGUI::ListPtr topicsList;
DialogueHistory* history;
MyGUI::ListBox* topicsList;
MyGUI::ProgressPtr pDispositionBar;
MyGUI::EditPtr pDispositionText;
std::map<std::string,std::string> pTopicsText;// this map links keyword and "real" text.

@ -13,10 +13,10 @@
using namespace MWGui;
using namespace Widgets;
UString DialogeHistory::getColorAtPos(size_t _pos)
UString DialogueHistory::getColorAtPos(size_t _pos)
{
UString colour = TextIterator::convertTagColour(mText->getTextColour());
TextIterator iterator(mText->getCaption());
UString colour = TextIterator::convertTagColour(getTextColour());
TextIterator iterator(getCaption());
while(iterator.moveNext())
{
size_t pos = iterator.getPosition();
@ -29,12 +29,12 @@ UString DialogeHistory::getColorAtPos(size_t _pos)
return colour;
}
UString DialogeHistory::getColorTextAt(size_t _pos)
UString DialogueHistory::getColorTextAt(size_t _pos)
{
bool breakOnNext = false;
UString colour = TextIterator::convertTagColour(mText->getTextColour());
UString colour = TextIterator::convertTagColour(getTextColour());
UString colour2 = colour;
TextIterator iterator(mText->getCaption());
TextIterator iterator(getCaption());
TextIterator col_start = iterator;
while(iterator.moveNext())
{
@ -59,7 +59,7 @@ UString DialogeHistory::getColorTextAt(size_t _pos)
return "";
}
void DialogeHistory::addDialogHeading(const UString& parText)
void DialogueHistory::addDialogHeading(const UString& parText)
{
UString head("\n#D8C09A");
head.append(parText);
@ -67,7 +67,7 @@ void DialogeHistory::addDialogHeading(const UString& parText)
addText(head);
}
void DialogeHistory::addDialogText(const UString& parText)
void DialogueHistory::addDialogText(const UString& parText)
{
addText(parText);
addText("\n");

@ -5,9 +5,9 @@
namespace MWGui
{
using namespace MyGUI;
class DialogeHistory : public MyGUI::Edit
class DialogueHistory : public MyGUI::EditBox
{
MYGUI_RTTI_DERIVED( DialogeHistory )
MYGUI_RTTI_DERIVED( DialogueHistory )
public:
Widget* getClient() { return mClient; }
UString getColorAtPos(size_t _pos);

@ -89,9 +89,9 @@ MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager)
getWidget(mLeftTextWidget, "LeftText");
getWidget(mRightTextWidget, "RightText");
getWidget(mPrevBtn, "PrevPageBTN");
mPrevBtn->eventMouseButtonClick = MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyPrevPage);
mPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyPrevPage);
getWidget(mNextBtn, "NextPageBTN");
mNextBtn->eventMouseButtonClick = MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyNextPage);
mNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this,&MWGui::JournalWindow::notifyNextPage);
//MyGUI::ItemBox* list = new MyGUI::ItemBox();
//list->addItem("qaq","aqzazaz");
//mScrollerWidget->addChildItem(list);
@ -111,7 +111,7 @@ MWGui::JournalWindow::JournalWindow (WindowManager& parWindowManager)
//displayLeftText(list.front());
MyGUI::WindowPtr t = static_cast<MyGUI::WindowPtr>(mMainWidget);
t->eventWindowChangeCoord = MyGUI::newDelegate(this, &JournalWindow::onWindowResize);
t->eventWindowChangeCoord += MyGUI::newDelegate(this, &JournalWindow::onWindowResize);
}
void MWGui::JournalWindow::open()

@ -41,7 +41,7 @@ namespace MWGui
static const int lineHeight;
MyGUI::WidgetPtr skillAreaWidget, skillClientWidget;
MyGUI::VScrollPtr skillScrollerWidget;
MyGUI::ScrollBar* skillScrollerWidget;
int lastPos, clientHeight;
MyGUI::EditPtr mLeftTextWidget;
MyGUI::EditPtr mRightTextWidget;
@ -54,4 +54,4 @@ namespace MWGui
}
#endif
#endif

@ -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,175 @@ 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)
, mVisible(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" );
}
LocalMapBase::LocalMapBase()
: mCurX(0)
, mCurY(0)
, mInterior(false)
, mLocalMap(NULL)
, mPrefix()
, mChanged(true)
, mLayout(NULL)
{
}
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,26 @@
namespace MWGui
{
class HUD : public OEngine::GUI::Layout
class LocalMapBase
{
public:
LocalMapBase();
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,40 +64,46 @@ 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::StaticImagePtr weapImage, spellImage;
MyGUI::ImageBox *weapImage, *spellImage;
MyGUI::ProgressPtr weapStatus, spellStatus;
MyGUI::WidgetPtr effectBox;
MyGUI::StaticImagePtr effect1;
MyGUI::StaticImagePtr minimap;
MyGUI::StaticImagePtr compass;
MyGUI::StaticImagePtr crosshair;
MyGUI::ImageBox* effect1;
MyGUI::ScrollView* minimap;
MyGUI::ImageBox* compass;
MyGUI::ImageBox* crosshair;
MyGUI::WidgetPtr fpsbox;
MyGUI::StaticTextPtr fpscounter;
MyGUI::StaticTextPtr trianglecounter;
MyGUI::StaticTextPtr batchcounter;
MyGUI::TextBox* fpscounter;
MyGUI::TextBox* trianglecounter;
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");
// Obviously you should override this later on
setCellName("No Cell Loaded");
}
void setCellName(const std::string& cellName)
{
mMainWidget->setCaption(cellName);
}
MapWindow();
virtual ~MapWindow(){}
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);
MyGUI::ScrollView* mGlobalMap;
MyGUI::ImageBox* mPlayerArrow;
MyGUI::Button* mButton;
MyGUI::IntPoint mLastDragPos;
bool mVisible;
bool mGlobal;
};
class MainMenu : public OEngine::GUI::Layout
@ -127,7 +154,7 @@ namespace MWGui
getWidget(avatar, "Avatar");
// Adjust armor rating text to bottom of avatar widget
MyGUI::StaticTextPtr armor_rating;
MyGUI::TextBox* armor_rating;
getWidget(armor_rating, "ArmorRating");
armor_rating->setCaption("Armor: 11");
MyGUI::IntCoord coord = armor_rating->getCoord();
@ -165,7 +192,7 @@ namespace MWGui
last_x += coord.width + margin;
button_pt->setCoord(coord);
button_pt->eventMouseButtonClick = MyGUI::newDelegate(this, &InventoryWindow::onCategorySelected);
button_pt->eventMouseButtonClick += MyGUI::newDelegate(this, &InventoryWindow::onCategorySelected);
}
}

@ -19,7 +19,7 @@ void MessageBoxManager::onFrame (float frameDuration)
if(it->current >= it->max)
{
it->messageBox->mMarkedToDelete = true;
if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one
{
// collect all with mMarkedToDelete and delete them.
@ -47,7 +47,7 @@ void MessageBoxManager::onFrame (float frameDuration)
it++;
}
}
if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) {
delete mInterMessageBoxe;
mInterMessageBoxe = NULL;
@ -57,20 +57,18 @@ void MessageBoxManager::onFrame (float frameDuration)
void MessageBoxManager::createMessageBox (const std::string& message)
{
std::cout << "MessageBox: " << message << std::endl;
MessageBox *box = new MessageBox(*this, message);
removeMessageBox(message.length()*mMessageBoxSpeed, box);
mMessageBoxes.push_back(box);
std::vector<MessageBox*>::iterator it;
if(mMessageBoxes.size() > 3) {
delete *mMessageBoxes.begin();
mMessageBoxes.erase(mMessageBoxes.begin());
}
int height = 0;
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
{
@ -88,9 +86,9 @@ bool MessageBoxManager::createInteractiveMessageBox (const std::string& message,
std::cout << "interactive MessageBox: " << message << " - ";
std::copy (buttons.begin(), buttons.end(), std::ostream_iterator<std::string> (std::cout, ", "));
std::cout << std::endl;
mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons);
return true;
}
@ -105,7 +103,7 @@ void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox)
timer.current = 0;
timer.max = time;
timer.messageBox = msgbox;
mTimers.insert(mTimers.end(), timer);
}
@ -152,25 +150,26 @@ MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::strin
mBottomPadding = 20;
mNextBoxPadding = 20;
mMarkedToDelete = false;
getWidget(mMessageWidget, "message");
mMessageWidget->setOverflowToTheLeft(true);
mMessageWidget->addText(cMessage);
MyGUI::IntSize size;
size.width = mFixedWidth;
size.height = 100; // dummy
MyGUI::IntCoord coord;
coord.left = 10; // dummy
coord.top = 10; // dummy
mMessageWidget->setSize(size);
MyGUI::IntSize textSize = mMessageWidget->_getTextSize();
MyGUI::IntSize textSize = mMessageWidget->getTextSize();
size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box
mMainWidget->setSize(size);
size.width -= 15; // this is to center the text (see messagebox_layout.xml, Widget type="Edit" position="-2 -3 0 0")
mMessageWidget->setSize(size);
@ -178,15 +177,15 @@ MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::strin
void MessageBox::update (int height)
{
MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize();
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::IntCoord coord;
coord.left = (gameWindowSize.width - mFixedWidth)/2;
coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding);
MyGUI::IntSize size;
size.width = mFixedWidth;
size.height = mHeight;
mMainWidget->setCoord(coord);
mMainWidget->setSize(size);
mMainWidget->setVisible(true);
@ -211,26 +210,26 @@ InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxMan
int buttonTopPadding = 5; // ^-- if vertical
int buttonPadding = 5; // padding between button label and button itself
int buttonMainPadding = 10; // padding between buttons and bottom of the main widget
mMarkedToDelete = false;
getWidget(mMessageWidget, "message");
getWidget(mButtonsWidget, "buttons");
mMessageWidget->setOverflowToTheLeft(true);
mMessageWidget->addText(message);
MyGUI::IntSize textSize = mMessageWidget->_getTextSize();
MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize();
MyGUI::IntSize textSize = mMessageWidget->getTextSize();
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
int biggestButtonWidth = 0;
int buttonWidth = 0;
int buttonsWidth = 0;
int buttonHeight = 0;
MyGUI::IntCoord dummyCoord(0, 0, 0, 0);
std::vector<std::string>::const_iterator it;
for(it = buttons.begin(); it != buttons.end(); ++it)
{
@ -240,28 +239,28 @@ InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxMan
dummyCoord,
MyGUI::Align::Default);
button->setCaption(*it);
button->eventMouseButtonClick = MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed);
mButtons.push_back(button);
buttonWidth = button->_getTextSize().width + 2*buttonPadding + buttonLeftPadding;
buttonWidth = button->getTextSize().width + 2*buttonPadding + buttonLeftPadding;
buttonsWidth += buttonWidth;
buttonHeight = button->_getTextSize().height + 2*buttonPadding + buttonTopPadding;
buttonHeight = button->getTextSize().height + 2*buttonPadding + buttonTopPadding;
if(buttonWidth > biggestButtonWidth)
{
biggestButtonWidth = buttonWidth;
}
}
buttonsWidth += buttonLeftPadding;
MyGUI::IntSize mainWidgetSize;
if(buttonsWidth < fixedWidth)
{
// on one line
std::cout << "on one line" << std::endl;
if(textSize.width + 2*textPadding < buttonsWidth)
{
std::cout << "width = buttonsWidth" << std::endl;
@ -272,48 +271,48 @@ InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxMan
mainWidgetSize.width = textSize.width + 3*textPadding;
}
mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;
MyGUI::IntCoord absCoord;
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl;
std::cout << "left " << absCoord.left << " top " << absCoord.top << std::endl;
mMainWidget->setCoord(absCoord);
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding;
mMessageWidget->setCoord(messageWidgetCoord);
mMessageWidget->setSize(textSize);
MyGUI::IntCoord buttonCord;
MyGUI::IntSize buttonSize(0, buttonHeight);
int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding;
std::vector<MyGUI::ButtonPtr>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button)
{
buttonCord.left = left;
buttonCord.top = textSize.height + textButtonPadding;
buttonSize.width = (*button)->_getTextSize().width + 2*buttonPadding;
buttonSize.height = (*button)->_getTextSize().height + 2*buttonPadding;
buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding;
buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding;
(*button)->setCoord(buttonCord);
(*button)->setSize(buttonSize);
left += buttonSize.width + buttonLeftPadding;
}
}
else
{
// among each other
if(biggestButtonWidth > textSize.width) {
mainWidgetSize.width = biggestButtonWidth + buttonTopPadding;
}
@ -321,46 +320,46 @@ InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxMan
mainWidgetSize.width = textSize.width + 3*textPadding;
}
mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding;
std::cout << "biggestButtonWidth " << biggestButtonWidth << " textSize.width " << textSize.width << std::endl;
std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl;
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord absCoord;
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
mMainWidget->setCoord(absCoord);
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding;
mMessageWidget->setCoord(messageWidgetCoord);
mMessageWidget->setSize(textSize);
MyGUI::IntCoord buttonCord;
MyGUI::IntSize buttonSize(0, buttonHeight);
int top = textButtonPadding + buttonTopPadding + textSize.height;
std::vector<MyGUI::ButtonPtr>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button)
{
buttonSize.width = (*button)->_getTextSize().width + buttonPadding*2;
buttonSize.height = (*button)->_getTextSize().height + buttonPadding*2;
buttonSize.width = (*button)->getTextSize().width + buttonPadding*2;
buttonSize.height = (*button)->getTextSize().height + buttonPadding*2;
buttonCord.top = top;
buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/
(*button)->setCoord(buttonCord);
(*button)->setSize(buttonSize);
top += buttonSize.height + 2*buttonTopPadding;
}
}
}
@ -387,8 +386,3 @@ int InteractiveMessageBox::readPressedButton ()
mButtonPressed = -1;
return pressed;
}

@ -7,6 +7,7 @@
#include "window_base.hpp"
#include "window_manager.hpp"
#undef MessageBox
namespace MWGui
{

@ -34,7 +34,7 @@ RaceDialog::RaceDialog(WindowManager& parWindowManager)
headRotate->setScrollRange(50);
headRotate->setScrollPosition(20);
headRotate->setScrollViewPage(10);
headRotate->eventScrollChangePosition = MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);
headRotate->eventScrollChangePosition += MyGUI::newDelegate(this, &RaceDialog::onHeadRotate);
// Set up next/previous buttons
MyGUI::ButtonPtr prevButton, nextButton;
@ -42,27 +42,27 @@ RaceDialog::RaceDialog(WindowManager& parWindowManager)
setText("GenderChoiceT", mWindowManager.getGameSettingString("sRaceMenu2", "Change Sex"));
getWidget(prevButton, "PrevGenderButton");
getWidget(nextButton, "NextGenderButton");
prevButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender);
nextButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender);
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousGender);
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextGender);
setText("FaceChoiceT", mWindowManager.getGameSettingString("sRaceMenu3", "Change Face"));
getWidget(prevButton, "PrevFaceButton");
getWidget(nextButton, "NextFaceButton");
prevButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace);
nextButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace);
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousFace);
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextFace);
setText("HairChoiceT", mWindowManager.getGameSettingString("sRaceMenu3", "Change Hair"));
getWidget(prevButton, "PrevHairButton");
getWidget(nextButton, "NextHairButton");
prevButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair);
nextButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair);
prevButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectPreviousHair);
nextButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onSelectNextHair);
setText("RaceT", mWindowManager.getGameSettingString("sRaceMenu4", "Race"));
getWidget(raceList, "RaceList");
raceList->setScrollVisible(true);
raceList->eventListSelectAccept = MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
raceList->eventListMouseItemActivate = MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
raceList->eventListChangePosition = MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
raceList->eventListSelectAccept += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
raceList->eventListMouseItemActivate += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
raceList->eventListChangePosition += MyGUI::newDelegate(this, &RaceDialog::onSelectRace);
setText("SkillsT", mWindowManager.getGameSettingString("sBonusSkillTitle", "Skill Bonus"));
getWidget(skillList, "SkillList");
@ -72,11 +72,11 @@ RaceDialog::RaceDialog(WindowManager& parWindowManager)
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onBackClicked);
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &RaceDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &RaceDialog::onOkClicked);
updateRaces();
updateSkills();
@ -157,7 +157,7 @@ void RaceDialog::onBackClicked(MyGUI::Widget* _sender)
eventBack();
}
void RaceDialog::onHeadRotate(MyGUI::VScroll*, size_t _position)
void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position)
{
// TODO: Rotate head
}
@ -192,7 +192,7 @@ void RaceDialog::onSelectNextHair(MyGUI::Widget*)
hairIndex = wrap(hairIndex - 1, hairCount);
}
void RaceDialog::onSelectRace(MyGUI::List* _sender, size_t _index)
void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index)
{
if (_index == MyGUI::ITEM_NONE)
return;

@ -46,7 +46,7 @@ namespace MWGui
void open();
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate0 EventHandle_Void;
/** Event : Back button clicked.\n
signature : void method()\n
@ -54,7 +54,7 @@ namespace MWGui
EventHandle_Void eventBack;
protected:
void onHeadRotate(MyGUI::VScroll* _sender, size_t _position);
void onHeadRotate(MyGUI::ScrollBar* _sender, size_t _position);
void onSelectPreviousGender(MyGUI::Widget* _sender);
void onSelectNextGender(MyGUI::Widget* _sender);
@ -65,7 +65,7 @@ namespace MWGui
void onSelectPreviousHair(MyGUI::Widget* _sender);
void onSelectNextHair(MyGUI::Widget* _sender);
void onSelectRace(MyGUI::List* _sender, size_t _index);
void onSelectRace(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender);
@ -76,8 +76,8 @@ namespace MWGui
void updateSpellPowers();
MyGUI::CanvasPtr appearanceBox;
MyGUI::ListPtr raceList;
MyGUI::HScrollPtr headRotate;
MyGUI::ListBox* raceList;
MyGUI::ScrollBar* headRotate;
MyGUI::WidgetPtr skillList;
std::vector<MyGUI::WidgetPtr> skillItems;

@ -28,22 +28,22 @@ ReviewDialog::ReviewDialog(WindowManager& parWindowManager)
getWidget(nameWidget, "NameText");
getWidget(button, "NameButton");
button->setCaption(mWindowManager.getGameSettingString("sName", ""));
button->eventMouseButtonClick = MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onNameClicked);;
getWidget(raceWidget, "RaceText");
getWidget(button, "RaceButton");
button->setCaption(mWindowManager.getGameSettingString("sRace", ""));
button->eventMouseButtonClick = MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onRaceClicked);;
getWidget(classWidget, "ClassText");
getWidget(button, "ClassButton");
button->setCaption(mWindowManager.getGameSettingString("sClass", ""));
button->eventMouseButtonClick = MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onClassClicked);;
getWidget(birthSignWidget, "SignText");
getWidget(button, "SignButton");
button->setCaption(mWindowManager.getGameSettingString("sBirthSign", ""));
button->eventMouseButtonClick = MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBirthSignClicked);;
// Setup dynamic stats
getWidget(health, "Health");
@ -75,25 +75,25 @@ ReviewDialog::ReviewDialog(WindowManager& parWindowManager)
getWidget(skillClientWidget, "SkillClient");
getWidget(skillScrollerWidget, "SkillScroller");
skillScrollerWidget->eventScrollChangePosition = MyGUI::newDelegate(this, &ReviewDialog::onScrollChangePosition);
skillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &ReviewDialog::onScrollChangePosition);
updateScroller();
for (int i = 0; i < ESM::Skill::Length; ++i)
{
skillValues.insert(std::make_pair(i, MWMechanics::Stat<float>()));
skillWidgetMap.insert(std::make_pair(i, static_cast<MyGUI::StaticText*> (0)));
skillWidgetMap.insert(std::make_pair(i, static_cast<MyGUI::TextBox*> (0)));
}
static_cast<MyGUI::WindowPtr>(mMainWidget)->eventWindowChangeCoord = MyGUI::newDelegate(this, &ReviewDialog::onWindowResize);
static_cast<MyGUI::WindowPtr>(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &ReviewDialog::onWindowResize);
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr backButton;
getWidget(backButton, "BackButton");
backButton->eventMouseButtonClick = MyGUI::newDelegate(this, &ReviewDialog::onBackClicked);
backButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onBackClicked);
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &ReviewDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &ReviewDialog::onOkClicked);
}
void ReviewDialog::open()
@ -102,7 +102,7 @@ void ReviewDialog::open()
setVisible(true);
}
void ReviewDialog::onScrollChangePosition(MyGUI::VScrollPtr scroller, size_t pos)
void ReviewDialog::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos)
{
int diff = lastPos - pos;
// Adjust position of all widget according to difference
@ -176,7 +176,7 @@ void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const M
void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::Stat<float>& value)
{
skillValues[skillId] = value;
MyGUI::StaticTextPtr widget = skillWidgetMap[skillId];
MyGUI::TextBox* widget = skillWidgetMap[skillId];
if (widget)
{
float modified = value.getModified(), base = value.getBase();
@ -210,7 +210,7 @@ void ReviewDialog::configureSkills(const std::vector<int>& major, const std::vec
}
}
void ReviewDialog::setStyledText(MyGUI::StaticTextPtr widget, ColorStyle style, const std::string &value)
void ReviewDialog::setStyledText(MyGUI::TextBox* widget, ColorStyle style, const std::string &value)
{
widget->setCaption(value);
if (style == CS_Super)
@ -223,7 +223,7 @@ void ReviewDialog::setStyledText(MyGUI::StaticTextPtr widget, ColorStyle style,
void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticImagePtr separator = skillClientWidget->createWidget<MyGUI::StaticImage>("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default);
MyGUI::ImageBox* separator = skillClientWidget->createWidget<MyGUI::ImageBox>("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default);
skillWidgets.push_back(separator);
coord1.top += separator->getHeight();
@ -232,7 +232,7 @@ void ReviewDialog::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2
void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticTextPtr groupWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default);
MyGUI::TextBox* groupWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default);
groupWidget->setCaption(label);
skillWidgets.push_back(groupWidget);
@ -240,14 +240,15 @@ void ReviewDialog::addGroup(const std::string &label, MyGUI::IntCoord &coord1, M
coord2.top += lineHeight;
}
MyGUI::StaticTextPtr ReviewDialog::addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
MyGUI::TextBox* ReviewDialog::addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticTextPtr skillNameWidget, skillValueWidget;
MyGUI::TextBox* skillNameWidget;
MyGUI::TextBox* skillValueWidget;
skillNameWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandText", coord1, MyGUI::Align::Default);
skillNameWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandText", coord1, MyGUI::Align::Default);
skillNameWidget->setCaption(text);
skillValueWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandTextRight", coord2, MyGUI::Align::Default);
skillValueWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Default);
setStyledText(skillValueWidget, style, value);
skillWidgets.push_back(skillNameWidget);
@ -261,9 +262,9 @@ MyGUI::StaticTextPtr ReviewDialog::addValueItem(const std::string text, const st
void ReviewDialog::addItem(const std::string text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticTextPtr skillNameWidget;
MyGUI::TextBox* skillNameWidget;
skillNameWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
skillNameWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
skillNameWidget->setCaption(text);
skillWidgets.push_back(skillNameWidget);
@ -299,7 +300,7 @@ void ReviewDialog::addSkills(const SkillList &skills, const std::string &titleId
style = CS_Super;
else if (modified < base)
style = CS_Sub;
MyGUI::StaticTextPtr widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), boost::lexical_cast<std::string>(static_cast<int>(modified)), style, coord1, coord2);
MyGUI::TextBox* widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), boost::lexical_cast<std::string>(static_cast<int>(modified)), style, coord1, coord2);
skillWidgetMap[skillId] = widget;
}
}

@ -49,8 +49,8 @@ namespace MWGui
void open();
// Events
typedef delegates::CDelegate0 EventHandle_Void;
typedef delegates::CDelegate1<int> EventHandle_Int;
typedef delegates::CMultiDelegate0 EventHandle_Void;
typedef delegates::CMultiDelegate1<int> EventHandle_Int;
/** Event : Back button clicked.\n
signature : void method()\n
@ -75,23 +75,23 @@ namespace MWGui
CS_Normal,
CS_Super
};
void setStyledText(MyGUI::StaticTextPtr widget, ColorStyle style, const std::string &value);
void setStyledText(MyGUI::TextBox* widget, ColorStyle style, const std::string &value);
void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
MyGUI::StaticTextPtr addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
MyGUI::TextBox* addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addItem(const std::string text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void updateScroller();
void updateSkillArea();
void onScrollChangePosition(MyGUI::VScrollPtr scroller, size_t pos);
void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos);
void onWindowResize(MyGUI::Window* window);
static const int lineHeight;
MyGUI::StaticTextPtr nameWidget, raceWidget, classWidget, birthSignWidget;
MyGUI::TextBox *nameWidget, *raceWidget, *classWidget, *birthSignWidget;
MyGUI::WidgetPtr skillAreaWidget, skillClientWidget;
MyGUI::VScrollPtr skillScrollerWidget;
MyGUI::ScrollBar* skillScrollerWidget;
int lastPos, clientHeight;
Widgets::MWDynamicStatPtr health, magicka, fatigue;
@ -100,7 +100,7 @@ namespace MWGui
SkillList majorSkills, minorSkills, miscSkills;
std::map<int, MWMechanics::Stat<float> > skillValues;
std::map<int, MyGUI::StaticTextPtr> skillWidgetMap;
std::map<int, MyGUI::TextBox*> skillWidgetMap;
std::string name, raceId, birthSignId;
ESM::Class klass;
std::vector<MyGUI::WidgetPtr> skillWidgets; //< Skills and other information

@ -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);
@ -48,20 +61,20 @@ StatsWindow::StatsWindow (WindowManager& parWindowManager)
getWidget(skillClientWidget, "SkillClient");
getWidget(skillScrollerWidget, "SkillScroller");
skillScrollerWidget->eventScrollChangePosition = MyGUI::newDelegate(this, &StatsWindow::onScrollChangePosition);
skillScrollerWidget->eventScrollChangePosition += MyGUI::newDelegate(this, &StatsWindow::onScrollChangePosition);
updateScroller();
for (int i = 0; i < ESM::Skill::Length; ++i)
{
skillValues.insert(std::pair<int, MWMechanics::Stat<float> >(i, MWMechanics::Stat<float>()));
skillWidgetMap.insert(std::pair<int, MyGUI::StaticTextPtr>(i, nullptr));
skillWidgetMap.insert(std::pair<int, MyGUI::TextBox*>(i, nullptr));
}
MyGUI::WindowPtr t = static_cast<MyGUI::WindowPtr>(mMainWidget);
t->eventWindowChangeCoord = MyGUI::newDelegate(this, &StatsWindow::onWindowResize);
t->eventWindowChangeCoord += MyGUI::newDelegate(this, &StatsWindow::onWindowResize);
}
void StatsWindow::onScrollChangePosition(MyGUI::VScrollPtr scroller, size_t pos)
void StatsWindow::onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos)
{
int diff = lastPos - pos;
// Adjust position of all widget according to difference
@ -95,10 +108,10 @@ void StatsWindow::setBar(const std::string& name, const std::string& tname, int
void StatsWindow::setPlayerName(const std::string& playerName)
{
mMainWidget->setCaption(playerName);
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(playerName);
}
void StatsWindow::setStyledText(MyGUI::StaticTextPtr widget, ColorStyle style, const std::string &value)
void StatsWindow::setStyledText(MyGUI::TextBox* widget, ColorStyle style, const std::string &value)
{
widget->setCaption(value);
if (style == CS_Super)
@ -175,7 +188,7 @@ void StatsWindow::setValue (const std::string& id, int value)
void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat<float>& value)
{
skillValues[parSkill] = value;
MyGUI::StaticTextPtr widget = skillWidgetMap[(int)parSkill];
MyGUI::TextBox* widget = skillWidgetMap[(int)parSkill];
if (widget)
{
float modified = value.getModified(), base = value.getBase();
@ -221,7 +234,7 @@ void StatsWindow::setBirthSign (const std::string& signId)
void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticImagePtr separator = skillClientWidget->createWidget<MyGUI::StaticImage>("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default);
MyGUI::ImageBox* separator = skillClientWidget->createWidget<MyGUI::ImageBox>("MW_HLine", MyGUI::IntCoord(10, coord1.top, coord1.width + coord2.width - 4, 18), MyGUI::Align::Default);
skillWidgets.push_back(separator);
coord1.top += separator->getHeight();
@ -230,7 +243,7 @@ void StatsWindow::addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticTextPtr groupWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default);
MyGUI::TextBox* groupWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandBrightText", MyGUI::IntCoord(0, coord1.top, coord1.width + coord2.width, coord1.height), MyGUI::Align::Default);
groupWidget->setCaption(label);
skillWidgets.push_back(groupWidget);
@ -238,14 +251,14 @@ void StatsWindow::addGroup(const std::string &label, MyGUI::IntCoord &coord1, My
coord2.top += lineHeight;
}
MyGUI::StaticTextPtr StatsWindow::addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
MyGUI::TextBox* StatsWindow::addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticTextPtr skillNameWidget, skillValueWidget;
MyGUI::TextBox *skillNameWidget, *skillValueWidget;
skillNameWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandText", coord1, MyGUI::Align::Default);
skillNameWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandText", coord1, MyGUI::Align::Default);
skillNameWidget->setCaption(text);
skillValueWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandTextRight", coord2, MyGUI::Align::Default);
skillValueWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandTextRight", coord2, MyGUI::Align::Default);
setStyledText(skillValueWidget, style, value);
skillWidgets.push_back(skillNameWidget);
@ -259,9 +272,9 @@ MyGUI::StaticTextPtr StatsWindow::addValueItem(const std::string text, const std
void StatsWindow::addItem(const std::string text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2)
{
MyGUI::StaticTextPtr skillNameWidget;
MyGUI::TextBox* skillNameWidget;
skillNameWidget = skillClientWidget->createWidget<MyGUI::StaticText>("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
skillNameWidget = skillClientWidget->createWidget<MyGUI::TextBox>("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
skillNameWidget->setCaption(text);
skillWidgets.push_back(skillNameWidget);
@ -297,7 +310,7 @@ void StatsWindow::addSkills(const SkillList &skills, const std::string &titleId,
style = CS_Super;
else if (modified < base)
style = CS_Sub;
MyGUI::StaticTextPtr widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), boost::lexical_cast<std::string>(static_cast<int>(modified)), style, coord1, coord2);
MyGUI::TextBox* widget = addValueItem(mWindowManager.getGameSettingString(skillNameId, skillNameId), boost::lexical_cast<std::string>(static_cast<int>(modified)), style, coord1, coord2);
skillWidgetMap[skillId] = widget;
}
}

@ -49,26 +49,26 @@ namespace MWGui
CS_Normal,
CS_Super
};
void setStyledText(MyGUI::StaticTextPtr widget, ColorStyle style, const std::string &value);
void setStyledText(MyGUI::TextBox* widget, ColorStyle style, const std::string &value);
void addSkills(const SkillList &skills, const std::string &titleId, const std::string &titleDefault, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addSeparator(MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addGroup(const std::string &label, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
MyGUI::StaticTextPtr addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
MyGUI::TextBox* addValueItem(const std::string text, const std::string &value, ColorStyle style, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void addItem(const std::string text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2);
void updateScroller();
void onScrollChangePosition(MyGUI::VScrollPtr scroller, size_t pos);
void onScrollChangePosition(MyGUI::ScrollBar* scroller, size_t pos);
void onWindowResize(MyGUI::Window* window);
static const int lineHeight;
MyGUI::WidgetPtr skillAreaWidget, skillClientWidget;
MyGUI::VScrollPtr skillScrollerWidget;
MyGUI::ScrollBar* skillScrollerWidget;
int lastPos, clientHeight;
SkillList majorSkills, minorSkills, miscSkills;
std::map<int, MWMechanics::Stat<float> > skillValues;
std::map<int, MyGUI::StaticTextPtr> skillWidgetMap;
std::map<int, MyGUI::TextBox*> skillWidgetMap;
std::map<std::string, MyGUI::WidgetPtr> factionWidgetMap;
FactionList factions; ///< Stores a list of factions and the current rank
std::string birthSignId;

@ -10,12 +10,12 @@ TextInputDialog::TextInputDialog(WindowManager& parWindowManager)
center();
getWidget(textEdit, "TextEdit");
textEdit->eventEditSelectAccept = newDelegate(this, &TextInputDialog::onTextAccepted);
textEdit->eventEditSelectAccept += newDelegate(this, &TextInputDialog::onTextAccepted);
// TODO: These buttons should be managed by a Dialog class
MyGUI::ButtonPtr okButton;
getWidget(okButton, "OKButton");
okButton->eventMouseButtonClick = MyGUI::newDelegate(this, &TextInputDialog::onOkClicked);
okButton->eventMouseButtonClick += MyGUI::newDelegate(this, &TextInputDialog::onOkClicked);
// Make sure the edit box has focus
MyGUI::InputManager::getInstance().setKeyFocusWidget(textEdit);

@ -62,24 +62,24 @@ void MWSkill::updateWidgets()
{
if (skillId == ESM::Skill::Length)
{
skillNameWidget->setCaption("");
static_cast<MyGUI::TextBox*>(skillNameWidget)->setCaption("");
}
else
{
const std::string &name = manager->getGameSettingString(ESM::Skill::sSkillNameIds[skillId], "");
skillNameWidget->setCaption(name);
static_cast<MyGUI::TextBox*>(skillNameWidget)->setCaption(name);
}
}
if (skillValueWidget)
{
SkillValue::Type modified = value.getModified(), base = value.getBase();
skillValueWidget->setCaption(boost::lexical_cast<std::string>(modified));
static_cast<MyGUI::TextBox*>(skillValueWidget)->setCaption(boost::lexical_cast<std::string>(modified));
if (modified > base)
skillValueWidget->setState("increased");
skillValueWidget->_setWidgetState("increased");
else if (modified < base)
skillValueWidget->setState("decreased");
skillValueWidget->_setWidgetState("decreased");
else
skillValueWidget->setState("normal");
skillValueWidget->_setWidgetState("normal");
}
}
@ -88,59 +88,32 @@ void MWSkill::onClicked(MyGUI::Widget* _sender)
eventClicked(this);
}
void MWSkill::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
{
Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
initialiseWidgetSkin(_info);
}
MWSkill::~MWSkill()
{
shutdownWidgetSkin();
}
void MWSkill::baseChangeWidgetSkin(ResourceSkin* _info)
void MWSkill::initialiseOverride()
{
shutdownWidgetSkin();
Base::baseChangeWidgetSkin(_info);
initialiseWidgetSkin(_info);
}
Base::initialiseOverride();
void MWSkill::initialiseWidgetSkin(ResourceSkin* _info)
{
for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
assignWidget(skillNameWidget, "StatName");
assignWidget(skillValueWidget, "StatValue");
MyGUI::ButtonPtr button;
assignWidget(button, "StatNameButton");
if (button)
{
const std::string &name = *(*iter)->_getInternalData<std::string>();
if (name == "StatName")
{
MYGUI_DEBUG_ASSERT( ! skillNameWidget, "widget already assigned");
skillNameWidget = (*iter)->castType<StaticText>();
}
else if (name == "StatValue")
{
MYGUI_DEBUG_ASSERT( ! skillValueWidget, "widget already assigned");
skillValueWidget = (*iter)->castType<StaticText>();
}
else if (name == "StatNameButton")
{
MYGUI_DEBUG_ASSERT( ! skillNameWidget, "widget already assigned");
MyGUI::ButtonPtr button = (*iter)->castType<Button>();
skillNameWidget = button;
button->eventMouseButtonClick = MyGUI::newDelegate(this, &MWSkill::onClicked);
}
else if (name == "StatValueButton")
{
MYGUI_DEBUG_ASSERT( ! skillValueWidget, "widget already assigned");
MyGUI::ButtonPtr button = (*iter)->castType<Button>();
skillNameWidget = button;
button->eventMouseButtonClick = MyGUI::newDelegate(this, &MWSkill::onClicked);
}
skillNameWidget = button;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked);
}
}
void MWSkill::shutdownWidgetSkin()
{
button = 0;
assignWidget(button, "StatValueButton");
if (button)
{
skillNameWidget = button;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWSkill::onClicked);
}
}
/* MWAttribute */
@ -176,7 +149,7 @@ void MWAttribute::updateWidgets()
{
if (id < 0 || id >= 8)
{
attributeNameWidget->setCaption("");
static_cast<MyGUI::TextBox*>(attributeNameWidget)->setCaption("");
}
else
{
@ -191,75 +164,48 @@ void MWAttribute::updateWidgets()
"sAttributeLuck"
};
const std::string &name = manager->getGameSettingString(attributes[id], "");
attributeNameWidget->setCaption(name);
static_cast<MyGUI::TextBox*>(attributeNameWidget)->setCaption(name);
}
}
if (attributeValueWidget)
{
AttributeValue::Type modified = value.getModified(), base = value.getBase();
attributeValueWidget->setCaption(boost::lexical_cast<std::string>(modified));
static_cast<MyGUI::TextBox*>(attributeValueWidget)->setCaption(boost::lexical_cast<std::string>(modified));
if (modified > base)
attributeValueWidget->setState("increased");
attributeValueWidget->_setWidgetState("increased");
else if (modified < base)
attributeValueWidget->setState("decreased");
attributeValueWidget->_setWidgetState("decreased");
else
attributeValueWidget->setState("normal");
attributeValueWidget->_setWidgetState("normal");
}
}
void MWAttribute::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
{
Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
initialiseWidgetSkin(_info);
}
MWAttribute::~MWAttribute()
{
shutdownWidgetSkin();
}
void MWAttribute::baseChangeWidgetSkin(ResourceSkin* _info)
void MWAttribute::initialiseOverride()
{
shutdownWidgetSkin();
Base::baseChangeWidgetSkin(_info);
initialiseWidgetSkin(_info);
}
Base::initialiseOverride();
void MWAttribute::initialiseWidgetSkin(ResourceSkin* _info)
{
for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
assignWidget(attributeNameWidget, "StatName");
assignWidget(attributeValueWidget, "StatValue");
MyGUI::ButtonPtr button;
assignWidget(button, "StatNameButton");
if (button)
{
const std::string &name = *(*iter)->_getInternalData<std::string>();
if (name == "StatName")
{
MYGUI_DEBUG_ASSERT( ! attributeNameWidget, "widget already assigned");
attributeNameWidget = (*iter)->castType<StaticText>();
}
else if (name == "StatValue")
{
MYGUI_DEBUG_ASSERT( ! attributeValueWidget, "widget already assigned");
attributeValueWidget = (*iter)->castType<StaticText>();
}
else if (name == "StatNameButton")
{
MYGUI_DEBUG_ASSERT( ! attributeNameWidget, "widget already assigned");
MyGUI::ButtonPtr button = (*iter)->castType<Button>();
attributeNameWidget = button;
button->eventMouseButtonClick = MyGUI::newDelegate(this, &MWAttribute::onClicked);
}
else if (name == "StatValue")
{
MYGUI_DEBUG_ASSERT( ! attributeValueWidget, "widget already assigned");
MyGUI::ButtonPtr button = (*iter)->castType<Button>();
attributeNameWidget = button;
button->eventMouseButtonClick = MyGUI::newDelegate(this, &MWAttribute::onClicked);
}
attributeNameWidget = button;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked);
}
}
void MWAttribute::shutdownWidgetSkin()
{
button = 0;
assignWidget(button, "StatValueButton");
if (button)
{
attributeValueWidget = button;
button->eventMouseButtonClick += MyGUI::newDelegate(this, &MWAttribute::onClicked);
}
}
/* MWSpell */
@ -301,45 +247,20 @@ void MWSpell::updateWidgets()
const ESMS::ESMStore &store = mWindowManager->getStore();
const ESM::Spell *spell = store.spells.search(id);
if (spell)
spellNameWidget->setCaption(spell->name);
static_cast<MyGUI::TextBox*>(spellNameWidget)->setCaption(spell->name);
else
spellNameWidget->setCaption("");
static_cast<MyGUI::TextBox*>(spellNameWidget)->setCaption("");
}
}
void MWSpell::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
void MWSpell::initialiseOverride()
{
Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
Base::initialiseOverride();
initialiseWidgetSkin(_info);
assignWidget(spellNameWidget, "StatName");
}
MWSpell::~MWSpell()
{
shutdownWidgetSkin();
}
void MWSpell::baseChangeWidgetSkin(ResourceSkin* _info)
{
shutdownWidgetSkin();
Base::baseChangeWidgetSkin(_info);
initialiseWidgetSkin(_info);
}
void MWSpell::initialiseWidgetSkin(ResourceSkin* _info)
{
for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
{
const std::string &name = *(*iter)->_getInternalData<std::string>();
if (name == "StatName")
{
MYGUI_DEBUG_ASSERT( ! spellNameWidget, "widget already assigned");
spellNameWidget = (*iter)->castType<StaticText>();
}
}
}
void MWSpell::shutdownWidgetSkin()
{
}
@ -408,10 +329,10 @@ void MWSpellEffect::updateWidgets()
spellLine += " on Touch";
else if (effect.range == ESM::RT_Target)
spellLine += " on Target";
textWidget->setCaption(spellLine);
static_cast<MyGUI::TextBox*>(textWidget)->setCaption(spellLine);
}
else
textWidget->setCaption("");
static_cast<MyGUI::TextBox*>(textWidget)->setCaption("");
}
if (imageWidget)
{
@ -421,45 +342,16 @@ void MWSpellEffect::updateWidgets()
}
}
void MWSpellEffect::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
{
Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
initialiseWidgetSkin(_info);
}
MWSpellEffect::~MWSpellEffect()
{
shutdownWidgetSkin();
}
void MWSpellEffect::baseChangeWidgetSkin(ResourceSkin* _info)
void MWSpellEffect::initialiseOverride()
{
shutdownWidgetSkin();
Base::baseChangeWidgetSkin(_info);
initialiseWidgetSkin(_info);
}
Base::initialiseOverride();
void MWSpellEffect::initialiseWidgetSkin(ResourceSkin* _info)
{
for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
{
const std::string &name = *(*iter)->_getInternalData<std::string>();
if (name == "Text")
{
MYGUI_DEBUG_ASSERT( ! textWidget, "widget already assigned");
textWidget = (*iter)->castType<StaticText>();
}
else if (name == "Image")
{
MYGUI_DEBUG_ASSERT( ! imageWidget, "widget already assigned");
imageWidget = (*iter)->castType<StaticImage>();
}
}
}
void MWSpellEffect::shutdownWidgetSkin()
{
assignWidget(textWidget, "Text");
assignWidget(imageWidget, "Image");
}
/* MWDynamicStat */
@ -491,60 +383,27 @@ void MWDynamicStat::setValue(int cur, int max_)
{
std::stringstream out;
out << value << "/" << max;
barTextWidget->setCaption(out.str().c_str());
static_cast<MyGUI::TextBox*>(barTextWidget)->setCaption(out.str().c_str());
}
else
barTextWidget->setCaption("");
static_cast<MyGUI::TextBox*>(barTextWidget)->setCaption("");
}
}
void MWDynamicStat::setTitle(const std::string text)
{
if (textWidget)
textWidget->setCaption(text);
}
void MWDynamicStat::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
{
Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
initialiseWidgetSkin(_info);
static_cast<MyGUI::TextBox*>(textWidget)->setCaption(text);
}
MWDynamicStat::~MWDynamicStat()
{
shutdownWidgetSkin();
}
void MWDynamicStat::baseChangeWidgetSkin(ResourceSkin* _info)
{
shutdownWidgetSkin();
Base::baseChangeWidgetSkin(_info);
initialiseWidgetSkin(_info);
}
void MWDynamicStat::initialiseWidgetSkin(ResourceSkin* _info)
void MWDynamicStat::initialiseOverride()
{
for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
{
const std::string &name = *(*iter)->_getInternalData<std::string>();
if (name == "Text")
{
MYGUI_DEBUG_ASSERT( ! textWidget, "widget already assigned");
textWidget = (*iter)->castType<StaticText>();
}
else if (name == "Bar")
{
MYGUI_DEBUG_ASSERT( ! barWidget, "widget already assigned");
barWidget = (*iter)->castType<Progress>();
}
else if (name == "BarText")
{
MYGUI_DEBUG_ASSERT( ! barTextWidget, "widget already assigned");
barTextWidget = (*iter)->castType<StaticText>();
}
}
}
Base::initialiseOverride();
void MWDynamicStat::shutdownWidgetSkin()
{
assignWidget(textWidget, "Text");
assignWidget(barWidget, "Bar");
assignWidget(barTextWidget, "BarText");
}

@ -7,6 +7,9 @@
#include "../mwmechanics/stat.hpp"
#undef MYGUI_EXPORT
#define MYGUI_EXPORT
/*
This file contains various custom widgets used in OpenMW.
*/
@ -38,26 +41,21 @@ namespace MWGui
const SkillValue& getSkillValue() const { return value; }
// Events
typedef delegates::CDelegate1<MWSkill*> EventHandle_SkillVoid;
typedef delegates::CMultiDelegate1<MWSkill*> EventHandle_SkillVoid;
/** Event : Skill clicked.\n
signature : void method(MWSkill* _sender)\n
*/
EventHandle_SkillVoid eventClicked;
/*internal:*/
virtual void _initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name);
protected:
virtual ~MWSkill();
void baseChangeWidgetSkin(ResourceSkin* _info);
virtual void initialiseOverride();
void onClicked(MyGUI::Widget* _sender);
private:
void initialiseWidgetSkin(ResourceSkin* _info);
void shutdownWidgetSkin();
void updateWidgets();
@ -85,26 +83,21 @@ namespace MWGui
const AttributeValue& getAttributeValue() const { return value; }
// Events
typedef delegates::CDelegate1<MWAttribute*> EventHandle_AttributeVoid;
typedef delegates::CMultiDelegate1<MWAttribute*> EventHandle_AttributeVoid;
/** Event : Attribute clicked.\n
signature : void method(MWAttribute* _sender)\n
*/
EventHandle_AttributeVoid eventClicked;
/*internal:*/
virtual void _initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name);
protected:
virtual ~MWAttribute();
void baseChangeWidgetSkin(ResourceSkin* _info);
virtual void initialiseOverride();
void onClicked(MyGUI::Widget* _sender);
private:
void initialiseWidgetSkin(ResourceSkin* _info);
void shutdownWidgetSkin();
void updateWidgets();
@ -130,23 +123,17 @@ namespace MWGui
const std::string &getSpellId() const { return id; }
/*internal:*/
virtual void _initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name);
protected:
virtual ~MWSpell();
void baseChangeWidgetSkin(ResourceSkin* _info);
virtual void initialiseOverride();
private:
void initialiseWidgetSkin(ResourceSkin* _info);
void shutdownWidgetSkin();
void updateWidgets();
WindowManager* mWindowManager;
std::string id;
MyGUI::StaticTextPtr spellNameWidget;
MyGUI::TextBox* spellNameWidget;
};
typedef MWSpell* MWSpellPtr;
@ -163,24 +150,19 @@ namespace MWGui
const SpellEffectValue &getSpellEffect() const { return effect; }
/*internal:*/
virtual void _initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name);
protected:
virtual ~MWSpellEffect();
void baseChangeWidgetSkin(ResourceSkin* _info);
virtual void initialiseOverride();
private:
void initialiseWidgetSkin(ResourceSkin* _info);
void shutdownWidgetSkin();
void updateWidgets();
WindowManager* mWindowManager;
SpellEffectValue effect;
MyGUI::StaticImagePtr imageWidget;
MyGUI::StaticTextPtr textWidget;
MyGUI::ImageBox* imageWidget;
MyGUI::TextBox* textWidget;
};
typedef MWSpellEffect* MWSpellEffectPtr;
@ -196,22 +178,17 @@ namespace MWGui
int getValue() const { return value; }
int getMax() const { return max; }
/*internal:*/
virtual void _initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name);
protected:
virtual ~MWDynamicStat();
void baseChangeWidgetSkin(ResourceSkin* _info);
virtual void initialiseOverride();
private:
void initialiseWidgetSkin(ResourceSkin* _info);
void shutdownWidgetSkin();
int value, max;
MyGUI::StaticTextPtr textWidget;
MyGUI::TextBox* textWidget;
MyGUI::ProgressPtr barWidget;
MyGUI::StaticTextPtr barTextWidget;
MyGUI::TextBox* barTextWidget;
};
typedef MWDynamicStat* MWDynamicStatPtr;

@ -16,7 +16,7 @@ void WindowBase::open()
void WindowBase::center()
{
// Centre dialog
MyGUI::IntSize gameWindowSize = mWindowManager.getGui()->getViewSize();
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
MyGUI::IntCoord coord = mMainWidget->getCoord();
coord.left = (gameWindowSize.width - coord.width)/2;
coord.top = (gameWindowSize.height - coord.height)/2;

@ -13,7 +13,7 @@ namespace MWGui
WindowBase(const std::string& parLayout, WindowManager& parWindowManager);
// Events
typedef MyGUI::delegates::CDelegate1<WindowBase*> EventHandle_WindowBase;
typedef MyGUI::delegates::CMultiDelegate1<WindowBase*> EventHandle_WindowBase;
virtual void open();
void center();

@ -22,27 +22,52 @@ 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);
gui = mGuiManager->getGui();
//Register own widgets with MyGUI
MyGUI::FactoryManager::getInstance().registerFactory<DialogeHistory>("Widget");
MyGUI::FactoryManager::getInstance().registerFactory<DialogueHistory>("Widget");
// Get size info from the Gui object
assert(gui);
int w = gui->getViewSize().width;
int h = gui->getViewSize().height;
int w = MyGUI::RenderManager::getInstance().getViewSize().width;
int h = MyGUI::RenderManager::getInstance().getViewSize().height;
hud = new HUD(w,h, showFPSLevel);
menu = new MainMenu(w,h);
@ -153,73 +178,60 @@ void WindowManager::updateVisible()
dialogueWindow->setVisible(false);
// Mouse is visible whenever we're not in game mode
gui->setVisiblePointer(isGuiMode());
// If in game mode, don't show anything.
if(mode == GM_Game) //Use a switch/case structure
{
return;
}
if(mode == GM_MainMenu)
{
// Enable the main menu
menu->setVisible(true);
return;
}
if(mode == GM_Console)
{
console->enable();
return;
}
//There must be a more elegant solution
if (mode == GM_Name || mode == GM_Race || mode == GM_Class || mode == GM_ClassPick || mode == GM_ClassCreate || mode == GM_Birth || mode == GM_ClassGenerate || mode == GM_Review)
{
mCharGen->spawnDialog(mode);
return;
}
if(mode == GM_Inventory)
{
// Ah, inventory mode. First, compute the effective set of
// windows to show. This is controlled both by what windows the
// user has opened/closed (the 'shown' variable) and by what
// windows we are allowed to show (the 'allowed' var.)
int eff = shown & allowed;
// Show the windows we want
map -> setVisible( (eff & GW_Map) != 0 );
stats -> setVisible( (eff & GW_Stats) != 0 );
return;
}
if (mode == GM_Dialogue)
{
dialogueWindow->open();
return;
}
if(mode == GM_InterMessageBox)
{
if(!mMessageBoxManager->isInteractiveMessageBox()) {
setGuiMode(GM_Game);
MyGUI::PointerManager::getInstance().setVisible(isGuiMode());
switch(mode) {
case GM_Game:
// If in game mode, don't show anything.
break;
case GM_MainMenu:
menu->setVisible(true);
break;
case GM_Console:
console->enable();
break;
case GM_Name:
case GM_Race:
case GM_Class:
case GM_ClassPick:
case GM_ClassCreate:
case GM_Birth:
case GM_ClassGenerate:
case GM_Review:
mCharGen->spawnDialog(mode);
break;
case GM_Inventory:
{
// First, compute the effective set of windows to show.
// This is controlled both by what windows the
// user has opened/closed (the 'shown' variable) and by what
// windows we are allowed to show (the 'allowed' var.)
int eff = shown & allowed;
// Show the windows we want
map -> setVisible( (eff & GW_Map) != 0 );
stats -> setVisible( (eff & GW_Stats) != 0 );
break;
}
return;
}
if(mode == GM_Journal)
{
mJournal->setVisible(true);
mJournal->open();
return;
case GM_Dialogue:
dialogueWindow->open();
break;
case GM_InterMessageBox:
if(!mMessageBoxManager->isInteractiveMessageBox()) {
setGuiMode(GM_Game);
}
break;
case GM_Journal:
mJournal->setVisible(true);
mJournal->open();
break;
default:
// Unsupported mode, switch back to game
// Note: The call will eventually end up this method again but
// will stop at the check if mode is GM_Game.
setGuiMode(GM_Game);
break;
}
// Unsupported mode, switch back to game
// Note: The call will eventually end up this method again but
// will stop at the check if(mode == GM_Game) above.
setGuiMode(GM_Game);
}
void WindowManager::setValue (const std::string& id, const MWMechanics::Stat<int>& value)
@ -346,7 +358,6 @@ void WindowManager::updateSkillArea()
void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
{
std::cout << "dialogue a la poubelle";
assert(dialog);
if (!dialog)
return;
@ -400,3 +411,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){
@ -103,6 +126,11 @@ namespace MWRender{
void Animation::handleShapes(std::vector<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){
shapeNumber = 0;
if (allshapes == NULL || creaturemodel == NULL || skel == NULL)
{
return;
}
std::vector<Nif::NiTriShapeCopy>::iterator allshapesiter;
for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++)

@ -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

@ -27,11 +27,7 @@ bool Debugging::toggleRenderMode (int mode){
switch (mode)
{
case MWWorld::World::Render_CollisionDebug:
// TODO use a proper function instead of accessing the member variable
// directly.
eng->setDebugRenderingMode (!eng->isDebugCreated);
return eng->isDebugCreated;
return eng->toggleDebugRendering();
}
return false;

@ -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();
}
}

@ -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

@ -88,33 +88,75 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
NifOgre::NIFLoader::load(mesh);
Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh);
/*
Ogre::Vector3 extents = ent->getBoundingBox().getSize();
extents *= insert->getScale();
// float size = std::max(std::max(extents.x, extents.y), extents.z);
bool small = (size < 250); /// \todo config value
// do not fade out doors. that will cause holes and look stupid
if (ptr.getTypeName().find("Door") != std::string::npos)
small = false;
*/
const bool small = false;
if (mBounds.find(ptr.getCell()) == mBounds.end())
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
Ogre::AxisAlignedBox bounds = ent->getBoundingBox();
bounds = Ogre::AxisAlignedBox(
insert->_getDerivedPosition() + bounds.getMinimum(),
insert->_getDerivedPosition() + bounds.getMaximum()
);
bounds.scale(insert->getScale());
mBounds[ptr.getCell()].merge(bounds);
if(!mIsStatic)
{
insert->attachObject(ent);
ent->setRenderingDistance(small ? 2500 : 0); /// \todo config value
}
else
{
Ogre::StaticGeometry* sg = 0;
if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
if (small)
{
uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
//Create the scenenode and put it in the map
mStaticGeometry[ptr.getCell()] = sg;
// This specifies the size of a single batch region.
// If it is set too high:
// - there will be problems choosing the correct lights
// - the culling will be more inefficient
// If it is set too low:
// - there will be too many batches.
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end())
{
uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
mStaticGeometrySmall[ptr.getCell()] = sg;
sg->setRenderingDistance(2500); /// \todo config value
}
else
sg = mStaticGeometrySmall[ptr.getCell()];
}
else
{
sg = mStaticGeometry[ptr.getCell()];
if( mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
{
uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
mStaticGeometry[ptr.getCell()] = sg;
}
else
sg = mStaticGeometry[ptr.getCell()];
}
// This specifies the size of a single batch region.
// If it is set too high:
// - there will be problems choosing the correct lights
// - the culling will be more inefficient
// If it is set too low:
// - there will be too many batches.
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
mRenderer.getScene()->destroyEntity(ent);
@ -202,6 +244,16 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store)
mRenderer.getScene()->destroyStaticGeometry (sg);
sg = 0;
}
if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[store];
mStaticGeometrySmall.erase(store);
mRenderer.getScene()->destroyStaticGeometry (sg);
sg = 0;
}
if(mBounds.find(store) != mBounds.end())
mBounds.erase(store);
}
void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
@ -211,4 +263,14 @@ void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
sg->build();
}
if(mStaticGeometrySmall.find(&cell) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[&cell];
sg->build();
}
}
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
{
return mBounds[cell];
}

@ -14,6 +14,8 @@ 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::StaticGeometry*> mStaticGeometrySmall;
std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds;
Ogre::SceneNode* mMwRoot;
bool mIsStatic;
static int uniqueID;
@ -42,6 +44,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?

@ -0,0 +1,254 @@
#include "occlusionquery.hpp"
#include <OgreRenderSystem.h>
#include <OgreRoot.h>
#include <OgreBillboardSet.h>
#include <OgreHardwareOcclusionQuery.h>
#include <OgreEntity.h>
using namespace MWRender;
using namespace Ogre;
OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNode* sunNode) :
mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mSingleObjectQuery(0), mActiveQuery(0),
mDoQuery(0), mSunVisibility(0), mQuerySingleObjectStarted(false), mTestResult(false),
mQuerySingleObjectRequested(false), mWasVisible(false), mObjectWasVisible(false), mDoQuery2(false)
{
mRendering = renderer;
mSunNode = sunNode;
try {
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
mSunTotalAreaQuery = renderSystem->createHardwareOcclusionQuery();
mSunVisibleAreaQuery = renderSystem->createHardwareOcclusionQuery();
mSingleObjectQuery = renderSystem->createHardwareOcclusionQuery();
mSupported = (mSunTotalAreaQuery != 0) && (mSunVisibleAreaQuery != 0) && (mSingleObjectQuery != 0);
}
catch (Ogre::Exception e)
{
mSupported = false;
}
if (!mSupported)
{
std::cout << "Hardware occlusion queries not supported." << std::endl;
return;
}
// This means that everything up to RENDER_QUEUE_MAIN can occlude the objects that are tested
const int queue = RENDER_QUEUE_MAIN+1;
MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting");
MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels");
matQueryArea->setDepthWriteEnabled(false);
matQueryArea->setColourWriteEnabled(false);
matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects
MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels");
matQueryVisible->setDepthWriteEnabled(false);
matQueryVisible->setColourWriteEnabled(false); // Uncomment this to visualize the occlusion query
matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects
matQueryVisible->setCullingMode(CULL_NONE);
matQueryVisible->setManualCullingMode(MANUAL_CULL_NONE);
mBBNode = mSunNode->getParentSceneNode()->createChildSceneNode();
mObjectNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
mBBNodeReal = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
mBBQueryTotal = mRendering->getScene()->createBillboardSet(1);
mBBQueryTotal->setDefaultDimensions(150, 150);
mBBQueryTotal->createBillboard(Vector3::ZERO);
mBBQueryTotal->setMaterialName("QueryTotalPixels");
mBBQueryTotal->setRenderQueueGroup(queue+1);
mBBNodeReal->attachObject(mBBQueryTotal);
mBBQueryVisible = mRendering->getScene()->createBillboardSet(1);
mBBQueryVisible->setDefaultDimensions(150, 150);
mBBQueryVisible->createBillboard(Vector3::ZERO);
mBBQueryVisible->setMaterialName("QueryVisiblePixels");
mBBQueryVisible->setRenderQueueGroup(queue+1);
mBBNodeReal->attachObject(mBBQueryVisible);
mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1);
/// \todo ideally this should occupy exactly 1 pixel on the screen
mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003);
mBBQuerySingleObject->createBillboard(Vector3::ZERO);
mBBQuerySingleObject->setMaterialName("QueryVisiblePixels");
mBBQuerySingleObject->setRenderQueueGroup(queue);
mObjectNode->attachObject(mBBQuerySingleObject);
mRendering->getScene()->addRenderObjectListener(this);
mRendering->getScene()->addRenderQueueListener(this);
mDoQuery = true;
mDoQuery2 = true;
}
OcclusionQuery::~OcclusionQuery()
{
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
if (mSunTotalAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery);
if (mSunVisibleAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery);
if (mSingleObjectQuery) renderSystem->destroyHardwareOcclusionQuery(mSingleObjectQuery);
}
bool OcclusionQuery::supported()
{
return mSupported;
}
void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source,
const LightList* pLightList, bool suppressRenderStateChanges)
{
// The following code activates and deactivates the occlusion queries
// so that the queries only include the rendering of their intended targets
// Close the last occlusion query
// Each occlusion query should only last a single rendering
if (mActiveQuery != NULL)
{
mActiveQuery->endOcclusionQuery();
mActiveQuery = NULL;
}
// Open a new occlusion query
if (mDoQuery == true)
{
if (rend == mBBQueryTotal)
{
mActiveQuery = mSunTotalAreaQuery;
mWasVisible = true;
}
else if (rend == mBBQueryVisible)
{
mActiveQuery = mSunVisibleAreaQuery;
}
}
if (mDoQuery == true && rend == mBBQuerySingleObject)
{
mQuerySingleObjectStarted = true;
mQuerySingleObjectRequested = false;
mActiveQuery = mSingleObjectQuery;
mObjectWasVisible = true;
}
if (mActiveQuery != NULL)
mActiveQuery->beginOcclusionQuery();
}
void OcclusionQuery::renderQueueEnded(uint8 queueGroupId, const String& invocation, bool& repeatThisInvocation)
{
if (mActiveQuery != NULL)
{
mActiveQuery->endOcclusionQuery();
mActiveQuery = NULL;
}
/**
* for every beginOcclusionQuery(), we want a respective pullOcclusionQuery() and vice versa
* this also means that results can be wrong at other places if we pull, but beginOcclusionQuery() was never called
* this can happen for example if the object that is tested is outside of the view frustum
* to prevent this, check if the queries have been performed after everything has been rendered and if not, start them manually
*/
if (queueGroupId == RENDER_QUEUE_SKIES_LATE)
{
if (mWasVisible == false && mDoQuery)
{
mSunTotalAreaQuery->beginOcclusionQuery();
mSunTotalAreaQuery->endOcclusionQuery();
mSunVisibleAreaQuery->beginOcclusionQuery();
mSunVisibleAreaQuery->endOcclusionQuery();
}
if (mObjectWasVisible == false && mDoQuery)
{
mSingleObjectQuery->beginOcclusionQuery();
mSingleObjectQuery->endOcclusionQuery();
mQuerySingleObjectStarted = true;
mQuerySingleObjectRequested = false;
}
}
}
void OcclusionQuery::update(float duration)
{
if (!mSupported) return;
mWasVisible = false;
mObjectWasVisible = false;
// Adjust the position of the sun billboards according to camera viewing distance
// we need to do this to make sure that _everything_ can occlude the sun
float dist = mRendering->getCamera()->getFarClipDistance();
if (dist==0) dist = 10000000;
dist -= 1000; // bias
dist /= 1000.f;
mBBNode->setPosition(mSunNode->getPosition() * dist);
mBBNode->setScale(dist, dist, dist);
mBBNodeReal->setPosition(mBBNode->_getDerivedPosition());
mBBNodeReal->setScale(mBBNode->getScale());
// Stop occlusion queries until we get their information
// (may not happen on the same frame they are requested in)
mDoQuery = false;
mDoQuery2 = false;
if (!mSunTotalAreaQuery->isStillOutstanding()
&& !mSunVisibleAreaQuery->isStillOutstanding()
&& !mSingleObjectQuery->isStillOutstanding())
{
unsigned int totalPixels;
unsigned int visiblePixels;
mSunTotalAreaQuery->pullOcclusionQuery(&totalPixels);
mSunVisibleAreaQuery->pullOcclusionQuery(&visiblePixels);
if (totalPixels == 0)
{
// probably outside of the view frustum
mSunVisibility = 0;
}
else
{
mSunVisibility = float(visiblePixels) / float(totalPixels);
if (mSunVisibility > 1) mSunVisibility = 1;
}
unsigned int result;
mSingleObjectQuery->pullOcclusionQuery(&result);
mTestResult = (result != 0);
mQuerySingleObjectStarted = false;
mQuerySingleObjectRequested = false;
mDoQuery = true;
}
}
void OcclusionQuery::occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object)
{
assert( !occlusionTestPending()
&& "Occlusion test still pending");
mBBQuerySingleObject->setVisible(true);
mObjectNode->setPosition(position);
// scale proportional to camera distance, in order to always give the billboard the same size in screen-space
mObjectNode->setScale( Vector3(1,1,1)*(position - mRendering->getCamera()->getRealPosition()).length() );
mQuerySingleObjectRequested = true;
}
bool OcclusionQuery::occlusionTestPending()
{
return (mQuerySingleObjectRequested || mQuerySingleObjectStarted);
}
bool OcclusionQuery::getTestResult()
{
assert( !occlusionTestPending()
&& "Occlusion test still pending");
return mTestResult;
}

@ -0,0 +1,95 @@
#ifndef _GAME_OCCLUSION_QUERY_H
#define _GAME_OCCLUSION_QUERY_H
#include <OgreRenderObjectListener.h>
#include <OgreRenderQueueListener.h>
namespace Ogre
{
class HardwareOcclusionQuery;
class Entity;
class SceneNode;
}
#include <openengine/ogre/renderer.hpp>
namespace MWRender
{
///
/// \brief Implements hardware occlusion queries on the GPU
///
class OcclusionQuery : public Ogre::RenderObjectListener, public Ogre::RenderQueueListener
{
public:
OcclusionQuery(OEngine::Render::OgreRenderer*, Ogre::SceneNode* sunNode);
~OcclusionQuery();
/**
* @return true if occlusion queries are supported on the user's hardware
*/
bool supported();
/**
* per-frame update
*/
void update(float duration);
/**
* request occlusion test for a billboard at the given position, omitting an entity
* @param position of the billboard in ogre coordinates
* @param object to exclude from the occluders
*/
void occlusionTest(const Ogre::Vector3& position, Ogre::SceneNode* object);
/**
* @return true if a request is still outstanding
*/
bool occlusionTestPending();
/**
* @return true if the object tested in the last request was occluded
*/
bool getTestResult();
float getSunVisibility() const {return mSunVisibility;};
private:
Ogre::HardwareOcclusionQuery* mSunTotalAreaQuery;
Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery;
Ogre::HardwareOcclusionQuery* mSingleObjectQuery;
Ogre::HardwareOcclusionQuery* mActiveQuery;
Ogre::BillboardSet* mBBQueryVisible;
Ogre::BillboardSet* mBBQueryTotal;
Ogre::BillboardSet* mBBQuerySingleObject;
Ogre::SceneNode* mSunNode;
Ogre::SceneNode* mBBNode;
Ogre::SceneNode* mBBNodeReal;
float mSunVisibility;
Ogre::SceneNode* mObjectNode;
bool mWasVisible;
bool mObjectWasVisible;
bool mTestResult;
bool mSupported;
bool mDoQuery;
bool mDoQuery2;
bool mQuerySingleObjectRequested;
bool mQuerySingleObjectStarted;
OEngine::Render::OgreRenderer* mRendering;
protected:
virtual void notifyRenderSingleObject(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source,
const Ogre::LightList* pLightList, bool suppressRenderStateChanges);
virtual void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String& invocation, bool& repeatThisInvocation);
};
}
#endif

@ -23,6 +23,12 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
:mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mDebugging(engine)
{
mRendering.createScene("PlayerCam", 55, 5);
mTerrainManager = new TerrainManager(mRendering.getScene(),
environment);
//The fog type must be set before any terrain objects are created as if the
//fog type is set to FOG_NONE then the initially created terrain won't have any fog
configureFog(1, ColourValue(1,1,1));
// Set default mipmap level (NB some APIs ignore this)
TextureManager::getSingleton().setDefaultNumMipmaps(5);
@ -40,9 +46,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
mMwRoot->pitch(Degree(-90));
mObjects.setMwRoot(mMwRoot);
mActors.setMwRoot(mMwRoot);
//used to obtain ingame information of ogre objects (which are faced or selected)
mRaySceneQuery = mRendering.getScene()->createRayQuery(Ray());
Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player");
playerNode->pitch(Degree(90));
@ -53,8 +56,14 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
//mSkyManager = 0;
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
mWater = 0;
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mSun = 0;
mLocalMap = new MWRender::LocalMap(&mRendering, &environment);
}
RenderingManager::~RenderingManager ()
@ -62,6 +71,9 @@ RenderingManager::~RenderingManager ()
//TODO: destroy mSun?
delete mPlayer;
delete mSkyManager;
delete mTerrainManager;
delete mLocalMap;
delete mOcclusionQuery;
}
MWRender::SkyManager* RenderingManager::getSkyManager()
@ -85,14 +97,33 @@ OEngine::Render::Fader* RenderingManager::getFader()
return mRendering.getFader();
}
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store)
{
mObjects.removeCell(store);
mActors.removeCell(store);
if (store->cell->isExterior())
mTerrainManager->cellRemoved(store);
}
void RenderingManager::removeWater ()
{
if(mWater){
delete mWater;
mWater = 0;
}
}
void RenderingManager::toggleWater()
{
if (mWater)
mWater->toggle();
}
void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
{
mObjects.buildStaticGeometry (*store);
if (store->cell->isExterior())
mTerrainManager->cellAdded(store);
}
void RenderingManager::addObject (const MWWorld::Ptr& ptr){
@ -133,10 +164,37 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve
void RenderingManager::update (float duration){
mActors.update (duration);
mOcclusionQuery->update(duration);
mSkyManager->update(duration);
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
mRendering.update(duration);
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );
checkUnderwater();
}
void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){
if(store->cell->data.flags & store->cell->HasWater){
if(mWater == 0)
mWater = new MWRender::Water(mRendering.getCamera(), store->cell);
else
mWater->changeCell(store->cell);
//else
}
else
removeWater();
}
void RenderingManager::setWaterHeight(const float height)
{
if (mWater)
mWater->setHeight(height);
}
void RenderingManager::skyEnable ()
@ -231,17 +289,17 @@ void RenderingManager::setAmbientMode()
{
case 0:
mRendering.getScene()->setAmbientLight(mAmbientColor);
setAmbientColour(mAmbientColor);
break;
case 1:
mRendering.getScene()->setAmbientLight(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1));
setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1));
break;
case 2:
mRendering.getScene()->setAmbientLight(ColourValue(1,1,1));
setAmbientColour(ColourValue(1,1,1));
break;
}
}
@ -281,6 +339,11 @@ void RenderingManager::toggleLight()
setAmbientMode();
}
void RenderingManager::checkUnderwater(){
if(mWater){
mWater->checkUnderwater( mRendering.getCamera()->getRealPosition().y );
}
}
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number)
@ -296,11 +359,13 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr)
void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
{
mSun->setDiffuseColour(colour);
mTerrainManager->setDiffuse(colour);
}
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
{
mRendering.getScene()->setAmbientLight(colour);
mTerrainManager->setAmbient(colour);
}
void RenderingManager::sunEnable()
@ -327,4 +392,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

@ -3,6 +3,7 @@
#include "sky.hpp"
#include "terrain.hpp"
#include "debugging.hpp"
#include "../mwworld/class.hpp"
@ -24,6 +25,9 @@
#include "objects.hpp"
#include "actors.hpp"
#include "player.hpp"
#include "water.hpp"
#include "localmap.hpp"
#include "occlusionquery.hpp"
namespace Ogre
{
@ -58,6 +62,8 @@ class RenderingManager: private RenderingInterface {
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment);
virtual ~RenderingManager();
virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as
/// MWWorld::Player has been rewritten to not need access
/// to internal details of the rendering system anymore
@ -66,7 +72,7 @@ class RenderingManager: private RenderingInterface {
void toggleLight();
bool toggleRenderMode(int mode);
OEngine::Render::Fader* getFader();
void removeCell (MWWorld::Ptr::CellStore *store);
@ -74,6 +80,12 @@ class RenderingManager: private RenderingInterface {
/// \todo this function should be removed later. Instead the rendering subsystems should track
/// when rebatching is needed and update automatically at the end of each frame.
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);
@ -82,17 +94,24 @@ class RenderingManager: private RenderingInterface {
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation);
void checkUnderwater();
void setWaterHeight(const float height);
void toggleWater();
/// \param store Cell the object was in previously (\a ptr has already been updated to the new cell).
void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store);
void update (float duration);
void setAmbientColour(const Ogre::ColourValue& colour);
void setSunColour(const Ogre::ColourValue& colour);
void setSunDirection(const Ogre::Vector3& direction);
void sunEnable();
void sunDisable();
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; };
void setGlare(bool glare);
void skyEnable ();
void skyDisable ();
@ -102,13 +121,16 @@ 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);
/// configure fog manually
void configureFog(const float density, const Ogre::ColourValue& colour);
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
int number = 1);
///< Run animation for a MW-reference. Calls to this function for references that are currently not
@ -124,9 +146,15 @@ class RenderingManager: private RenderingInterface {
private:
void setAmbientMode();
SkyManager* mSkyManager;
OcclusionQuery* mOcclusionQuery;
TerrainManager* mTerrainManager;
MWRender::Water *mWater;
OEngine::Render::OgreRenderer &mRendering;
MWRender::Objects mObjects;
@ -142,12 +170,13 @@ class RenderingManager: private RenderingInterface {
/// that the OGRE coordinate system matches that used internally in
/// Morrowind.
Ogre::SceneNode *mMwRoot;
Ogre::RaySceneQuery *mRaySceneQuery;
OEngine::Physic::PhysicEngine* mPhysicsEngine;
MWRender::Player *mPlayer;
MWRender::Debugging mDebugging;
MWRender::LocalMap* mLocalMap;
};
}

@ -12,6 +12,7 @@
#include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp"
#include "occlusionquery.hpp"
using namespace MWRender;
using namespace Ogre;
@ -30,7 +31,7 @@ BillboardObject::BillboardObject()
void BillboardObject::setVisible(const bool visible)
{
mNode->setVisible(visible);
mBBSet->setVisible(visible);
}
void BillboardObject::setSize(const float size)
@ -88,7 +89,7 @@ void BillboardObject::init(const String& textureName,
/// \todo These billboards are not 100% correct, might want to revisit them later
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize);
mBBSet->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+2);
mBBSet->setRenderQueueGroup(RENDER_QUEUE_MAIN+2);
mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON);
mBBSet->setCommonDirection( -position.normalisedCopy() );
mNode = rootNode->createChildSceneNode();
@ -254,7 +255,7 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType)
// Get a pointer to the vertex colour
ves_diffuse->baseVertexPointerToElement( pData, &currentVertex );
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 +293,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)
, mGlare(0.0f)
, mEnabled(true)
, mSunEnabled(true)
, mMasserEnabled(true)
, mSecundaEnabled(true)
{
mEnvironment = env;
mViewport = pCamera->getViewport();
mSceneMgr = pMwRoot->getCreator();
mRootNode = pCamera->getParentSceneNode()->createChildSceneNode();
@ -446,6 +477,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");
@ -561,10 +593,23 @@ void SkyManager::update(float duration)
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
mSecunda->setPhase ( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
if (mSunEnabled)
{
// take 1/5 sec for fading the glare effect from invisible to full
if (mGlareFade > mGlare)
{
mGlareFade -= duration*5;
if (mGlareFade < mGlare) mGlareFade = mGlare;
}
else if (mGlareFade < mGlare)
{
mGlareFade += duration*5;
if (mGlareFade > mGlare) mGlareFade = mGlare;
}
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
Vector3 sun = mSunGlare->getPosition();
sun = Vector3(sun.x, sun.z, -sun.y);
Vector3 cam = mViewport->getCamera()->getRealDirection();
@ -572,21 +617,10 @@ void SkyManager::update(float duration)
float val = 1- (angle.valueDegrees() / 180.f);
val = (val*val*val*val)*2;
if (mGlareEnabled)
{
mGlareFade += duration*3;
if (mGlareFade > 1) mGlareFade = 1;
}
else
{
mGlareFade -= duration*3;
if (mGlareFade < 0.3) mGlareFade = 0;
}
mSunGlare->setSize(val * (mGlareFade));
mSunGlare->setSize(val * mGlareFade);
}
mSunGlare->setVisible(mGlareFade>0 && mSunEnabled);
mSunGlare->setVisible(mSunEnabled);
mSun->setVisible(mSunEnabled);
mMasser->setVisible(mMasserEnabled);
mSecunda->setVisible(mSecundaEnabled);
@ -688,15 +722,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
else
strength = 1.f;
mSunGlare->setVisibility(weather.mGlareView * strength);
mSun->setVisibility(strength);
mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0);
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
}
void SkyManager::setGlare(bool glare)
void SkyManager::setGlare(const float glare)
{
mGlareEnabled = glare;
mGlare = glare;
}
Vector3 SkyManager::getRealSunPos()
@ -781,3 +815,8 @@ void SkyManager::setDate(int day, int month)
mDay = day;
mMonth = month;
}
Ogre::SceneNode* SkyManager::getSunNode()
{
return mSun->getNode();
}

@ -109,58 +109,60 @@ namespace MWRender
public:
SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env);
~SkyManager();
void update(float duration);
void enable();
void disable();
void setHour (double hour);
///< will be called even when sky is disabled.
void setDate (int day, int month);
///< will be called even when sky is disabled.
int getMasserPhase() const;
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
/// 3 waxing or waning gibbous, 4 full moon
int getSecundaPhase() const;
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
/// 3 waxing or waning gibbous, 4 full moon
void setMoonColour (bool red);
///< change Secunda colour to red
void setCloudsOpacity(float opacity);
///< change opacity of the clouds
void setWeather(const MWWorld::WeatherResult& weather);
Ogre::SceneNode* getSunNode();
void sunEnable();
void sunDisable();
void setSunDirection(const Ogre::Vector3& direction);
void setMasserDirection(const Ogre::Vector3& direction);
void setSecundaDirection(const Ogre::Vector3& direction);
void setMasserFade(const float fade);
void setSecundaFade(const float fade);
void masserEnable();
void masserDisable();
void secundaEnable();
void secundaDisable();
void setThunder(const float factor);
void setGlare(bool glare);
void setGlare(const float glare);
Ogre::Vector3 getRealSunPos();
private:
@ -203,12 +205,12 @@ namespace MWRender
float mRemainingTransitionTime;
float mGlareFade;
float mGlare; // target
float mGlareFade; // actual
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
bool mEnabled;
bool mGlareEnabled;
bool mSunEnabled;
bool mMasserEnabled;
bool mSecundaEnabled;

@ -0,0 +1,510 @@
#include <OgreTerrain.h>
#include <OgreTerrainGroup.h>
#include <boost/lexical_cast.hpp>
#include "../mwworld/world.hpp"
#include "terrainmaterial.hpp"
#include "terrain.hpp"
using namespace Ogre;
namespace MWRender
{
//----------------------------------------------------------------------------------------------
TerrainManager::TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& evn) :
mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize))
{
TerrainMaterialGeneratorPtr matGen;
TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB();
matGen.bind(matGenP);
mTerrainGlobals.setDefaultMaterialGenerator(matGen);
TerrainMaterialGenerator::Profile* const activeProfile =
mTerrainGlobals.getDefaultMaterialGenerator()
->getActiveProfile();
mActiveProfile = static_cast<TerrainMaterialGeneratorB::SM2Profile*>(activeProfile);
//The pixel error should be as high as possible without it being noticed
//as it governs how fast mesh quality decreases.
mTerrainGlobals.setMaxPixelError(8);
mTerrainGlobals.setLayerBlendMapSize(32);
mTerrainGlobals.setDefaultGlobalColourMapSize(65);
//10 (default) didn't seem to be quite enough
mTerrainGlobals.setSkirtSize(128);
//due to the sudden flick between composite and non composite textures,
//this seemed the distance where it wasn't too noticeable
mTerrainGlobals.setCompositeMapDistance(mWorldSize*2);
mActiveProfile->setLightmapEnabled(false);
mActiveProfile->setLayerSpecularMappingEnabled(false);
mActiveProfile->setLayerNormalMappingEnabled(false);
mActiveProfile->setLayerParallaxMappingEnabled(false);
mActiveProfile->setReceiveDynamicShadowsEnabled(false);
//composite maps lead to a drastic reduction in loading time so are
//disabled
mActiveProfile->setCompositeMapEnabled(false);
mTerrainGroup.setOrigin(Vector3(mWorldSize/2,
0,
-mWorldSize/2));
Terrain::ImportData& importSettings = mTerrainGroup.getDefaultImportSettings();
importSettings.inputBias = 0;
importSettings.terrainSize = mLandSize;
importSettings.worldSize = mWorldSize;
importSettings.minBatchSize = 9;
importSettings.maxBatchSize = mLandSize;
importSettings.deleteInputData = true;
}
//----------------------------------------------------------------------------------------------
TerrainManager::~TerrainManager()
{
}
//----------------------------------------------------------------------------------------------
void TerrainManager::setDiffuse(const ColourValue& diffuse)
{
mTerrainGlobals.setCompositeMapDiffuse(diffuse);
}
//----------------------------------------------------------------------------------------------
void TerrainManager::setAmbient(const ColourValue& ambient)
{
mTerrainGlobals.setCompositeMapAmbient(ambient);
}
//----------------------------------------------------------------------------------------------
void TerrainManager::cellAdded(MWWorld::Ptr::CellStore *store)
{
const int cellX = store->cell->getGridX();
const int cellY = store->cell->getGridY();
ESM::Land* land = mEnvironment.mWorld->getStore().lands.search(cellX, cellY);
if ( land != NULL )
{
land->loadData();
}
//split the cell terrain into four segments
const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2;
for ( int x = 0; x < 2; x++ )
{
for ( int y = 0; y < 2; y++ )
{
Terrain::ImportData terrainData =
mTerrainGroup.getDefaultImportSettings();
const int terrainX = cellX * 2 + x;
const int terrainY = cellY * 2 + y;
//it makes far more sense to reallocate the memory here,
//and let Ogre deal with it due to the issues with deleting
//it at the wrong time if using threads (Which Terrain does)
terrainData.inputFloat = OGRE_ALLOC_T(float,
mLandSize*mLandSize,
MEMCATEGORY_GEOMETRY);
if ( land != NULL )
{
//copy the height data row by row
for ( int terrainCopyY = 0; terrainCopyY < mLandSize; terrainCopyY++ )
{
//the offset of the current segment
const size_t yOffset = y * (mLandSize-1) * ESM::Land::LAND_SIZE +
//offset of the row
terrainCopyY * ESM::Land::LAND_SIZE;
const size_t xOffset = x * (mLandSize-1);
memcpy(&terrainData.inputFloat[terrainCopyY*mLandSize],
&land->landData->heights[yOffset + xOffset],
mLandSize*sizeof(float));
}
}
else
{
memset(terrainData.inputFloat, 0, mLandSize*mLandSize*sizeof(float));
}
std::map<uint16_t, int> indexes;
initTerrainTextures(&terrainData, cellX, cellY,
x * numTextures, y * numTextures,
numTextures, indexes);
if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL)
{
mTerrainGroup.defineTerrain(terrainX, terrainY, &terrainData);
mTerrainGroup.loadTerrain(terrainX, terrainY, true);
Terrain* terrain = mTerrainGroup.getTerrain(terrainX, terrainY);
initTerrainBlendMaps(terrain,
cellX, cellY,
x * numTextures, y * numTextures,
numTextures,
indexes);
if ( land && land->landData->usingColours )
{
// disable or enable global colour map (depends on available vertex colours)
mActiveProfile->setGlobalColourMapEnabled(true);
TexturePtr vertex = getVertexColours(land,
cellX, cellY,
x*(mLandSize-1),
y*(mLandSize-1),
mLandSize);
//this is a hack to get around the fact that Ogre seems to
//corrupt the global colour map leading to rendering errors
MaterialPtr mat = terrain->getMaterial();
mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() );
//mat = terrain->_getCompositeMapMaterial();
//mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() );
}
else
{
mActiveProfile->setGlobalColourMapEnabled(false);
}
}
}
}
mTerrainGroup.freeTemporaryResources();
}
//----------------------------------------------------------------------------------------------
void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store)
{
for ( int x = 0; x < 2; x++ )
{
for ( int y = 0; y < 2; y++ )
{
mTerrainGroup.unloadTerrain(store->cell->getGridX() * 2 + x,
store->cell->getGridY() * 2 + y);
}
}
}
//----------------------------------------------------------------------------------------------
void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData,
int cellX, int cellY,
int fromX, int fromY, int size,
std::map<uint16_t, int>& indexes)
{
assert(terrainData != NULL && "Must have valid terrain data");
assert(fromX >= 0 && fromY >= 0 &&
"Can't get a terrain texture on terrain outside the current cell");
assert(fromX+size <= ESM::Land::LAND_TEXTURE_SIZE &&
fromY+size <= ESM::Land::LAND_TEXTURE_SIZE &&
"Can't get a terrain texture on terrain outside the current cell");
//this ensures that the ltex indexes are sorted (or retrived as sorted
//which simplifies shading between cells).
//
//If we don't sort the ltex indexes, the splatting order may differ between
//cells which may lead to inconsistent results when shading between cells
std::set<uint16_t> ltexIndexes;
for ( int y = fromY - 1; y < fromY + size + 1; y++ )
{
for ( int x = fromX - 1; x < fromX + size + 1; x++ )
{
ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y));
}
}
//there is one texture that we want to use as a base (i.e. it won't have
//a blend map). This holds the ltex index of that base texture so that
//we know not to include it in the output map
int baseTexture = -1;
for ( std::set<uint16_t>::iterator iter = ltexIndexes.begin();
iter != ltexIndexes.end();
++iter )
{
const uint16_t ltexIndex = *iter;
//this is the base texture, so we can ignore this at present
if ( ltexIndex == baseTexture )
{
continue;
}
const std::map<uint16_t, int>::const_iterator it = indexes.find(ltexIndex);
if ( it == indexes.end() )
{
//NB: All vtex ids are +1 compared to the ltex ids
assert( (int)mEnvironment.mWorld->getStore().landTexts.getSize() >= (int)ltexIndex - 1 &&
"LAND.VTEX must be within the bounds of the LTEX array");
std::string texture;
if ( ltexIndex == 0 )
{
texture = "_land_default.dds";
}
else
{
texture = mEnvironment.mWorld->getStore().landTexts.search(ltexIndex-1)->texture;
//TODO this is needed due to MWs messed up texture handling
texture = texture.substr(0, texture.rfind(".")) + ".dds";
}
const size_t position = terrainData->layerList.size();
terrainData->layerList.push_back(Terrain::LayerInstance());
terrainData->layerList[position].worldSize = 256;
terrainData->layerList[position].textureNames.push_back("textures\\" + texture);
if ( baseTexture == -1 )
{
baseTexture = ltexIndex;
}
else
{
indexes[ltexIndex] = position;
}
}
}
}
//----------------------------------------------------------------------------------------------
void TerrainManager::initTerrainBlendMaps(Terrain* terrain,
int cellX, int cellY,
int fromX, int fromY, int size,
const std::map<uint16_t, int>& indexes)
{
assert(terrain != NULL && "Must have valid terrain");
assert(fromX >= 0 && fromY >= 0 &&
"Can't get a terrain texture on terrain outside the current cell");
assert(fromX+size <= ESM::Land::LAND_TEXTURE_SIZE &&
fromY+size <= ESM::Land::LAND_TEXTURE_SIZE &&
"Can't get a terrain texture on terrain outside the current cell");
//size must be a power of 2 as we do divisions with a power of 2 number
//that need to result in an integer for correct splatting
assert( (size & (size - 1)) == 0 && "Size must be a power of 2");
const int blendMapSize = terrain->getLayerBlendMapSize();
const int splatSize = blendMapSize / size;
//zero out every map
std::map<uint16_t, int>::const_iterator iter;
for ( iter = indexes.begin(); iter != indexes.end(); ++iter )
{
float* pBlend = terrain->getLayerBlendMap(iter->second)
->getBlendPointer();
memset(pBlend, 0, sizeof(float) * blendMapSize * blendMapSize);
}
//covert the ltex data into a set of blend maps
for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ )
{
for ( int texX = fromX - 1; texX < fromX + size + 1; texX++ )
{
const uint16_t ltexIndex = getLtexIndexAt(cellX, cellY, texX, texY);
//check if it is the base texture (which isn't in the map) and
//if it is don't bother altering the blend map for it
if ( indexes.find(ltexIndex) == indexes.end() )
{
continue;
}
//while texX is the splat index relative to the entire cell,
//relX is relative to the current segment we are splatting
const int relX = texX - fromX;
const int relY = texY - fromY;
const int layerIndex = indexes.find(ltexIndex)->second;
float* const pBlend = terrain->getLayerBlendMap(layerIndex)
->getBlendPointer();
for ( int y = -1; y < splatSize + 1; y++ )
{
for ( int x = -1; x < splatSize + 1; x++ )
{
//Note: Y is reversed
const int splatY = blendMapSize - 1 - relY * splatSize - y;
const int splatX = relX * splatSize + x;
if ( splatX >= 0 && splatX < blendMapSize &&
splatY >= 0 && splatY < blendMapSize )
{
const int index = (splatY)*blendMapSize + splatX;
if ( y >= 0 && y < splatSize &&
x >= 0 && x < splatSize )
{
pBlend[index] = 1;
}
else
{
//this provides a transition shading but also
//rounds off the corners slightly
pBlend[index] = std::min(1.0f, pBlend[index] + 0.5f);
}
}
}
}
}
}
for ( int i = 1; i < terrain->getLayerCount(); i++ )
{
TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i);
blend->dirty();
blend->update();
}
}
//----------------------------------------------------------------------------------------------
int TerrainManager::getLtexIndexAt(int cellX, int cellY,
int x, int y)
{
//check texture index falls within the 9 cell bounds
//as this function can't cope with anything above that
assert(x >= -ESM::Land::LAND_TEXTURE_SIZE &&
y >= -ESM::Land::LAND_TEXTURE_SIZE &&
"Trying to get land textures that are out of bounds");
assert(x < 2*ESM::Land::LAND_TEXTURE_SIZE &&
y < 2*ESM::Land::LAND_TEXTURE_SIZE &&
"Trying to get land textures that are out of bounds");
if ( x < 0 )
{
cellX--;
x += ESM::Land::LAND_TEXTURE_SIZE;
}
else if ( x >= ESM::Land::LAND_TEXTURE_SIZE )
{
cellX++;
x -= ESM::Land::LAND_TEXTURE_SIZE;
}
if ( y < 0 )
{
cellY--;
y += ESM::Land::LAND_TEXTURE_SIZE;
}
else if ( y >= ESM::Land::LAND_TEXTURE_SIZE )
{
cellY++;
y -= ESM::Land::LAND_TEXTURE_SIZE;
}
ESM::Land* land = mEnvironment.mWorld->getStore().lands.search(cellX, cellY);
if ( land != NULL )
{
land->loadData();
return land->landData
->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
}
else
{
return 0;
}
}
//----------------------------------------------------------------------------------------------
TexturePtr TerrainManager::getVertexColours(ESM::Land* land,
int cellX, int cellY,
int fromX, int fromY, int size)
{
TextureManager* const texMgr = TextureManager::getSingletonPtr();
const std::string colourTextureName = "VtexColours_" +
boost::lexical_cast<std::string>(cellX) +
"_" +
boost::lexical_cast<std::string>(cellY) +
"_" +
boost::lexical_cast<std::string>(fromX) +
"_" +
boost::lexical_cast<std::string>(fromY);
TexturePtr tex = texMgr->getByName(colourTextureName);
if ( !tex.isNull() )
{
return tex;
}
tex = texMgr->createManual(colourTextureName,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
TEX_TYPE_2D, size, size, 0, PF_BYTE_BGR);
HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer();
pixelBuffer->lock(HardwareBuffer::HBL_DISCARD);
const PixelBox& pixelBox = pixelBuffer->getCurrentLock();
uint8* pDest = static_cast<uint8*>(pixelBox.data);
if ( land != NULL )
{
const char* const colours = land->landData->colours;
for ( int y = 0; y < size; y++ )
{
for ( int x = 0; x < size; x++ )
{
const size_t colourOffset = (y+fromY)*3*65 + (x+fromX)*3;
assert( colourOffset < 65*65*3 &&
"Colour offset is out of the expected bounds of record" );
const unsigned char r = colours[colourOffset + 0];
const unsigned char g = colours[colourOffset + 1];
const unsigned char b = colours[colourOffset + 2];
//as is the case elsewhere we need to flip the y
const size_t imageOffset = (size - 1 - y)*size*4 + x*4;
pDest[imageOffset + 0] = b;
pDest[imageOffset + 1] = g;
pDest[imageOffset + 2] = r;
}
}
}
else
{
for ( int y = 0; y < size; y++ )
{
for ( int x = 0; x < size; x++ )
{
for ( int k = 0; k < 3; k++ )
{
*pDest++ = 0;
}
}
}
}
pixelBuffer->unlock();
return tex;
}
}

@ -0,0 +1,117 @@
#ifndef _GAME_RENDER_TERRAIN_H
#define _GAME_RENDER_TERRAIN_H
#include <OgreTerrain.h>
#include <OgreTerrainGroup.h>
#include "terrainmaterial.hpp"
#include "../mwworld/ptr.hpp"
namespace Ogre{
class SceneManager;
class TerrainGroup;
class TerrainGlobalOptions;
class Terrain;
}
namespace MWRender{
/**
* Implements the Morrowind terrain using the Ogre Terrain Component
*
* Each terrain cell is split into four blocks as this leads to an increase
* in performance and means we don't hit splat limits quite as much
*/
class TerrainManager{
public:
TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& env);
virtual ~TerrainManager();
void setDiffuse(const Ogre::ColourValue& diffuse);
void setAmbient(const Ogre::ColourValue& ambient);
void cellAdded(MWWorld::Ptr::CellStore* store);
void cellRemoved(MWWorld::Ptr::CellStore* store);
private:
Ogre::TerrainGlobalOptions mTerrainGlobals;
Ogre::TerrainGroup mTerrainGroup;
const MWWorld::Environment& mEnvironment;
Ogre::TerrainMaterialGeneratorB::SM2Profile* mActiveProfile;
/**
* The length in verticies of a single terrain block.
*/
static const int mLandSize = (ESM::Land::LAND_SIZE - 1)/2 + 1;
/**
* The length in game units of a single terrain block.
*/
static const int mWorldSize = ESM::Land::REAL_SIZE/2;
/**
* Setups up the list of textures for part of a cell, using indexes as
* an output to create a mapping of MW LtexIndex to the relevant terrain
* layer
*
* @param terrainData the terrain data to setup the textures for
* @param cellX the coord of the cell
* @param cellY the coord of the cell
* @param fromX the ltex index in the current cell to start making the texture from
* @param fromY the ltex index in the current cell to start making the texture from
* @param size the size (number of splats) to get
* @param indexes a mapping of ltex index to the terrain texture layer that
* can be used by initTerrainBlendMaps
*/
void initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
int cellX, int cellY,
int fromX, int fromY, int size,
std::map<uint16_t, int>& indexes);
/**
* Creates the blend (splatting maps) for the given terrain from the ltex data.
*
* @param terrain the terrain object for the current cell
* @param cellX the coord of the cell
* @param cellY the coord of the cell
* @param fromX the ltex index in the current cell to start making the texture from
* @param fromY the ltex index in the current cell to start making the texture from
* @param size the size (number of splats) to get
* @param indexes the mapping of ltex to blend map produced by initTerrainTextures
*/
void initTerrainBlendMaps(Ogre::Terrain* terrain,
int cellX, int cellY,
int fromX, int fromY, int size,
const std::map<uint16_t, int>& indexes);
/**
* Gets a LTEX index at the given point, assuming the current cell
* starts at (0,0). This supports getting values from the surrounding
* cells so negative x, y is acceptable
*
* @param cellX the coord of the cell
* @param cellY the coord of the cell
* @param x, y the splat position of the ltex index to get relative to the
* first splat of the current cell
*/
int getLtexIndexAt(int cellX, int cellY, int x, int y);
/**
* Due to the fact that Ogre terrain doesn't support vertex colours
* we have to generate them manually
*
* @param cellX the coord of the cell
* @param cellY the coord of the cell
* @param fromX the *vertex* index in the current cell to start making texture from
* @param fromY the *vertex* index in the current cell to start making the texture from
* @param size the size (number of vertexes) to get
*/
Ogre::TexturePtr getVertexColours(ESM::Land* land,
int cellX, int cellY,
int fromX, int fromY, int size);
};
}
#endif // _GAME_RENDER_TERRAIN_H

File diff suppressed because it is too large Load Diff

@ -0,0 +1,266 @@
/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/
Copyright (c) 2000-2011 Torus Knot Software Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#ifndef __Ogre_TerrainMaterialGeneratorB_H__
#define __Ogre_TerrainMaterialGeneratorB_H__
#include "OgreTerrainPrerequisites.h"
#include "OgreTerrainMaterialGenerator.h"
#include "OgreGpuProgramParams.h"
namespace Ogre
{
class PSSMShadowCameraSetup;
/** \addtogroup Optional Components
* @{
*/
/** \addtogroup Terrain
* Some details on the terrain component
* @{
*/
/** A TerrainMaterialGenerator which can cope with normal mapped, specular mapped
terrain.
@note Requires the Cg plugin to render correctly
*/
class TerrainMaterialGeneratorB : public TerrainMaterialGenerator
{
public:
TerrainMaterialGeneratorB();
~TerrainMaterialGeneratorB();
/** Shader model 2 profile target.
*/
class SM2Profile : public TerrainMaterialGenerator::Profile
{
public:
SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc);
~SM2Profile();
bool isVertexCompressionSupported() const {return false;}
MaterialPtr generate(const Terrain* terrain);
MaterialPtr generateForCompositeMap(const Terrain* terrain);
uint8 getMaxLayers(const Terrain* terrain) const;
void updateParams(const MaterialPtr& mat, const Terrain* terrain);
void updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain);
void requestOptions(Terrain* terrain);
/** Whether to support normal mapping per layer in the shader (default true).
*/
bool isLayerNormalMappingEnabled() const { return mLayerNormalMappingEnabled; }
/** Whether to support normal mapping per layer in the shader (default true).
*/
void setLayerNormalMappingEnabled(bool enabled);
/** Whether to support parallax mapping per layer in the shader (default true).
*/
bool isLayerParallaxMappingEnabled() const { return mLayerParallaxMappingEnabled; }
/** Whether to support parallax mapping per layer in the shader (default true).
*/
void setLayerParallaxMappingEnabled(bool enabled);
/** Whether to support specular mapping per layer in the shader (default true).
*/
bool isLayerSpecularMappingEnabled() const { return mLayerSpecularMappingEnabled; }
/** Whether to support specular mapping per layer in the shader (default true).
*/
void setLayerSpecularMappingEnabled(bool enabled);
/** Whether to support a global colour map over the terrain in the shader,
if it's present (default true).
*/
bool isGlobalColourMapEnabled() const { return mGlobalColourMapEnabled; }
/** Whether to support a global colour map over the terrain in the shader,
if it's present (default true).
*/
void setGlobalColourMapEnabled(bool enabled);
/** Whether to support a light map over the terrain in the shader,
if it's present (default true).
*/
bool isLightmapEnabled() const { return mLightmapEnabled; }
/** Whether to support a light map over the terrain in the shader,
if it's present (default true).
*/
void setLightmapEnabled(bool enabled);
/** Whether to use the composite map to provide a lower LOD technique
in the distance (default true).
*/
bool isCompositeMapEnabled() const { return mCompositeMapEnabled; }
/** Whether to use the composite map to provide a lower LOD technique
in the distance (default true).
*/
void setCompositeMapEnabled(bool enabled);
/** Whether to support dynamic texture shadows received from other
objects, on the terrain (default true).
*/
bool getReceiveDynamicShadowsEnabled() const { return mReceiveDynamicShadows; }
/** Whether to support dynamic texture shadows received from other
objects, on the terrain (default true).
*/
void setReceiveDynamicShadowsEnabled(bool enabled);
/** Whether to use PSSM support dynamic texture shadows, and if so the
settings to use (default 0).
*/
void setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings);
/** Whether to use PSSM support dynamic texture shadows, and if so the
settings to use (default 0).
*/
PSSMShadowCameraSetup* getReceiveDynamicShadowsPSSM() const { return mPSSM; }
/** Whether to use depth shadows (default false).
*/
void setReceiveDynamicShadowsDepth(bool enabled);
/** Whether to use depth shadows (default false).
*/
bool getReceiveDynamicShadowsDepth() const { return mDepthShadows; }
/** Whether to use shadows on low LOD material rendering (when using composite map) (default false).
*/
void setReceiveDynamicShadowsLowLod(bool enabled);
/** Whether to use shadows on low LOD material rendering (when using composite map) (default false).
*/
bool getReceiveDynamicShadowsLowLod() const { return mLowLodShadows; }
int getNumberOfLightsSupported() const;
/// Internal
bool _isSM3Available() const { return mSM3Available; }
protected:
enum TechniqueType
{
HIGH_LOD,
LOW_LOD,
RENDER_COMPOSITE_MAP
};
void addTechnique(const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt);
/// Interface definition for helper class to generate shaders
class ShaderHelper : public TerrainAlloc
{
public:
ShaderHelper() {}
virtual ~ShaderHelper() {}
virtual HighLevelGpuProgramPtr generateVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
virtual HighLevelGpuProgramPtr generateFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
virtual void updateParams(const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap);
protected:
virtual String getVertexProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
virtual String getFragmentProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
virtual HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0;
virtual HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0;
virtual void generateVertexProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
virtual void generateFragmentProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
virtual void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
virtual void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
virtual void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0;
virtual void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0;
virtual void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
virtual void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0;
virtual void defaultVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog);
virtual void defaultFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog);
virtual void updateVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params);
virtual void updateFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params);
static String getChannel(uint idx);
size_t mShadowSamplerStartHi;
size_t mShadowSamplerStartLo;
};
/// Utility class to help with generating shaders for Cg / HLSL.
class ShaderHelperCg : public ShaderHelper
{
protected:
HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream);
void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream);
void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
uint generateVpDynamicShadowsParams(uint texCoordStart, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
void generateVpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
void generateFpDynamicShadowsHelpers(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
void generateFpDynamicShadowsParams(uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
void generateFpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream);
};
class ShaderHelperHLSL : public ShaderHelperCg
{
protected:
HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
};
/// Utility class to help with generating shaders for GLSL.
class ShaderHelperGLSL : public ShaderHelper
{
protected:
HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt);
void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {}
void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {}
void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {}
};
ShaderHelper* mShaderGen;
bool mLayerNormalMappingEnabled;
bool mLayerParallaxMappingEnabled;
bool mLayerSpecularMappingEnabled;
bool mGlobalColourMapEnabled;
bool mLightmapEnabled;
bool mCompositeMapEnabled;
bool mReceiveDynamicShadows;
PSSMShadowCameraSetup* mPSSM;
bool mDepthShadows;
bool mLowLodShadows;
bool mSM3Available;
bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const;
};
};
/** @} */
/** @} */
}
#endif

@ -0,0 +1,95 @@
#include "water.hpp"
namespace MWRender
{
Water::Water (Ogre::Camera *camera, const ESM::Cell* cell) :
mCamera (camera), mViewport (camera->getViewport()), mSceneManager (camera->getSceneManager()),
mIsUnderwater(false)
{
try
{
Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "Water", -1);
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
} catch(...) {}
mTop = cell->water;
mIsUnderwater = false;
mWaterPlane = Ogre::Plane(Ogre::Vector3::UNIT_Y, 0);
Ogre::MeshManager::getSingleton().createPlane("water", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,5, Ogre::Vector3::UNIT_Z);
mWater = mSceneManager->createEntity("water");
mWater->setMaterialName("Examples/Water0");
mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
mWaterNode->setPosition(0, mTop, 0);
if(!(cell->data.flags & cell->Interior))
{
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
}
mWaterNode->attachObject(mWater);
}
Water::~Water()
{
Ogre::MeshManager::getSingleton().remove("water");
mWaterNode->detachObject(mWater);
mSceneManager->destroyEntity(mWater);
mSceneManager->destroySceneNode(mWaterNode);
Ogre::CompositorManager::getSingleton().removeCompositorChain(mViewport);
}
void Water::changeCell(const ESM::Cell* cell)
{
mTop = cell->water;
if(!(cell->data.flags & cell->Interior))
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
else
setHeight(mTop);
}
void Water::setHeight(const float height)
{
mTop = height;
mWaterNode->setPosition(0, height, 0);
}
void Water::toggle()
{
mWater->setVisible(!mWater->getVisible());
}
void Water::checkUnderwater(float y)
{
if ((mIsUnderwater && y > mTop) || !mWater->isVisible())
{
try {
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
} catch(...) {}
mIsUnderwater = false;
}
if (!mIsUnderwater && y < mTop && mWater->isVisible())
{
try {
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", true);
} catch(...) {}
mIsUnderwater = true;
}
}
Ogre::Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY)
{
return Ogre::Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2));
}
} // namespace

@ -0,0 +1,40 @@
#ifndef GAME_MWRENDER_WATER_H
#define GAME_MWRENDER_WATER_H
#include <Ogre.h>
#include <components/esm/loadcell.hpp>
namespace MWRender {
/// Water rendering
class Water : Ogre::RenderTargetListener, Ogre::Camera::Listener
{
static const int CELL_SIZE = 8192;
Ogre::Camera *mCamera;
Ogre::SceneManager *mSceneManager;
Ogre::Viewport *mViewport;
Ogre::Plane mWaterPlane;
Ogre::SceneNode *mWaterNode;
Ogre::Entity *mWater;
bool mIsUnderwater;
int mTop;
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
public:
Water (Ogre::Camera *camera, const ESM::Cell* cell);
~Water();
void toggle();
void checkUnderwater(float y);
void changeCell(const ESM::Cell* cell);
void setHeight(const float height);
};
}
#endif

@ -133,11 +133,70 @@ namespace MWScript
}
};
class OpGetWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
runtime.push (cell->mWaterLevel);
}
};
class OpSetWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
if (!(cell->cell->data.flags & ESM::Cell::Interior))
throw std::runtime_error("Can't set water level in exterior cell");
cell->mWaterLevel = level;
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
}
};
class OpModWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
if (!(cell->cell->data.flags & ESM::Cell::Interior))
throw std::runtime_error("Can't set water level in exterior cell");
cell->mWaterLevel +=level;
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
}
};
const int opcodeCellChanged = 0x2000000;
const int opcodeCOC = 0x2000026;
const int opcodeCOE = 0x200008e;
const int opcodeGetInterior = 0x2000131;
const int opcodeGetPCCell = 0x2000136;
const int opcodeGetWaterLevel = 0x2000141;
const int opcodeSetWaterLevel = 0x2000142;
const int opcodeModWaterLevel = 0x2000143;
void registerExtensions (Compiler::Extensions& extensions)
{
@ -146,8 +205,11 @@ namespace MWScript
extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
extensions.registerInstruction ("coe", "ll", opcodeCOE);
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel);
extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel);
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
@ -157,6 +219,9 @@ namespace MWScript
interpreter.installSegment5 (opcodeCOE, new OpCOE);
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
interpreter.installSegment5 (opcodeGetWaterLevel, new OpGetWaterLevel);
interpreter.installSegment5 (opcodeSetWaterLevel, new OpSetWaterLevel);
interpreter.installSegment5 (opcodeModWaterLevel, new OpModWaterLevel);
}
}
}

@ -123,4 +123,8 @@ op 0x200013d: FadeOut
op 0x200013e: FadeTo
op 0x200013f: GetCurrentWeather
op 0x2000140: ChangeWeather
opcodes 0x2000141-0x3ffffff unused
op 0x2000141: GetWaterLevel
op 0x2000142: SetWaterLevel
op 0x2000143: ModWaterLevel
op 0x2000144: ToggleWater, twa
opcodes 0x2000145-0x3ffffff unused

@ -175,6 +175,19 @@ namespace MWScript
}
};
class OpToggleWater : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().toggleWater();
}
};
const int opcodeXBox = 0x200000c;
const int opcodeOnActivate = 0x200000d;
const int opcodeActivate = 0x2000075;
@ -187,6 +200,7 @@ namespace MWScript
const int opcodeFadeIn = 0x200013c;
const int opcodeFadeOut = 0x200013d;
const int opcodeFadeTo = 0x200013e;
const int opcodeToggleWater = 0x2000144;
void registerExtensions (Compiler::Extensions& extensions)
{
@ -204,6 +218,8 @@ namespace MWScript
extensions.registerInstruction ("fadein", "f", opcodeFadeIn);
extensions.registerInstruction ("fadeout", "f", opcodeFadeOut);
extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo);
extensions.registerInstruction ("togglewater", "", opcodeToggleWater);
extensions.registerInstruction ("twa", "", opcodeToggleWater);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
@ -220,6 +236,7 @@ namespace MWScript
interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn);
interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut);
interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo);
interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater);
}
}
}

@ -130,7 +130,7 @@ namespace MWScript
std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
context.getSoundManager().playSound3D (ptr, sound, 1.0, 1.0, mLoop);
context.getSoundManager().playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWSound::Play_Loop : 0);
}
};
@ -159,7 +159,7 @@ namespace MWScript
Interpreter::Type_Float pitch = runtime[0].mFloat;
runtime.pop();
context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop);
context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop ? MWSound::Play_Loop : 0);
}
};

@ -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

@ -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

@ -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

@ -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

@ -0,0 +1,775 @@
#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)
{
const ALCchar *errstring = alcGetString(device, err);
fail(errstring ? errstring : "");
}
}
static void throwALerror()
{
ALenum err = alGetError();
if(err != AL_NO_ERROR)
{
const ALchar *errstring = alGetString(err);
fail(errstring ? errstring : "");
}
}
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 update();
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)
{
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::update()
{
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume);
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[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 2D OpenAL sound
//
class OpenAL_Sound : public Sound
{
protected:
OpenAL_Output &mOutput;
ALuint mSource;
ALuint mBuffer;
private:
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 update();
};
//
// A regular 3D OpenAL sound
//
class OpenAL_Sound3D : public OpenAL_Sound
{
OpenAL_Sound3D(const OpenAL_Sound &rhs);
OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs);
public:
OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf)
: OpenAL_Sound(output, src, buf)
{ }
virtual void update();
};
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::update()
{
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume);
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]);
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
throwALerror();
}
void OpenAL_Sound3D::update()
{
if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance)
alSourcef(mSource, AL_GAIN, 0.0f);
else
alSourcef(mSource, AL_GAIN, mVolume*mBaseVolume);
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[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)
{
deinit();
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)
{
if(mContext)
alcDestroyContext(mContext);
mContext = 0;
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);
if (maxtotal == 0) // workaround for broken implementations
maxtotal = 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, int flags)
{
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, (flags&Play_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 Ogre::Vector3 &pos, float volume, float pitch,
float min, float max, int flags)
{
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_Sound3D(*this, src, buf));
}
catch(std::exception &e)
{
mFreeSources.push_back(src);
if(buf && alIsBuffer(buf))
bufferFinished(buf);
alGetError();
throw;
}
alSource3f(src, AL_POSITION, pos.x, pos.z, -pos.y);
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, (pos.squaredDistance(mPos) > max*max) ?
0.0f : volume);
alSourcef(src, AL_PITCH, pitch);
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcei(src, AL_LOOPING, (flags&Play_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)
{
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;
}
void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir)
{
mPos = pos;
if(mContext)
{
ALfloat orient[6] = {
atdir.x, atdir.z, -atdir.y,
updir.x, updir.z, -updir.y
};
alListener3f(AL_POSITION, mPos.x, mPos.z, -mPos.y);
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();
}
}

@ -0,0 +1,69 @@
#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, int flags);
virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags);
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch);
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &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_Sound3D;
friend class OpenAL_SoundStream;
friend class SoundManager;
};
#ifndef DEFAULT_OUTPUT
#define DEFAULT_OUTPUT (::MWSound::OpenAL_Output)
#endif
};
#endif

@ -0,0 +1,43 @@
#ifndef GAME_SOUND_SOUND_H
#define GAME_SOUND_SOUND_H
#include <OgreVector3.h>
namespace MWSound
{
class Sound
{
virtual void update() = 0;
Sound& operator=(const Sound &rhs);
Sound(const Sound &rhs);
protected:
Ogre::Vector3 mPos;
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
float mBaseVolume;
float mMinDistance;
float mMaxDistance;
int mFlags;
public:
virtual void stop() = 0;
virtual bool isPlaying() = 0;
void setPosition(const Ogre::Vector3 &pos) { mPos = pos; }
void setVolume(float volume) { mVolume = volume; }
Sound() : mPos(0.0f, 0.0f, 0.0f)
, mVolume(1.0f)
, mBaseVolume(1.0f)
, mMinDistance(20.0f) /* 1 * min_range_scale */
, mMaxDistance(12750.0f) /* 255 * max_range_scale */
, mFlags(Play_Normal)
{ }
virtual ~Sound() { }
friend class OpenAL_Output;
friend class SoundManager;
};
}
#endif

@ -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

@ -0,0 +1,52 @@
#ifndef GAME_SOUND_SOUND_OUTPUT_H
#define GAME_SOUND_SOUND_OUTPUT_H
#include <string>
#include <memory>
#include <OgreVector3.h>
#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, int flags) = 0;
virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
float volume, float pitch, float min, float max, int flags) = 0;
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch) = 0;
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir) = 0;
Sound_Output& operator=(const Sound_Output &rhs);
Sound_Output(const Sound_Output &rhs);
protected:
Ogre::Vector3 mPos;
Sound_Output(SoundManager &mgr)
: mManager(mgr)
, mPos(0.0f, 0.0f, 0.0f)
{ }
public:
virtual ~Sound_Output() { }
friend class OpenAL_Output;
friend class SoundManager;
};
}
#endif

@ -6,111 +6,76 @@
#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)
{
// 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");
// 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);
}
, mOutput(new DEFAULT_OUTPUT(*this))
// 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);
}
{
if(!useSound)
return;
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;
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);
try
{
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;
// 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;
}
}
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 +84,419 @@ 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 "";
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);
}
const ESM::Sound *snd = mEnvironment.mWorld->getStore().sounds.search(soundId);
if(snd == NULL)
throw std::runtime_error(std::string("Failed to lookup sound ")+soundId);
// 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
{
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(...)
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)
{
std::cout << "Error loading " << file << ", skipping.\n";
min = 100.0f;
max = 2000.0f;
}
}
// 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())
else
{
// Get sound pointer, if any
SoundPtr snd = sit->second.lock();
// Stop the sound
if(snd) snd->stop();
sit++;
min = snd->data.minRange * 20.0f;
max = snd->data.maxRange * 50.0f;
min = std::max(min, 1.0f);
max = std::max(min, max);
}
// Remove the ptr reference
sounds.erase(it);
return "Sound/"+snd->sound;
}
// 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())
SoundMap::const_iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.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();
}
}
if(snditer->second.first == ptr && snditer->second.second == id)
return snditer->first->isPlaying();
snditer++;
}
// Nothing found, sound is not playing
return false;
return false;
}
// Remove all references to objects belonging to a given cell
void SoundManager::removeCell(const MWWorld::Ptr::CellStore *cell)
void SoundManager::stopMusic()
{
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);
}
if(mMusic)
mMusic->stop();
mMusic.reset();
}
void SoundManager::updatePositions(MWWorld::Ptr ptr)
void SoundManager::streamMusicFull(const std::string& filename)
{
// Find the reference (if any)
PtrMap::iterator it = sounds.find(ptr);
if(it != sounds.end())
std::cout <<"Playing "<<filename<< std::endl;
try
{
// 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);
}
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::stopMusic()
void SoundManager::streamMusic(const std::string& filename)
{
if (music)
music->stop();
setPlaylist();
streamMusicFull("Music/"+filename);
}
void SoundManager::startRandomTitle()
{
Ogre::StringVectorPtr filelist;
filelist = mResourceMgr.findResourceNames(Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
"Music/"+mCurrentPlaylist+"/*");
if(!filelist->size())
return;
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();
}
int i = rand()%filelist->size();
streamMusicFull((*filelist)[i]);
}
void SoundManager::streamMusic(const std::string& filename)
bool SoundManager::isMusicPlaying()
{
std::string filePath = mMusicLibrary.locate(filename, mFSStrict, true).string();
if(!filePath.empty())
{
streamMusicFull(filePath);
}
return mMusic && mMusic->isPlaying();
}
void SoundManager::startRandomTitle()
{
if(mCurrentPlaylist && !mCurrentPlaylist->empty())
void SoundManager::playPlaylist(const std::string &playlist)
{
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";
mCurrentPlaylist = playlist;
startRandomTitle();
}
void SoundManager::say(MWWorld::Ptr ptr, const std::string& filename)
{
try
{
streamMusicFull(music);
// The range values are not tested
float basevol = 1.0f; /* TODO: volume settings */
std::string filePath = "Sound/"+filename;
const ESM::Position &pos = ptr.getCellRef().pos;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f,
20.0f, 12750.0f, Play_Normal);
sound->mPos = objpos;
sound->mBaseVolume = basevol;
mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound"));
}
catch (std::exception &e)
catch(std::exception &e)
{
std::cout << " Music Error: " << e.what() << "\n";
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
}
}
bool SoundManager::isMusicPlaying()
bool SoundManager::sayDone(MWWorld::Ptr ptr) const
{
bool test = false;
if(music)
{
test = music->isPlaying();
}
return test;
return !isPlaying(ptr, "_say_sound");
}
bool SoundManager::setPlaylist(std::string playlist)
SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode)
{
const Files::PathContainer* previousPlaylist;
previousPlaylist = mCurrentPlaylist;
if (playlist == "")
SoundPtr sound;
try
{
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
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, mode);
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
}
else if(mMusicLibrary.containsSection(playlist, mFSStrict))
catch(std::exception &e)
{
mCurrentPlaylist = mMusicLibrary.section(playlist, mFSStrict);
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
else
return sound;
}
SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
float volume, float pitch, int mode)
{
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;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode);
sound->mPos = objpos;
sound->mVolume = volume;
sound->mBaseVolume = basevol;
sound->mMinDistance = min;
sound->mMaxDistance = max;
sound->mFlags = mode;
if((mode&Play_NoTrack))
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
else
mActiveSounds[sound] = std::make_pair(ptr, soundId);
}
catch(std::exception &e)
{
std::cout << "Warning: playlist named " << playlist << " does not exist.\n";
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
return previousPlaylist == mCurrentPlaylist;
return sound;
}
void SoundManager::playPlaylist(std::string playlist)
void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId)
{
if (!mUsingSound)
return;
if (playlist == "")
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
if(!isMusicPlaying())
if(snditer->second.first == ptr && snditer->second.second == soundId)
{
startRandomTitle();
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
return;
else
snditer++;
}
}
if(!setPlaylist(playlist))
{
startRandomTitle();
}
else if (!isMusicPlaying())
void SoundManager::stopSound3D(MWWorld::Ptr ptr)
{
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
startRandomTitle();
if(snditer->second.first == ptr)
{
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
else
snditer++;
}
}
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 != "")
void SoundManager::stopSound(const MWWorld::Ptr::CellStore *cell)
{
SoundPtr snd = mgr->load(file);
snd->setRepeat(loop);
snd->setVolume(volume);
snd->setRange(min,max);
snd->setPitch(pitch);
snd->setRelative(true);
snd->play();
if (loop)
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
// Only add the looping sound once
IDMap::iterator it = mLoopedSounds.find(soundId);
if(it == mLoopedSounds.end())
if(snditer->second.first != MWWorld::Ptr() &&
snditer->second.first.getCell() == cell)
{
mLoopedSounds[soundId] = WSoundPtr(snd);
snditer->first->stop();
mActiveSounds.erase(snditer++);
}
else
snditer++;
}
}
}
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);
}
void SoundManager::stopSound3D (MWWorld::Ptr ptr, const std::string& soundId)
{
remove(ptr, soundId);
}
void SoundManager::stopSound (MWWorld::Ptr::CellStore *cell)
{
removeCell(cell);
}
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.
return isPlaying(ptr, soundId);
}
bool SoundManager::getSoundPlaying(MWWorld::Ptr ptr, const std::string& soundId) const
{
return isPlaying(ptr, soundId);
}
void SoundManager::updateObject(MWWorld::Ptr ptr)
{
updatePositions(ptr);
}
void SoundManager::updateObject(MWWorld::Ptr ptr)
{
const ESM::Position &pos = ptr.getCellRef().pos;
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
SoundMap::iterator snditer = mActiveSounds.begin();
while(snditer != mActiveSounds.end())
{
if(snditer->second.first == ptr)
snditer->first->setPosition(objpos);
snditer++;
}
}
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)
{
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;
}
ESM::Region test = (ESM::Region) *(mEnvironment.mWorld->getStore().regions.find(current->cell->region));
int r = (int)(rand()/((double)RAND_MAX+1) * total);
int pos = 0;
timePassed = 0;
if (regionName != current->cell->region)
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)
{
regionName = current->cell->region;
total = 0;
//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();
if(test.soundList.size() > 0)
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.
const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]);
const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]);
const Ogre::Vector3 up(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
{
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;
}
snditer->first->update();
snditer++;
}
}
else if(current->cell->data.flags & current->cell->Interior)
}
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)
{
regionName = "";
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,109 @@ 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;
enum PlayMode {
Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */
Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */
Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */
Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position
* but do not keep it updated (the sound will not move with
* the object and will not stop when the object is deleted. */
};
static inline int operator|(const PlayMode &a, const PlayMode &b)
{ return (int)a | (int)b; }
static inline int operator&(const PlayMode &a, const PlayMode &b)
{ return (int)a & (int)b; }
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;
void streamMusicFull (const std::string& filename);
///< Play a soundifle
/// \param absolute filename
/* This is the sound manager. It loades, stores and deletes
sounds based on the sound factory it is given.
*/
OEManagerPtr mgr;
Mangle::Sound::SoundPtr music;
/* This class calls update() on the sound manager each frame
using and Ogre::FrameListener
*/
Mangle::Sound::OgreOutputUpdater updater;
/* This class tracks the movement of an Ogre::Camera and moves
a sound listener automatically to follow it.
*/
Mangle::Sound::OgreListenerMover cameraTracker;
typedef std::map<std::string,Mangle::Sound::WSoundPtr> IDMap;
typedef std::map<MWWorld::Ptr,IDMap> PtrMap;
PtrMap sounds;
// A list of all sound files used to lookup paths
Files::PathContainer mSoundFiles;
MWWorld::Environment& mEnvironment;
// A library of all Music file paths stored by the folder they are contained in
Files::FileLibrary mMusicLibrary;
std::auto_ptr<Sound_Output> mOutput;
// Points to the current playlist of music files stored in the music library
const Files::PathContainer* mCurrentPlaylist;
boost::shared_ptr<Sound> mMusic;
std::string mCurrentPlaylist;
IDMap mLoopedSounds;
typedef std::pair<MWWorld::Ptr,std::string> PtrIDPair;
typedef std::map<SoundPtr,PtrIDPair> SoundMap;
SoundMap mActiveSounds;
bool mUsingSound;
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);
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);
SoundManager(const SoundManager &rhs);
SoundManager& operator=(const SoundManager &rhs);
public:
protected:
DecoderPtr getDecoder();
friend class OpenAL_Output;
SoundManager(Ogre::Root*, Ogre::Camera*,
const Files::PathContainer& dataDir, bool useSound, bool fsstrict,
MWWorld::Environment& environment);
~SoundManager();
public:
SoundManager(bool useSound, MWWorld::Environment& environment);
~SoundManager();
void stopMusic();
///< Stops music if it's playing
void stopMusic();
///< Stops music if it's playing
void streamMusic(const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void streamMusic(const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void startRandomTitle();
///< Starts a random track from the current playlist
void startRandomTitle();
///< Starts a random track from the current playlist
bool isMusicPlaying();
///< Returns true if music is playing
bool isMusicPlaying();
///< Returns true if music is playing
bool setPlaylist(std::string playlist="");
///< Set the playlist to an existing folder
/// \param name of the folder that contains the playlist
/// if none is set then it is set to an empty playlist
/// \return Return true if the previous playlist was the same
void playPlaylist(const std::string &playlist);
///< Start playing music from the selected folder
/// \param name of the folder that contains the playlist
void playPlaylist(std::string playlist="");
///< Start playing music from the selected folder
/// \param name of the folder that contains the playlist
/// if none is set then it plays from the current playlist
void say(MWWorld::Ptr reference, const std::string& filename);
///< Make an actor say some text.
/// \param filename name of a sound file in "Sound/Vo/" in the data directory.
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 sayDone(MWWorld::Ptr reference) const;
///< Is actor not speaking?
bool sayDone (MWWorld::Ptr reference) const;
///< Is actor not speaking?
SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal);
///< Play a sound, independently of 3D-position
void playSound (const std::string& soundId, float volume, float pitch, bool loop=false);
///< Play a sound, independently of 3D-position
SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId,
float volume, float pitch, int mode=Play_Normal);
///< Play a sound from an object
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,
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 stopSound3D(MWWorld::Ptr reference);
///< Stop the given object from playing all sounds.
void stopSound (MWWorld::Ptr::CellStore *cell);
///< Stop all sounds for the given cell.
void stopSound(const MWWorld::Ptr::CellStore *cell);
///< Stop all sounds for the given cell.
void stopSound(const std::string& soundId);
///< Stop a non-3d looping sound
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?
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 updateObject(MWWorld::Ptr reference);
///< Update the position of all sounds connected to the given object.
void update (float duration);
void update(float duration);
};
}

@ -8,6 +8,29 @@
#include <components/esm/loadcont.hpp>
#include "manualref.hpp"
#include "refdata.hpp"
namespace
{
template<typename T>
float getTotalWeight (const ESMS::CellRefList<T, MWWorld::RefData>& cellRefList)
{
float sum = 0;
for (typename ESMS::CellRefList<T, MWWorld::RefData>::List::const_iterator iter (
cellRefList.list.begin());
iter!=cellRefList.list.end();
++iter)
{
if (iter->mData.getCount()>0)
sum += iter->mData.getCount()*iter->base->data.weight;
}
return sum;
}
}
MWWorld::ContainerStore::ContainerStore() : mStateId (0), mCachedWeight (0), mWeightUpToDate (false) {}
MWWorld::ContainerStore::~ContainerStore() {}
@ -40,6 +63,8 @@ void MWWorld::ContainerStore::add (const Ptr& ptr)
case Type_Repair: repairs.list.push_back (*ptr.get<ESM::Repair>()); break;
case Type_Weapon: weapons.list.push_back (*ptr.get<ESM::Weapon>()); break;
}
flagAsModified();
}
void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const ESMS::ESMStore& store)
@ -58,6 +83,8 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const ESMS:
ref.getPtr().getRefData().setCount (iter->count);
add (ref.getPtr());
}
flagAsModified();
}
void MWWorld::ContainerStore::clear()
@ -74,6 +101,44 @@ void MWWorld::ContainerStore::clear()
probes.list.clear();
repairs.list.clear();
weapons.list.clear();
flagAsModified();
}
void MWWorld::ContainerStore::flagAsModified()
{
++mStateId;
mWeightUpToDate = false;
}
int MWWorld::ContainerStore::getStateId() const
{
return mStateId;
}
float MWWorld::ContainerStore::getWeight() const
{
if (!mWeightUpToDate)
{
mCachedWeight = 0;
mCachedWeight += getTotalWeight (potions);
mCachedWeight += getTotalWeight (appas);
mCachedWeight += getTotalWeight (armors);
mCachedWeight += getTotalWeight (books);
mCachedWeight += getTotalWeight (clothes);
mCachedWeight += getTotalWeight (ingreds);
mCachedWeight += getTotalWeight (lights);
mCachedWeight += getTotalWeight (lockpicks);
mCachedWeight += getTotalWeight (miscItems);
mCachedWeight += getTotalWeight (probes);
mCachedWeight += getTotalWeight (repairs);
mCachedWeight += getTotalWeight (weapons);
mWeightUpToDate = true;
}
return mCachedWeight;
}
int MWWorld::ContainerStore::getType (const Ptr& ptr)
@ -301,23 +366,30 @@ MWWorld::Ptr *MWWorld::ContainerStoreIterator::operator->() const
MWWorld::Ptr MWWorld::ContainerStoreIterator::operator*() const
{
Ptr ptr;
switch (mType)
{
case ContainerStore::Type_Potion: return MWWorld::Ptr (&*mPotion, 0);
case ContainerStore::Type_Apparatus: return MWWorld::Ptr (&*mApparatus, 0);
case ContainerStore::Type_Armor: return MWWorld::Ptr (&*mArmor, 0);
case ContainerStore::Type_Book: return MWWorld::Ptr (&*mBook, 0);
case ContainerStore::Type_Clothing: return MWWorld::Ptr (&*mClothing, 0);
case ContainerStore::Type_Ingredient: return MWWorld::Ptr (&*mIngredient, 0);
case ContainerStore::Type_Light: return MWWorld::Ptr (&*mLight, 0);
case ContainerStore::Type_Lockpick: return MWWorld::Ptr (&*mLockpick, 0);
case ContainerStore::Type_Miscellaneous: return MWWorld::Ptr (&*mMiscellaneous, 0);
case ContainerStore::Type_Probe: return MWWorld::Ptr (&*mProbe, 0);
case ContainerStore::Type_Repair: return MWWorld::Ptr (&*mRepair, 0);
case ContainerStore::Type_Weapon: return MWWorld::Ptr (&*mWeapon, 0);
case ContainerStore::Type_Potion: ptr = MWWorld::Ptr (&*mPotion, 0); break;
case ContainerStore::Type_Apparatus: ptr = MWWorld::Ptr (&*mApparatus, 0); break;
case ContainerStore::Type_Armor: ptr = MWWorld::Ptr (&*mArmor, 0); break;
case ContainerStore::Type_Book: ptr = MWWorld::Ptr (&*mBook, 0); break;
case ContainerStore::Type_Clothing: ptr = MWWorld::Ptr (&*mClothing, 0); break;
case ContainerStore::Type_Ingredient: ptr = MWWorld::Ptr (&*mIngredient, 0); break;
case ContainerStore::Type_Light: ptr = MWWorld::Ptr (&*mLight, 0); break;
case ContainerStore::Type_Lockpick: ptr = MWWorld::Ptr (&*mLockpick, 0); break;
case ContainerStore::Type_Miscellaneous: ptr = MWWorld::Ptr (&*mMiscellaneous, 0); break;
case ContainerStore::Type_Probe: ptr = MWWorld::Ptr (&*mProbe, 0); break;
case ContainerStore::Type_Repair: ptr = MWWorld::Ptr (&*mRepair, 0); break;
case ContainerStore::Type_Weapon: ptr = MWWorld::Ptr (&*mWeapon, 0); break;
}
throw std::runtime_error ("invalid pointer");
if (ptr.isEmpty())
throw std::runtime_error ("invalid iterator");
ptr.setContainerStore (mContainer);
return ptr;
}
MWWorld::ContainerStoreIterator& MWWorld::ContainerStoreIterator::operator++()

@ -52,9 +52,14 @@ namespace MWWorld
ESMS::CellRefList<ESM::Probe, RefData> probes;
ESMS::CellRefList<ESM::Repair, RefData> repairs;
ESMS::CellRefList<ESM::Weapon, RefData> weapons;
int mStateId;
mutable float mCachedWeight;
mutable bool mWeightUpToDate;
public:
ContainerStore();
virtual ~ContainerStore();
ContainerStoreIterator begin (int mask = Type_All);
@ -75,6 +80,18 @@ namespace MWWorld
void clear();
///< Empty container.
void flagAsModified();
///< \attention This function is internal to the world model and should not be called from
/// outside.
int getStateId() const;
///< This ID is changed every time the container is modified or items in the container
/// are accessed in a way that may be used to modify the item.
/// \note This method of change-tracking will ocasionally yield false positives.
float getWeight() const;
///< Return total weight of the items contained in *this.
static int getType (const Ptr& ptr);
///< This function throws an exception, if ptr does not point to an object, that can be
/// put into a container.

@ -65,6 +65,8 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite
/// \todo unstack item pointed to by iterator if required)
mSlots[slot] = iterator;
flagAsModified();
}
MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot)

@ -53,6 +53,28 @@ namespace MWWorld
return mEngine->rayTest(from,to);
}
std::vector < std::pair <float, std::string> > PhysicsSystem::getFacedObjects ()
{
//get a ray pointing to the center of the viewport
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(
mRender.getViewport()->getWidth()/2,
mRender.getViewport()->getHeight()/2);
btVector3 from(centerRay.getOrigin().x,-centerRay.getOrigin().z,centerRay.getOrigin().y);
btVector3 to(centerRay.getPoint(500).x,-centerRay.getPoint(500).z,centerRay.getPoint(500).y);
return mEngine->rayTest2(from,to);
}
btVector3 PhysicsSystem::getRayPoint(float extent)
{
//get a ray pointing to the center of the viewport
Ray centerRay = mRender.getCamera()->getCameraToViewportRay(
mRender.getViewport()->getWidth()/2,
mRender.getViewport()->getHeight()/2);
btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y);
return result;
}
bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to)
{

@ -36,7 +36,11 @@ namespace MWWorld
bool toggleCollisionMode();
std::pair<std::string, float> getFacedHandle (MWWorld::World& world);
btVector3 getRayPoint(float extent);
std::vector < std::pair <float, std::string> > getFacedObjects ();
// cast ray, return true if it hit something
bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to);

@ -0,0 +1,39 @@
#include "ptr.hpp"
#include <cassert>
#include "containerstore.hpp"
ESM::CellRef& MWWorld::Ptr::getCellRef() const
{
assert (mCellRef);
if (mContainerStore)
mContainerStore->flagAsModified();
return *mCellRef;
}
MWWorld::RefData& MWWorld::Ptr::getRefData() const
{
assert (mRefData);
if (mContainerStore)
mContainerStore->flagAsModified();
return *mRefData;
}
void MWWorld::Ptr::setContainerStore (ContainerStore *store)
{
assert (store);
assert (!mCell);
mContainerStore = store;
}
MWWorld::ContainerStore *MWWorld::Ptr::getContainerStore() const
{
return mContainerStore;
}

@ -13,6 +13,8 @@
namespace MWWorld
{
class ContainerStore;
/// \brief Pointer to a LiveCellRef
class Ptr
@ -26,10 +28,11 @@ namespace MWWorld
RefData *mRefData;
CellStore *mCell;
std::string mTypeName;
ContainerStore *mContainerStore;
public:
Ptr() : mCellRef (0), mRefData (0), mCell (0) {}
Ptr() : mCellRef (0), mRefData (0), mCell (0), mContainerStore (0) {}
bool isEmpty() const
{
@ -49,6 +52,7 @@ namespace MWWorld
template<typename T>
Ptr (ESMS::LiveCellRef<T, RefData> *liveCellRef, CellStore *cell)
: mContainerStore (0)
{
mPtr = liveCellRef;
mCellRef = &liveCellRef->ref;
@ -63,23 +67,21 @@ namespace MWWorld
return boost::any_cast<ESMS::LiveCellRef<T, RefData>*> (mPtr);
}
ESM::CellRef& getCellRef() const
{
assert (mCellRef);
return *mCellRef;
}
ESM::CellRef& getCellRef() const;
RefData& getRefData() const
{
assert (mRefData);
return *mRefData;
}
RefData& getRefData() const;
Ptr::CellStore *getCell() const
{
assert (mCell);
return mCell;
}
void setContainerStore (ContainerStore *store);
///< Must not be called on references that are in a cell.
ContainerStore *getContainerStore() const;
///< May return a 0-pointer, if reference is not in a container.
};
inline bool operator== (const Ptr& left, const Ptr& right)

@ -6,6 +6,8 @@
#include "../mwsound/soundmanager.hpp"
#include "../mwgui/window_manager.hpp"
#include "ptr.hpp"
#include "environment.hpp"
#include "player.hpp"
@ -52,9 +54,11 @@ void insertCellRefList(MWRender::RenderingManager& rendering, MWWorld::Environme
namespace MWWorld
{
void Scene::update (float duration){
mRendering.update (duration);
}
void Scene::unloadCell (CellStoreCollection::iterator iter)
{
std::cout << "Unloading cell\n";
@ -77,6 +81,7 @@ namespace MWWorld
mPhysics->removeObject (node->getName());
}
}
mRendering.removeCell(*iter);
//mPhysics->removeObject("Unnamed_43");
@ -85,6 +90,7 @@ namespace MWWorld
mEnvironment.mSoundManager->stopSound (*iter);
mActiveCells.erase(*iter);
}
@ -99,9 +105,10 @@ namespace MWWorld
mActiveCells.insert(cell);
if(result.second){
insertCell(*cell, mEnvironment);
mRendering.cellAdded (cell);
mRendering.cellAdded(cell);
mRendering.configureAmbient(*cell);
mRendering.requestMap(cell);
mRendering.configureAmbient(*cell);
}
@ -117,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());
@ -185,6 +196,7 @@ namespace MWWorld
mCurrentCell = *iter;
// adjust player
playerCellChange (mWorld->getExterior(X, Y), position, adjustPlayerPos);
@ -192,6 +204,7 @@ namespace MWWorld
mWorld->adjustSky();
mCellChanged = true;
mRendering.waterAdded(mCurrentCell);
}
//We need the ogre renderer and a scene node.
@ -231,6 +244,7 @@ namespace MWWorld
Ptr::CellStore *cell = mWorld->getInterior(cellName);
loadCell (cell);
// adjust player
mCurrentCell = cell;
@ -243,6 +257,8 @@ namespace MWWorld
mWorld->adjustSky();
mCellChanged = true;
mRendering.waterAdded(cell);
}
void Scene::changeToExteriorCell (const ESM::Position& position)

@ -268,7 +268,8 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering, Environmen
blight.mGlareView = 0;
blight.mAmbientLoopSoundID = "blight";
mWeatherSettings["blight"] = blight;
/*
Weather snow;
snow.mCloudTexture = "tx_bm_sky_snow.dds";
snow.mCloudsMaximumPercent = 1.0;
@ -325,6 +326,7 @@ WeatherManager::WeatherManager(MWRender::RenderingManager* rendering, Environmen
blizzard.mGlareView = 0;
blizzard.mAmbientLoopSoundID = "BM Blizzard";
mWeatherSettings["blizzard"] = blizzard;
*/
}
void WeatherManager::setWeather(const String& weather, bool instant)
@ -506,32 +508,32 @@ void WeatherManager::update(float duration)
float thunder = region->data.thunder/255.f;
float ash = region->data.ash/255.f;
float blight = region->data.blight/255.f;
float snow = region->data.a/255.f;
float blizzard = region->data.b/255.f;
//float snow = region->data.a/255.f;
//float blizzard = region->data.b/255.f;
// re-scale to 100 percent
const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight+snow+blizzard;
const float total = clear+cloudy+foggy+overcast+rain+thunder+ash+blight;//+snow+blizzard;
srand(time(NULL));
float random = ((rand()%100)/100.f) * total;
if (random >= snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear)
weather = "blizzard";
else if (random >= blight+ash+thunder+rain+overcast+foggy+cloudy+clear)
weather = "snow";
else if (random >= ash+thunder+rain+overcast+foggy+cloudy+clear)
//if (random > snow+blight+ash+thunder+rain+overcast+foggy+cloudy+clear)
// weather = "blizzard";
//else if (random > blight+ash+thunder+rain+overcast+foggy+cloudy+clear)
// weather = "snow";
/*else*/ if (random > ash+thunder+rain+overcast+foggy+cloudy+clear)
weather = "blight";
else if (random >= thunder+rain+overcast+foggy+cloudy+clear)
else if (random > thunder+rain+overcast+foggy+cloudy+clear)
weather = "ashstorm";
else if (random >= rain+overcast+foggy+cloudy+clear)
else if (random > rain+overcast+foggy+cloudy+clear)
weather = "thunderstorm";
else if (random >= overcast+foggy+cloudy+clear)
else if (random > overcast+foggy+cloudy+clear)
weather = "rain";
else if (random >= foggy+cloudy+clear)
else if (random > foggy+cloudy+clear)
weather = "overcast";
else if (random >= cloudy+clear)
else if (random > cloudy+clear)
weather = "foggy";
else if (random >= clear)
else if (random > clear)
weather = "cloudy";
else
weather = "clear";

@ -157,7 +157,8 @@ namespace MWWorld
const std::string& master, const boost::filesystem::path& resDir,
bool newGame, Environment& environment, const std::string& encoding)
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this)
mSky (true), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this),
mNumFacing(0)
{
mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine();
@ -498,13 +499,21 @@ namespace MWWorld
std::string World::getFacedHandle()
{
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this);
if (!mRendering->occlusionQuerySupported())
{
std::pair<std::string, float> result = mPhysics->getFacedHandle (*this);
if (result.first.empty() ||
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i)
return "";
if (result.first.empty() ||
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i)
return "";
return result.first;
return result.first;
}
else
{
// updated every few frames in update()
return mFacedHandle;
}
}
void World::deleteObject (Ptr ptr)
@ -531,9 +540,10 @@ namespace MWWorld
ptr.getRefData().getPosition().pos[0] = x;
ptr.getRefData().getPosition().pos[1] = y;
ptr.getRefData().getPosition().pos[2] = z;
if (ptr==mPlayer->getPlayer())
{
//std::cout << "X:" << ptr.getRefData().getPosition().pos[0] << " Z: " << ptr.getRefData().getPosition().pos[1] << "\n";
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
if (currentCell)
{
@ -705,13 +715,82 @@ namespace MWWorld
mWeatherManager->update (duration);
// cast a ray from player to sun to detect if the sun is visible
// this is temporary until we find a better place to put this code
// currently its here because we need to access the physics system
float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
if (!mRendering->occlusionQuerySupported())
{
// cast a ray from player to sun to detect if the sun is visible
// this is temporary until we find a better place to put this code
// currently its here because we need to access the physics system
float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
}
// update faced handle (object the player is looking at)
// this uses a mixture of raycasts and occlusion queries.
else // if (mRendering->occlusionQuerySupported())
{
MWRender::OcclusionQuery* query = mRendering->getOcclusionQuery();
if (!query->occlusionTestPending())
{
// get result of last query
if (mNumFacing == 0) mFacedHandle = "";
else if (mNumFacing == 1)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced1Name : "";
}
else if (mNumFacing == 2)
{
bool result = query->getTestResult();
mFacedHandle = result ? mFaced2Name : mFaced1Name;
}
// send new query
// figure out which object we want to test against
std::vector < std::pair < float, std::string > > results = mPhysics->getFacedObjects();
// ignore the player
for (std::vector < std::pair < float, std::string > >::iterator it = results.begin();
it != results.end(); ++it)
{
if ( (*it).second == mPlayer->getPlayer().getRefData().getHandle() )
{
results.erase(it);
break;
}
}
if (results.size() == 0)
{
mNumFacing = 0;
}
else if (results.size() == 1)
{
mFaced1 = getPtrViaHandle(results.front().second);
mFaced1Name = results.front().second;
mNumFacing = 1;
btVector3 p = mPhysics->getRayPoint(results.front().first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode();
query->occlusionTest(pos, node);
}
else
{
mFaced1Name = results.front().second;
mFaced2Name = results[1].second;
mFaced1 = getPtrViaHandle(results.front().second);
mFaced2 = getPtrViaHandle(results[1].second);
mNumFacing = 2;
btVector3 p = mPhysics->getRayPoint(results[1].first);
Ogre::Vector3 pos(p.x(), p.z(), -p.y());
Ogre::SceneNode* node = mFaced2.getRefData().getBaseNode();
query->occlusionTest(pos, node);
}
}
}
}
bool World::isCellExterior() const
@ -754,4 +833,15 @@ namespace MWWorld
{
return mRendering->getFader();
}
void World::setWaterHeight(const float height)
{
mRendering->setWaterHeight(height);
}
void World::toggleWater()
{
mRendering->toggleWater();
}
}

@ -93,6 +93,12 @@ namespace MWWorld
Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore);
std::string mFacedHandle;
Ptr mFaced1;
Ptr mFaced2;
std::string mFaced1Name;
std::string mFaced2Name;
int mNumFacing;
int getDaysPerMonth (int month) const;
@ -112,6 +118,9 @@ namespace MWWorld
Ptr::CellStore *getExterior (int x, int y);
Ptr::CellStore *getInterior (const std::string& name);
void setWaterHeight(const float height);
void toggleWater();
void adjustSky();

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save