mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-10 06:44:29 +00:00
Merge branch 'master' of https://github.com/zinnschlag/openmw.git into Factions2
Conflicts: apps/openmw/mwscript/docs/vmformat.txt
This commit is contained in:
commit
07ea63c10c
@ -4,9 +4,6 @@ if (APPLE)
|
|||||||
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
|
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
|
||||||
|
|
||||||
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
|
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)
|
endif (APPLE)
|
||||||
|
|
||||||
# Macros
|
# Macros
|
||||||
@ -146,6 +143,7 @@ endif (USE_MPG123)
|
|||||||
|
|
||||||
# Platform specific
|
# Platform specific
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
set(PLATFORM_INCLUDE_DIR "platform")
|
set(PLATFORM_INCLUDE_DIR "platform")
|
||||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||||
else (WIN32)
|
else (WIN32)
|
||||||
@ -163,7 +161,7 @@ endif (APPLE)
|
|||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
find_package (Threads)
|
find_package (Threads)
|
||||||
endif()
|
endif()
|
||||||
@ -184,6 +182,7 @@ ENDIF(WIN32)
|
|||||||
ENDIF(OGRE_STATIC)
|
ENDIF(OGRE_STATIC)
|
||||||
include_directories("."
|
include_directories("."
|
||||||
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
|
${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}
|
${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}
|
||||||
${PLATFORM_INCLUDE_DIR}
|
${PLATFORM_INCLUDE_DIR}
|
||||||
${MYGUI_INCLUDE_DIRS}
|
${MYGUI_INCLUDE_DIRS}
|
||||||
@ -203,6 +202,7 @@ if(APPLE)
|
|||||||
"Plugin_ParticleFX")
|
"Plugin_ParticleFX")
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
|
add_subdirectory( files/)
|
||||||
add_subdirectory( files/mygui )
|
add_subdirectory( files/mygui )
|
||||||
|
|
||||||
# Specify build paths
|
# Specify build paths
|
||||||
@ -258,7 +258,16 @@ endif (APPLE)
|
|||||||
|
|
||||||
# Compiler settings
|
# Compiler settings
|
||||||
if (CMAKE_COMPILER_IS_GNUCC)
|
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)
|
endif (CMAKE_COMPILER_IS_GNUCC)
|
||||||
|
|
||||||
if(DPKG_PROGRAM)
|
if(DPKG_PROGRAM)
|
||||||
@ -297,7 +306,7 @@ if(DPKG_PROGRAM)
|
|||||||
Data files from the original game is required to run it.")
|
Data files from the original game is required to run it.")
|
||||||
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
|
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
|
||||||
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
|
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher")
|
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
|
||||||
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_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")
|
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
|
||||||
@ -318,6 +327,7 @@ if(WIN32)
|
|||||||
FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*")
|
FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*")
|
||||||
INSTALL(FILES ${files} DESTINATION ".")
|
INSTALL(FILES ${files} DESTINATION ".")
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
|
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 ".")
|
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
|
||||||
|
|
||||||
SET(CPACK_GENERATOR "NSIS")
|
SET(CPACK_GENERATOR "NSIS")
|
||||||
@ -328,6 +338,7 @@ if(WIN32)
|
|||||||
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO})
|
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO})
|
||||||
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;esmtool;Esmtool;omwlauncher;OpenMW Launcher")
|
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_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
|
||||||
SET(CPACK_RESOURCE_FILE_LICENSE "${OpenMW_SOURCE_DIR}/GPL3.txt")
|
SET(CPACK_RESOURCE_FILE_LICENSE "${OpenMW_SOURCE_DIR}/GPL3.txt")
|
||||||
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||||
@ -381,6 +392,11 @@ if (BUILD_LAUNCHER)
|
|||||||
add_subdirectory( apps/launcher )
|
add_subdirectory( apps/launcher )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(BUILD_MWINIIMPORTER "build MWiniImporter inspector" ON)
|
||||||
|
if (BUILD_MWINIIMPORTER)
|
||||||
|
add_subdirectory( apps/mwiniimporter )
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
if (USE_DEBUG_CONSOLE)
|
if (USE_DEBUG_CONSOLE)
|
||||||
@ -470,6 +486,7 @@ if (APPLE)
|
|||||||
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
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}/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_GENERATOR "DragNDrop")
|
||||||
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
||||||
|
@ -95,5 +95,5 @@ else()
|
|||||||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
|
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
|
||||||
|
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
|
||||||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg")
|
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
|
||||||
endif()
|
endif()
|
||||||
|
@ -225,7 +225,7 @@ void DataFilesPage::setupDataFiles()
|
|||||||
msgBox.setIcon(QMessageBox::Warning);
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||||
msgBox.setText(tr("<br><b>Could not find the Data Files location</b><br><br> \
|
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>"));
|
Press \"Browse...\" to specify the location manually.<br>"));
|
||||||
|
|
||||||
QAbstractButton *dirSelectButton =
|
QAbstractButton *dirSelectButton =
|
||||||
@ -1057,16 +1057,8 @@ void DataFilesPage::writeConfig(QString profile)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the OpenMW config
|
// Open the OpenMW config as a QFile
|
||||||
QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "openmw.cfg").string());
|
QFile file(QString::fromStdString((mCfgMgr.getUserPath() / "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);
|
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||||
// File cannot be opened or created
|
// File cannot be opened or created
|
||||||
|
@ -194,6 +194,7 @@ void MainDialog::play()
|
|||||||
QDir dir(QCoreApplication::applicationDirPath());
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
QString game = dir.absoluteFilePath("openmw");
|
QString game = dir.absoluteFilePath("openmw");
|
||||||
QFile file(game);
|
QFile file(game);
|
||||||
|
game = "\"" + game + "\"";
|
||||||
#else
|
#else
|
||||||
QString game = "./openmw";
|
QString game = "./openmw";
|
||||||
QFile file(game);
|
QFile file(game);
|
||||||
|
20
apps/mwiniimporter/CMakeLists.txt
Normal file
20
apps/mwiniimporter/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
set(MWINIIMPORT
|
||||||
|
main.cpp
|
||||||
|
importer.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(MWINIIMPORT_HEADER
|
||||||
|
importer.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
|
||||||
|
|
||||||
|
add_executable(mwiniimport
|
||||||
|
${MWINIIMPORT}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(mwiniimport
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
components
|
||||||
|
)
|
||||||
|
|
184
apps/mwiniimporter/importer.cpp
Normal file
184
apps/mwiniimporter/importer.cpp
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
#include "importer.hpp"
|
||||||
|
#include <boost/iostreams/device/file.hpp>
|
||||||
|
#include <boost/iostreams/stream.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
MwIniImporter::MwIniImporter() {
|
||||||
|
const char *map[][2] =
|
||||||
|
{
|
||||||
|
{ "fps", "General:Show FPS" },
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
for(int i=0; map[i][0]; i++) {
|
||||||
|
mMergeMap.insert(std::make_pair<std::string, std::string>(map[i][0], map[i][1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::setVerbose(bool verbose) {
|
||||||
|
mVerbose = verbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MwIniImporter::numberToString(int n) {
|
||||||
|
std::stringstream str;
|
||||||
|
str << n;
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) {
|
||||||
|
std::cout << "load ini file: " << filename << std::endl;
|
||||||
|
|
||||||
|
std::string section("");
|
||||||
|
MwIniImporter::multistrmap map;
|
||||||
|
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
|
||||||
|
if(line[0] == '[') {
|
||||||
|
if(line.length() > 2) {
|
||||||
|
section = line.substr(1, line.length()-3);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int comment_pos = line.find(";");
|
||||||
|
if(comment_pos > 0) {
|
||||||
|
line = line.substr(0,comment_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(line.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = line.find("=");
|
||||||
|
if(pos < 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key(section + ":" + line.substr(0,pos));
|
||||||
|
std::string value(line.substr(pos+1));
|
||||||
|
|
||||||
|
multistrmap::iterator it;
|
||||||
|
if((it = map.find(key)) == map.end()) {
|
||||||
|
map.insert( std::make_pair<std::string, std::vector<std::string> > (key, std::vector<std::string>() ) );
|
||||||
|
}
|
||||||
|
map[key].push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
MwIniImporter::multistrmap MwIniImporter::loadCfgFile(std::string filename) {
|
||||||
|
std::cout << "load cfg file: " << filename << std::endl;
|
||||||
|
|
||||||
|
MwIniImporter::multistrmap map;
|
||||||
|
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
|
||||||
|
// we cant say comment by only looking at first char anymore
|
||||||
|
int comment_pos = line.find("#");
|
||||||
|
if(comment_pos > 0) {
|
||||||
|
line = line.substr(0,comment_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(line.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = line.find("=");
|
||||||
|
if(pos < 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key(line.substr(0,pos));
|
||||||
|
std::string value(line.substr(pos+1));
|
||||||
|
|
||||||
|
multistrmap::iterator it;
|
||||||
|
if((it = map.find(key)) == map.end()) {
|
||||||
|
map.insert( std::make_pair<std::string, std::vector<std::string> > (key, std::vector<std::string>() ) );
|
||||||
|
}
|
||||||
|
map[key].push_back(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::merge(multistrmap &cfg, multistrmap &ini) {
|
||||||
|
multistrmap::iterator cfgIt;
|
||||||
|
multistrmap::iterator iniIt;
|
||||||
|
for(strmap::iterator it=mMergeMap.begin(); it!=mMergeMap.end(); it++) {
|
||||||
|
if((iniIt = ini.find(it->second)) != ini.end()) {
|
||||||
|
cfg.erase(it->first);
|
||||||
|
if(!this->specialMerge(it->first, it->second, cfg, ini)) {
|
||||||
|
cfg.insert(std::make_pair<std::string, std::vector<std::string> >(it->first, iniIt->second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MwIniImporter::specialMerge(std::string cfgKey, std::string iniKey, multistrmap &cfg, multistrmap &ini) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
||||||
|
std::vector<std::string> esmFiles;
|
||||||
|
std::vector<std::string> espFiles;
|
||||||
|
std::string baseGameFile("Game Files:GameFile");
|
||||||
|
std::string gameFile("");
|
||||||
|
|
||||||
|
multistrmap::iterator it = ini.begin();
|
||||||
|
for(int i=0; it != ini.end(); i++) {
|
||||||
|
gameFile = baseGameFile;
|
||||||
|
gameFile.append(this->numberToString(i));
|
||||||
|
|
||||||
|
it = ini.find(gameFile);
|
||||||
|
if(it == ini.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); entry++) {
|
||||||
|
std::string filetype(entry->substr(entry->length()-4, 3));
|
||||||
|
std::transform(filetype.begin(), filetype.end(), filetype.begin(), ::tolower);
|
||||||
|
|
||||||
|
if(filetype.compare("esm") == 0) {
|
||||||
|
esmFiles.push_back(*entry);
|
||||||
|
}
|
||||||
|
else if(filetype.compare("esp") == 0) {
|
||||||
|
espFiles.push_back(*entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gameFile = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.erase("master");
|
||||||
|
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) );
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator it=esmFiles.begin(); it!=esmFiles.end(); it++) {
|
||||||
|
cfg["master"].push_back(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.erase("plugin");
|
||||||
|
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator it=espFiles.begin(); it!=espFiles.end(); it++) {
|
||||||
|
cfg["plugin"].push_back(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg) {
|
||||||
|
|
||||||
|
for(multistrmap::iterator it=cfg.begin(); it != cfg.end(); it++) {
|
||||||
|
for(std::vector<std::string>::iterator entry=it->second.begin(); entry != it->second.end(); entry++) {
|
||||||
|
out << (it->first) << "=" << (*entry) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
apps/mwiniimporter/importer.hpp
Normal file
32
apps/mwiniimporter/importer.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef MWINIIMPORTER_IMPORTER
|
||||||
|
#define MWINIIMPORTER_IMPORTER 1
|
||||||
|
|
||||||
|
#include <boost/iostreams/device/file.hpp>
|
||||||
|
#include <boost/iostreams/stream.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
class MwIniImporter {
|
||||||
|
public:
|
||||||
|
typedef std::map<std::string, std::string> strmap;
|
||||||
|
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
||||||
|
|
||||||
|
MwIniImporter();
|
||||||
|
void setVerbose(bool verbose);
|
||||||
|
multistrmap loadIniFile(std::string filename);
|
||||||
|
multistrmap loadCfgFile(std::string filename);
|
||||||
|
void merge(multistrmap &cfg, multistrmap &ini);
|
||||||
|
void importGameFiles(multistrmap &cfg, multistrmap &ini);
|
||||||
|
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool specialMerge(std::string cfgKey, std::string iniKey, multistrmap &cfg, multistrmap &ini);
|
||||||
|
std::string numberToString(int n);
|
||||||
|
bool mVerbose;
|
||||||
|
strmap mMergeMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
79
apps/mwiniimporter/main.cpp
Normal file
79
apps/mwiniimporter/main.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "importer.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
bpo::options_description desc("Syntax: mwiniimporter <options>\nAllowed options");
|
||||||
|
desc.add_options()
|
||||||
|
("help,h", "produce help message")
|
||||||
|
("verbose,v", "verbose output")
|
||||||
|
("ini,i", bpo::value<std::string>(), "morrowind.ini file")
|
||||||
|
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
|
||||||
|
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
|
||||||
|
("game-files,g", "import esm and esp files")
|
||||||
|
;
|
||||||
|
|
||||||
|
bpo::variables_map vm;
|
||||||
|
try {
|
||||||
|
bpo::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
|
||||||
|
|
||||||
|
// parse help before calling notify because we dont want it to throw an error if help is set
|
||||||
|
if(vm.count("help")) {
|
||||||
|
std::cout << desc;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpo::notify(vm);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(std::exception& e) {
|
||||||
|
std::cerr << "Error:" << e.what() << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
catch(...) {
|
||||||
|
std::cerr << "Error" << std::endl;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string iniFile = vm["ini"].as<std::string>();
|
||||||
|
std::string cfgFile = vm["cfg"].as<std::string>();
|
||||||
|
|
||||||
|
// if no output is given, write back to cfg file
|
||||||
|
std::string outputFile(vm["output"].as<std::string>());
|
||||||
|
if(vm["output"].defaulted()) {
|
||||||
|
outputFile = vm["cfg"].as<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!boost::filesystem::exists(iniFile)) {
|
||||||
|
std::cerr << "ini file does not exist" << std::endl;
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
if(!boost::filesystem::exists(cfgFile)) {
|
||||||
|
std::cerr << "cfg file does not exist" << std::endl;
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
MwIniImporter importer;
|
||||||
|
importer.setVerbose(vm.count("verbose"));
|
||||||
|
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
||||||
|
|
||||||
|
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
|
||||||
|
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
||||||
|
|
||||||
|
importer.merge(cfg, ini);
|
||||||
|
|
||||||
|
if(vm.count("game-files")) {
|
||||||
|
importer.importGameFiles(cfg, ini);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "write to: " << outputFile << std::endl;
|
||||||
|
importer.writeToFile(file, cfg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
|
|||||||
|
|
||||||
add_openmw_dir (mwrender
|
add_openmw_dir (mwrender
|
||||||
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
|
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
|
||||||
renderinginterface localmap
|
renderinginterface localmap occlusionquery terrain terrainmaterial water
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwinput
|
add_openmw_dir (mwinput
|
||||||
@ -82,6 +82,7 @@ add_definitions(${SOUND_DEFINE})
|
|||||||
|
|
||||||
target_link_libraries(openmw
|
target_link_libraries(openmw
|
||||||
${OGRE_LIBRARIES}
|
${OGRE_LIBRARIES}
|
||||||
|
${OGRE_Terrain_LIBRARY}
|
||||||
${OGRE_STATIC_PLUGINS}
|
${OGRE_STATIC_PLUGINS}
|
||||||
${OIS_LIBRARIES}
|
${OIS_LIBRARIES}
|
||||||
${Boost_LIBRARIES}
|
${Boost_LIBRARIES}
|
||||||
|
@ -204,13 +204,18 @@ OMW::Engine::~Engine()
|
|||||||
void OMW::Engine::loadBSA()
|
void OMW::Engine::loadBSA()
|
||||||
{
|
{
|
||||||
const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
|
const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
|
||||||
std::string dataDirectory;
|
|
||||||
for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter)
|
for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter)
|
||||||
{
|
{
|
||||||
std::cout << "Adding " << iter->second.string() << std::endl;
|
std::cout << "Adding " << iter->second.string() << std::endl;
|
||||||
Bsa::addBSA(iter->second.string());
|
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;
|
std::cout << "Data dir " << dataDirectory << std::endl;
|
||||||
Bsa::addDir(dataDirectory, mFSStrict);
|
Bsa::addDir(dataDirectory, mFSStrict);
|
||||||
}
|
}
|
||||||
@ -315,7 +320,11 @@ void OMW::Engine::go()
|
|||||||
|
|
||||||
// This has to be added BEFORE MyGUI is initialized, as it needs
|
// This has to be added BEFORE MyGUI is initialized, as it needs
|
||||||
// to find core.xml here.
|
// to find core.xml here.
|
||||||
|
|
||||||
|
//addResourcesDirectory(mResDir);
|
||||||
|
|
||||||
addResourcesDirectory(mResDir / "mygui");
|
addResourcesDirectory(mResDir / "mygui");
|
||||||
|
addResourcesDirectory(mResDir / "water");
|
||||||
|
|
||||||
// Create the window
|
// Create the window
|
||||||
mOgre->createWindow("OpenMW");
|
mOgre->createWindow("OpenMW");
|
||||||
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -60,7 +60,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -58,7 +58,7 @@ namespace MWClass
|
|||||||
{
|
{
|
||||||
// TODO implement reading
|
// 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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -85,7 +85,7 @@ namespace MWClass
|
|||||||
{
|
{
|
||||||
// TODO check for key
|
// TODO check for key
|
||||||
std::cout << "Locked container" << std::endl;
|
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);
|
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -100,7 +100,7 @@ namespace MWClass
|
|||||||
{
|
{
|
||||||
// Trap activation goes here
|
// Trap activation goes here
|
||||||
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
|
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 = "";
|
ptr.getCellRef().trap = "";
|
||||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ namespace MWClass
|
|||||||
// TODO check for key
|
// TODO check for key
|
||||||
// TODO report failure to player (message, sound?). Look up behaviour of original MW.
|
// TODO report failure to player (message, sound?). Look up behaviour of original MW.
|
||||||
std::cout << "Locked!" << std::endl;
|
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);
|
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ namespace MWClass
|
|||||||
{
|
{
|
||||||
// Trap activation
|
// Trap activation
|
||||||
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
|
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 = "";
|
ptr.getCellRef().trap = "";
|
||||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ namespace MWClass
|
|||||||
// TODO return action for rotating the door
|
// TODO return action for rotating the door
|
||||||
|
|
||||||
// This is a little pointless, but helps with testing
|
// 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);
|
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,
|
boost::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -59,7 +59,7 @@ namespace MWClass
|
|||||||
|
|
||||||
if (!ref->base->sound.empty())
|
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))
|
if (!(ref->base->data.flags & ESM::Light::Carry))
|
||||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -58,7 +58,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||||||
boost::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
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> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
|
@ -43,9 +43,6 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager,MWWorld::Environm
|
|||||||
// Centre dialog
|
// Centre dialog
|
||||||
center();
|
center();
|
||||||
|
|
||||||
//WindowManager *wm = environment.mWindowManager;
|
|
||||||
setText("NpcName", "Name of character");
|
|
||||||
|
|
||||||
//History view
|
//History view
|
||||||
getWidget(history, "History");
|
getWidget(history, "History");
|
||||||
history->setOverflowToTheLeft(true);
|
history->setOverflowToTheLeft(true);
|
||||||
@ -116,7 +113,8 @@ void DialogueWindow::onSelectTopic(MyGUI::ListBox* _sender, size_t _index)
|
|||||||
|
|
||||||
void DialogueWindow::startDialogue(std::string npcName)
|
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)
|
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
||||||
|
@ -182,7 +182,9 @@ void HUD::setPlayerPos(const float x, const float y)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MapWindow::MapWindow()
|
MapWindow::MapWindow()
|
||||||
: Layout("openmw_map_window_layout.xml"), mGlobal(false)
|
: Layout("openmw_map_window_layout.xml")
|
||||||
|
, mGlobal(false)
|
||||||
|
, mVisible(false)
|
||||||
{
|
{
|
||||||
setCoord(500,0,320,300);
|
setCoord(500,0,320,300);
|
||||||
setText("WorldButton", "World");
|
setText("WorldButton", "World");
|
||||||
@ -272,6 +274,17 @@ void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
|
|||||||
mButton->setCaption( mGlobal ? "Local" : "World" );
|
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)
|
void LocalMapBase::init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout)
|
||||||
{
|
{
|
||||||
mLocalMap = widget;
|
mLocalMap = widget;
|
||||||
|
@ -34,6 +34,7 @@ namespace MWGui
|
|||||||
class LocalMapBase
|
class LocalMapBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
LocalMapBase();
|
||||||
void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout);
|
void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout);
|
||||||
|
|
||||||
void setCellPrefix(const std::string& prefix);
|
void setCellPrefix(const std::string& prefix);
|
||||||
@ -85,6 +86,7 @@ namespace MWGui
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MapWindow();
|
MapWindow();
|
||||||
|
virtual ~MapWindow(){}
|
||||||
|
|
||||||
void setVisible(bool b);
|
void setVisible(bool b);
|
||||||
void setPlayerPos(const float x, const float y);
|
void setPlayerPos(const float x, const float y);
|
||||||
|
@ -180,71 +180,58 @@ void WindowManager::updateVisible()
|
|||||||
// Mouse is visible whenever we're not in game mode
|
// Mouse is visible whenever we're not in game mode
|
||||||
MyGUI::PointerManager::getInstance().setVisible(isGuiMode());
|
MyGUI::PointerManager::getInstance().setVisible(isGuiMode());
|
||||||
|
|
||||||
// If in game mode, don't show anything.
|
switch(mode) {
|
||||||
if(mode == GM_Game) //Use a switch/case structure
|
case GM_Game:
|
||||||
{
|
// If in game mode, don't show anything.
|
||||||
return;
|
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;
|
||||||
|
|
||||||
if(mode == GM_MainMenu)
|
// Show the windows we want
|
||||||
{
|
map -> setVisible( (eff & GW_Map) != 0 );
|
||||||
// Enable the main menu
|
stats -> setVisible( (eff & GW_Stats) != 0 );
|
||||||
menu->setVisible(true);
|
break;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mode == GM_Journal)
|
|
||||||
{
|
|
||||||
mJournal->setVisible(true);
|
|
||||||
mJournal->open();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
void WindowManager::setValue (const std::string& id, const MWMechanics::Stat<int>& value)
|
||||||
@ -371,7 +358,6 @@ void WindowManager::updateSkillArea()
|
|||||||
|
|
||||||
void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
|
void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
|
||||||
{
|
{
|
||||||
std::cout << "dialogue a la poubelle";
|
|
||||||
assert(dialog);
|
assert(dialog);
|
||||||
if (!dialog)
|
if (!dialog)
|
||||||
return;
|
return;
|
||||||
|
@ -126,6 +126,11 @@ namespace MWRender{
|
|||||||
void Animation::handleShapes(std::vector<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){
|
void Animation::handleShapes(std::vector<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){
|
||||||
shapeNumber = 0;
|
shapeNumber = 0;
|
||||||
|
|
||||||
|
if (allshapes == NULL || creaturemodel == NULL || skel == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Nif::NiTriShapeCopy>::iterator allshapesiter;
|
std::vector<Nif::NiTriShapeCopy>::iterator allshapesiter;
|
||||||
for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++)
|
for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++)
|
||||||
|
|
||||||
|
@ -27,11 +27,7 @@ bool Debugging::toggleRenderMode (int mode){
|
|||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case MWWorld::World::Render_CollisionDebug:
|
case MWWorld::World::Render_CollisionDebug:
|
||||||
|
return eng->toggleDebugRendering();
|
||||||
// TODO use a proper function instead of accessing the member variable
|
|
||||||
// directly.
|
|
||||||
eng->setDebugRenderingMode (!eng->isDebugCreated);
|
|
||||||
return eng->isDebugCreated;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -88,38 +88,76 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
|
|||||||
NifOgre::NIFLoader::load(mesh);
|
NifOgre::NIFLoader::load(mesh);
|
||||||
Ogre::Entity *ent = mRenderer.getScene()->createEntity(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)
|
if(!mIsStatic)
|
||||||
{
|
{
|
||||||
insert->attachObject(ent);
|
insert->attachObject(ent);
|
||||||
|
|
||||||
|
ent->setRenderingDistance(small ? 2500 : 0); /// \todo config value
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Ogre::StaticGeometry* sg = 0;
|
Ogre::StaticGeometry* sg = 0;
|
||||||
if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
|
|
||||||
|
if (small)
|
||||||
{
|
{
|
||||||
uniqueID = uniqueID +1;
|
if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end())
|
||||||
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
|
{
|
||||||
//Create the scenenode and put it in the map
|
uniqueID = uniqueID +1;
|
||||||
mStaticGeometry[ptr.getCell()] = sg;
|
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
|
||||||
|
mStaticGeometrySmall[ptr.getCell()] = sg;
|
||||||
|
|
||||||
// This specifies the size of a single batch region.
|
sg->setRenderingDistance(2500); /// \todo config value
|
||||||
// If it is set too high:
|
}
|
||||||
// - there will be problems choosing the correct lights
|
else
|
||||||
// - the culling will be more inefficient
|
sg = mStaticGeometrySmall[ptr.getCell()];
|
||||||
// If it is set too low:
|
|
||||||
// - there will be too many batches.
|
|
||||||
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
|
|
||||||
|
|
||||||
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
|
|
||||||
mBounds[ptr.getCell()].merge(ent->getBoundingBox());
|
|
||||||
}
|
}
|
||||||
else
|
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());
|
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
|
||||||
mBounds[ptr.getCell()].merge(insert->_getDerivedPosition());
|
|
||||||
|
|
||||||
mRenderer.getScene()->destroyEntity(ent);
|
mRenderer.getScene()->destroyEntity(ent);
|
||||||
}
|
}
|
||||||
@ -206,7 +244,14 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store)
|
|||||||
mRenderer.getScene()->destroyStaticGeometry (sg);
|
mRenderer.getScene()->destroyStaticGeometry (sg);
|
||||||
sg = 0;
|
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())
|
if(mBounds.find(store) != mBounds.end())
|
||||||
mBounds.erase(store);
|
mBounds.erase(store);
|
||||||
}
|
}
|
||||||
@ -218,6 +263,11 @@ void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
|
|||||||
Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
|
Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
|
||||||
sg->build();
|
sg->build();
|
||||||
}
|
}
|
||||||
|
if(mStaticGeometrySmall.find(&cell) != mStaticGeometrySmall.end())
|
||||||
|
{
|
||||||
|
Ogre::StaticGeometry* sg = mStaticGeometrySmall[&cell];
|
||||||
|
sg->build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
|
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
|
||||||
|
@ -14,6 +14,7 @@ class Objects{
|
|||||||
OEngine::Render::OgreRenderer &mRenderer;
|
OEngine::Render::OgreRenderer &mRenderer;
|
||||||
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
|
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
|
||||||
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometry;
|
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometry;
|
||||||
|
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometrySmall;
|
||||||
std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds;
|
std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds;
|
||||||
Ogre::SceneNode* mMwRoot;
|
Ogre::SceneNode* mMwRoot;
|
||||||
bool mIsStatic;
|
bool mIsStatic;
|
||||||
|
266
apps/openmw/mwrender/occlusionquery.cpp
Normal file
266
apps/openmw/mwrender/occlusionquery.cpp
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
#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),
|
||||||
|
mBBNode(0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (sunNode)
|
||||||
|
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;
|
||||||
|
if (mBBNode)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OcclusionQuery::setSunNode(Ogre::SceneNode* node)
|
||||||
|
{
|
||||||
|
mSunNode = node;
|
||||||
|
if (!mBBNode)
|
||||||
|
mBBNode = node->getParentSceneNode()->createChildSceneNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OcclusionQuery::getTestResult()
|
||||||
|
{
|
||||||
|
assert( !occlusionTestPending()
|
||||||
|
&& "Occlusion test still pending");
|
||||||
|
|
||||||
|
return mTestResult;
|
||||||
|
}
|
97
apps/openmw/mwrender/occlusionquery.hpp
Normal file
97
apps/openmw/mwrender/occlusionquery.hpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#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;};
|
||||||
|
|
||||||
|
void setSunNode(Ogre::SceneNode* node);
|
||||||
|
|
||||||
|
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(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mDebugging(engine)
|
||||||
{
|
{
|
||||||
mRendering.createScene("PlayerCam", 55, 5);
|
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)
|
// Set default mipmap level (NB some APIs ignore this)
|
||||||
TextureManager::getSingleton().setDefaultNumMipmaps(5);
|
TextureManager::getSingleton().setDefaultNumMipmaps(5);
|
||||||
@ -40,9 +46,6 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
|
|||||||
mMwRoot->pitch(Degree(-90));
|
mMwRoot->pitch(Degree(-90));
|
||||||
mObjects.setMwRoot(mMwRoot);
|
mObjects.setMwRoot(mMwRoot);
|
||||||
mActors.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");
|
Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player");
|
||||||
playerNode->pitch(Degree(90));
|
playerNode->pitch(Degree(90));
|
||||||
@ -53,6 +56,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
|
|||||||
//mSkyManager = 0;
|
//mSkyManager = 0;
|
||||||
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
|
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
|
||||||
|
|
||||||
|
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
|
||||||
|
|
||||||
|
mWater = 0;
|
||||||
|
|
||||||
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
|
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
|
||||||
mSun = 0;
|
mSun = 0;
|
||||||
|
|
||||||
@ -64,7 +71,9 @@ RenderingManager::~RenderingManager ()
|
|||||||
//TODO: destroy mSun?
|
//TODO: destroy mSun?
|
||||||
delete mPlayer;
|
delete mPlayer;
|
||||||
delete mSkyManager;
|
delete mSkyManager;
|
||||||
|
delete mTerrainManager;
|
||||||
delete mLocalMap;
|
delete mLocalMap;
|
||||||
|
delete mOcclusionQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWRender::SkyManager* RenderingManager::getSkyManager()
|
MWRender::SkyManager* RenderingManager::getSkyManager()
|
||||||
@ -88,14 +97,33 @@ OEngine::Render::Fader* RenderingManager::getFader()
|
|||||||
return mRendering.getFader();
|
return mRendering.getFader();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){
|
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store)
|
||||||
|
{
|
||||||
mObjects.removeCell(store);
|
mObjects.removeCell(store);
|
||||||
mActors.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)
|
void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
|
||||||
{
|
{
|
||||||
mObjects.buildStaticGeometry (*store);
|
mObjects.buildStaticGeometry (*store);
|
||||||
|
if (store->cell->isExterior())
|
||||||
|
mTerrainManager->cellAdded(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::addObject (const MWWorld::Ptr& ptr){
|
void RenderingManager::addObject (const MWWorld::Ptr& ptr){
|
||||||
@ -136,18 +164,45 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve
|
|||||||
void RenderingManager::update (float duration){
|
void RenderingManager::update (float duration){
|
||||||
|
|
||||||
mActors.update (duration);
|
mActors.update (duration);
|
||||||
|
|
||||||
|
mOcclusionQuery->update(duration);
|
||||||
|
|
||||||
mSkyManager->update(duration);
|
mSkyManager->update(duration);
|
||||||
|
|
||||||
|
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
|
||||||
|
|
||||||
mRendering.update(duration);
|
mRendering.update(duration);
|
||||||
|
|
||||||
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );
|
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 ()
|
void RenderingManager::skyEnable ()
|
||||||
{
|
{
|
||||||
if(mSkyManager)
|
if(mSkyManager)
|
||||||
mSkyManager->enable();
|
mSkyManager->enable();
|
||||||
|
|
||||||
|
mOcclusionQuery->setSunNode(mSkyManager->getSunNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::skyDisable ()
|
void RenderingManager::skyDisable ()
|
||||||
@ -236,17 +291,17 @@ void RenderingManager::setAmbientMode()
|
|||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
|
|
||||||
mRendering.getScene()->setAmbientLight(mAmbientColor);
|
setAmbientColour(mAmbientColor);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
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;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
|
|
||||||
mRendering.getScene()->setAmbientLight(ColourValue(1,1,1));
|
setAmbientColour(ColourValue(1,1,1));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,6 +341,11 @@ void RenderingManager::toggleLight()
|
|||||||
|
|
||||||
setAmbientMode();
|
setAmbientMode();
|
||||||
}
|
}
|
||||||
|
void RenderingManager::checkUnderwater(){
|
||||||
|
if(mWater){
|
||||||
|
mWater->checkUnderwater( mRendering.getCamera()->getRealPosition().y );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
|
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
|
||||||
int mode, int number)
|
int mode, int number)
|
||||||
@ -301,11 +361,13 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr)
|
|||||||
void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
|
void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
|
||||||
{
|
{
|
||||||
mSun->setDiffuseColour(colour);
|
mSun->setDiffuseColour(colour);
|
||||||
|
mTerrainManager->setDiffuse(colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
|
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
|
||||||
{
|
{
|
||||||
mRendering.getScene()->setAmbientLight(colour);
|
mRendering.getScene()->setAmbientLight(colour);
|
||||||
|
mTerrainManager->setAmbient(colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::sunEnable()
|
void RenderingManager::sunEnable()
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "sky.hpp"
|
#include "sky.hpp"
|
||||||
|
#include "terrain.hpp"
|
||||||
#include "debugging.hpp"
|
#include "debugging.hpp"
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
@ -24,7 +25,9 @@
|
|||||||
#include "objects.hpp"
|
#include "objects.hpp"
|
||||||
#include "actors.hpp"
|
#include "actors.hpp"
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
|
#include "water.hpp"
|
||||||
#include "localmap.hpp"
|
#include "localmap.hpp"
|
||||||
|
#include "occlusionquery.hpp"
|
||||||
|
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
{
|
{
|
||||||
@ -59,6 +62,8 @@ class RenderingManager: private RenderingInterface {
|
|||||||
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment);
|
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment);
|
||||||
virtual ~RenderingManager();
|
virtual ~RenderingManager();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as
|
virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as
|
||||||
/// MWWorld::Player has been rewritten to not need access
|
/// MWWorld::Player has been rewritten to not need access
|
||||||
/// to internal details of the rendering system anymore
|
/// to internal details of the rendering system anymore
|
||||||
@ -67,7 +72,7 @@ class RenderingManager: private RenderingInterface {
|
|||||||
|
|
||||||
void toggleLight();
|
void toggleLight();
|
||||||
bool toggleRenderMode(int mode);
|
bool toggleRenderMode(int mode);
|
||||||
|
|
||||||
OEngine::Render::Fader* getFader();
|
OEngine::Render::Fader* getFader();
|
||||||
|
|
||||||
void removeCell (MWWorld::Ptr::CellStore *store);
|
void removeCell (MWWorld::Ptr::CellStore *store);
|
||||||
@ -75,6 +80,9 @@ class RenderingManager: private RenderingInterface {
|
|||||||
/// \todo this function should be removed later. Instead the rendering subsystems should track
|
/// \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.
|
/// when rebatching is needed and update automatically at the end of each frame.
|
||||||
void cellAdded (MWWorld::Ptr::CellStore *store);
|
void cellAdded (MWWorld::Ptr::CellStore *store);
|
||||||
|
void waterAdded(MWWorld::Ptr::CellStore *store);
|
||||||
|
|
||||||
|
void removeWater();
|
||||||
|
|
||||||
void preCellChange (MWWorld::Ptr::CellStore* store);
|
void preCellChange (MWWorld::Ptr::CellStore* store);
|
||||||
///< this event is fired immediately before changing cell
|
///< this event is fired immediately before changing cell
|
||||||
@ -86,17 +94,24 @@ class RenderingManager: private RenderingInterface {
|
|||||||
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
|
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
|
||||||
void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation);
|
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).
|
/// \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 moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store);
|
||||||
|
|
||||||
void update (float duration);
|
void update (float duration);
|
||||||
|
|
||||||
void setAmbientColour(const Ogre::ColourValue& colour);
|
void setAmbientColour(const Ogre::ColourValue& colour);
|
||||||
void setSunColour(const Ogre::ColourValue& colour);
|
void setSunColour(const Ogre::ColourValue& colour);
|
||||||
void setSunDirection(const Ogre::Vector3& direction);
|
void setSunDirection(const Ogre::Vector3& direction);
|
||||||
void sunEnable();
|
void sunEnable();
|
||||||
void sunDisable();
|
void sunDisable();
|
||||||
|
|
||||||
|
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
|
||||||
|
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; };
|
||||||
|
|
||||||
void setGlare(bool glare);
|
void setGlare(bool glare);
|
||||||
void skyEnable ();
|
void skyEnable ();
|
||||||
void skyDisable ();
|
void skyDisable ();
|
||||||
@ -109,13 +124,13 @@ class RenderingManager: private RenderingInterface {
|
|||||||
|
|
||||||
void requestMap (MWWorld::Ptr::CellStore* cell);
|
void requestMap (MWWorld::Ptr::CellStore* cell);
|
||||||
///< request the local map for a cell
|
///< request the local map for a cell
|
||||||
|
|
||||||
/// configure fog according to cell
|
/// configure fog according to cell
|
||||||
void configureFog(ESMS::CellStore<MWWorld::RefData> &mCell);
|
void configureFog(ESMS::CellStore<MWWorld::RefData> &mCell);
|
||||||
|
|
||||||
/// configure fog manually
|
/// configure fog manually
|
||||||
void configureFog(const float density, const Ogre::ColourValue& colour);
|
void configureFog(const float density, const Ogre::ColourValue& colour);
|
||||||
|
|
||||||
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
|
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
|
||||||
int number = 1);
|
int number = 1);
|
||||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||||
@ -131,9 +146,15 @@ class RenderingManager: private RenderingInterface {
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
void setAmbientMode();
|
void setAmbientMode();
|
||||||
|
|
||||||
SkyManager* mSkyManager;
|
SkyManager* mSkyManager;
|
||||||
|
|
||||||
|
OcclusionQuery* mOcclusionQuery;
|
||||||
|
|
||||||
|
TerrainManager* mTerrainManager;
|
||||||
|
|
||||||
|
MWRender::Water *mWater;
|
||||||
|
|
||||||
OEngine::Render::OgreRenderer &mRendering;
|
OEngine::Render::OgreRenderer &mRendering;
|
||||||
|
|
||||||
MWRender::Objects mObjects;
|
MWRender::Objects mObjects;
|
||||||
@ -149,7 +170,6 @@ class RenderingManager: private RenderingInterface {
|
|||||||
/// that the OGRE coordinate system matches that used internally in
|
/// that the OGRE coordinate system matches that used internally in
|
||||||
/// Morrowind.
|
/// Morrowind.
|
||||||
Ogre::SceneNode *mMwRoot;
|
Ogre::SceneNode *mMwRoot;
|
||||||
Ogre::RaySceneQuery *mRaySceneQuery;
|
|
||||||
|
|
||||||
OEngine::Physic::PhysicEngine* mPhysicsEngine;
|
OEngine::Physic::PhysicEngine* mPhysicsEngine;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "../mwworld/environment.hpp"
|
#include "../mwworld/environment.hpp"
|
||||||
#include "../mwworld/world.hpp"
|
#include "../mwworld/world.hpp"
|
||||||
|
#include "occlusionquery.hpp"
|
||||||
|
|
||||||
using namespace MWRender;
|
using namespace MWRender;
|
||||||
using namespace Ogre;
|
using namespace Ogre;
|
||||||
@ -30,7 +31,7 @@ BillboardObject::BillboardObject()
|
|||||||
|
|
||||||
void BillboardObject::setVisible(const bool visible)
|
void BillboardObject::setVisible(const bool visible)
|
||||||
{
|
{
|
||||||
mNode->setVisible(visible);
|
mBBSet->setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BillboardObject::setSize(const float size)
|
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
|
/// \todo These billboards are not 100% correct, might want to revisit them later
|
||||||
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
|
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
|
||||||
mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize);
|
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->setBillboardType(BBT_PERPENDICULAR_COMMON);
|
||||||
mBBSet->setCommonDirection( -position.normalisedCopy() );
|
mBBSet->setCommonDirection( -position.normalisedCopy() );
|
||||||
mNode = rootNode->createChildSceneNode();
|
mNode = rootNode->createChildSceneNode();
|
||||||
@ -319,19 +320,22 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||||||
, mThunderTextureUnit(NULL)
|
, mThunderTextureUnit(NULL)
|
||||||
, mRemainingTransitionTime(0.0f)
|
, mRemainingTransitionTime(0.0f)
|
||||||
, mGlareFade(0.0f)
|
, mGlareFade(0.0f)
|
||||||
|
, mGlare(0.0f)
|
||||||
, mEnabled(true)
|
, mEnabled(true)
|
||||||
, mGlareEnabled(true)
|
|
||||||
, mSunEnabled(true)
|
, mSunEnabled(true)
|
||||||
, mMasserEnabled(true)
|
, mMasserEnabled(true)
|
||||||
, mSecundaEnabled(true)
|
, mSecundaEnabled(true)
|
||||||
|
, mCreated(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
mViewport = pCamera->getViewport();
|
mViewport = pCamera->getViewport();
|
||||||
mSceneMgr = pMwRoot->getCreator();
|
mSceneMgr = pMwRoot->getCreator();
|
||||||
mRootNode = pCamera->getParentSceneNode()->createChildSceneNode();
|
mRootNode = pCamera->getParentSceneNode()->createChildSceneNode();
|
||||||
mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates
|
mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates
|
||||||
mRootNode->setInheritOrientation(false);
|
mRootNode->setInheritOrientation(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkyManager::create()
|
||||||
|
{
|
||||||
/// \todo preload all the textures and meshes that are used for sky rendering
|
/// \todo preload all the textures and meshes that are used for sky rendering
|
||||||
|
|
||||||
// Create overlay used for thunderstorm
|
// Create overlay used for thunderstorm
|
||||||
@ -532,7 +536,7 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||||||
" uniform float4 emissive \n"
|
" uniform float4 emissive \n"
|
||||||
") \n"
|
") \n"
|
||||||
"{ \n"
|
"{ \n"
|
||||||
" uv += float2(1,0) * time * speed * 0.003; \n" // Scroll in x direction
|
" uv += float2(0,1) * time * speed * 0.003; \n" // Scroll in y direction
|
||||||
" float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n"
|
" float4 tex = lerp(tex2D(texture, uv), tex2D(secondTexture, uv), transitionFactor); \n"
|
||||||
" oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n"
|
" oColor = color * float4(emissive.xyz,1) * tex * float4(1,1,1,opacity); \n"
|
||||||
"}";
|
"}";
|
||||||
@ -561,6 +565,8 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||||||
mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
|
mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
|
||||||
|
|
||||||
mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("");
|
mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("");
|
||||||
|
|
||||||
|
mCreated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkyManager::~SkyManager()
|
SkyManager::~SkyManager()
|
||||||
@ -573,11 +579,13 @@ SkyManager::~SkyManager()
|
|||||||
|
|
||||||
int SkyManager::getMasserPhase() const
|
int SkyManager::getMasserPhase() const
|
||||||
{
|
{
|
||||||
|
if (!mCreated) return 0;
|
||||||
return mMasser->getPhaseInt();
|
return mMasser->getPhaseInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
int SkyManager::getSecundaPhase() const
|
int SkyManager::getSecundaPhase() const
|
||||||
{
|
{
|
||||||
|
if (!mCreated) return 0;
|
||||||
return mSecunda->getPhaseInt();
|
return mSecunda->getPhaseInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,10 +600,23 @@ void SkyManager::update(float duration)
|
|||||||
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
|
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
|
||||||
mSecunda->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)
|
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();
|
Vector3 sun = mSunGlare->getPosition();
|
||||||
sun = Vector3(sun.x, sun.z, -sun.y);
|
sun = Vector3(sun.x, sun.z, -sun.y);
|
||||||
Vector3 cam = mViewport->getCamera()->getRealDirection();
|
Vector3 cam = mViewport->getCamera()->getRealDirection();
|
||||||
@ -603,21 +624,10 @@ void SkyManager::update(float duration)
|
|||||||
float val = 1- (angle.valueDegrees() / 180.f);
|
float val = 1- (angle.valueDegrees() / 180.f);
|
||||||
val = (val*val*val*val)*2;
|
val = (val*val*val*val)*2;
|
||||||
|
|
||||||
if (mGlareEnabled)
|
mSunGlare->setSize(val * mGlareFade);
|
||||||
{
|
|
||||||
mGlareFade += duration*3;
|
|
||||||
if (mGlareFade > 1) mGlareFade = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mGlareFade -= duration*3;
|
|
||||||
if (mGlareFade < 0.3) mGlareFade = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSunGlare->setSize(val * (mGlareFade));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mSunGlare->setVisible(mGlareFade>0 && mSunEnabled);
|
mSunGlare->setVisible(mSunEnabled);
|
||||||
mSun->setVisible(mSunEnabled);
|
mSun->setVisible(mSunEnabled);
|
||||||
mMasser->setVisible(mMasserEnabled);
|
mMasser->setVisible(mMasserEnabled);
|
||||||
mSecunda->setVisible(mSecundaEnabled);
|
mSecunda->setVisible(mSecundaEnabled);
|
||||||
@ -628,6 +638,9 @@ void SkyManager::update(float duration)
|
|||||||
|
|
||||||
void SkyManager::enable()
|
void SkyManager::enable()
|
||||||
{
|
{
|
||||||
|
if (!mCreated)
|
||||||
|
create();
|
||||||
|
|
||||||
mRootNode->setVisible(true);
|
mRootNode->setVisible(true);
|
||||||
mEnabled = true;
|
mEnabled = true;
|
||||||
}
|
}
|
||||||
@ -651,6 +664,7 @@ void SkyManager::setCloudsOpacity(float opacity)
|
|||||||
|
|
||||||
void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
||||||
{
|
{
|
||||||
|
if (!mCreated) return;
|
||||||
if (mClouds != weather.mCloudTexture)
|
if (mClouds != weather.mCloudTexture)
|
||||||
{
|
{
|
||||||
mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("textures\\"+weather.mCloudTexture);
|
mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("textures\\"+weather.mCloudTexture);
|
||||||
@ -719,15 +733,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
|||||||
else
|
else
|
||||||
strength = 1.f;
|
strength = 1.f;
|
||||||
|
|
||||||
mSunGlare->setVisibility(weather.mGlareView * strength);
|
mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
|
||||||
mSun->setVisibility(strength);
|
mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0);
|
||||||
|
|
||||||
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
|
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkyManager::setGlare(bool glare)
|
void SkyManager::setGlare(const float glare)
|
||||||
{
|
{
|
||||||
mGlareEnabled = glare;
|
mGlare = glare;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 SkyManager::getRealSunPos()
|
Vector3 SkyManager::getRealSunPos()
|
||||||
@ -747,17 +761,20 @@ void SkyManager::sunDisable()
|
|||||||
|
|
||||||
void SkyManager::setSunDirection(const Vector3& direction)
|
void SkyManager::setSunDirection(const Vector3& direction)
|
||||||
{
|
{
|
||||||
|
if (!mCreated) return;
|
||||||
mSun->setPosition(direction);
|
mSun->setPosition(direction);
|
||||||
mSunGlare->setPosition(direction);
|
mSunGlare->setPosition(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkyManager::setMasserDirection(const Vector3& direction)
|
void SkyManager::setMasserDirection(const Vector3& direction)
|
||||||
{
|
{
|
||||||
|
if (!mCreated) return;
|
||||||
mMasser->setPosition(direction);
|
mMasser->setPosition(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkyManager::setSecundaDirection(const Vector3& direction)
|
void SkyManager::setSecundaDirection(const Vector3& direction)
|
||||||
{
|
{
|
||||||
|
if (!mCreated) return;
|
||||||
mSecunda->setPosition(direction);
|
mSecunda->setPosition(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,6 +800,7 @@ void SkyManager::secundaDisable()
|
|||||||
|
|
||||||
void SkyManager::setThunder(const float factor)
|
void SkyManager::setThunder(const float factor)
|
||||||
{
|
{
|
||||||
|
if (!mCreated) return;
|
||||||
if (factor > 0.f)
|
if (factor > 0.f)
|
||||||
{
|
{
|
||||||
mThunderOverlay->show();
|
mThunderOverlay->show();
|
||||||
@ -812,3 +830,9 @@ void SkyManager::setDate(int day, int month)
|
|||||||
mDay = day;
|
mDay = day;
|
||||||
mMonth = month;
|
mMonth = month;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ogre::SceneNode* SkyManager::getSunNode()
|
||||||
|
{
|
||||||
|
if (!mCreated) return 0;
|
||||||
|
return mSun->getNode();
|
||||||
|
}
|
||||||
|
@ -109,61 +109,68 @@ namespace MWRender
|
|||||||
public:
|
public:
|
||||||
SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env);
|
SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env);
|
||||||
~SkyManager();
|
~SkyManager();
|
||||||
|
|
||||||
void update(float duration);
|
void update(float duration);
|
||||||
|
|
||||||
|
void create();
|
||||||
|
///< no need to call this, automatically done on first enable()
|
||||||
|
|
||||||
void enable();
|
void enable();
|
||||||
|
|
||||||
void disable();
|
void disable();
|
||||||
|
|
||||||
void setHour (double hour);
|
void setHour (double hour);
|
||||||
///< will be called even when sky is disabled.
|
///< will be called even when sky is disabled.
|
||||||
|
|
||||||
void setDate (int day, int month);
|
void setDate (int day, int month);
|
||||||
///< will be called even when sky is disabled.
|
///< will be called even when sky is disabled.
|
||||||
|
|
||||||
int getMasserPhase() const;
|
int getMasserPhase() const;
|
||||||
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
|
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
|
||||||
/// 3 waxing or waning gibbous, 4 full moon
|
/// 3 waxing or waning gibbous, 4 full moon
|
||||||
|
|
||||||
int getSecundaPhase() const;
|
int getSecundaPhase() const;
|
||||||
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
|
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
|
||||||
/// 3 waxing or waning gibbous, 4 full moon
|
/// 3 waxing or waning gibbous, 4 full moon
|
||||||
|
|
||||||
void setMoonColour (bool red);
|
void setMoonColour (bool red);
|
||||||
///< change Secunda colour to red
|
///< change Secunda colour to red
|
||||||
|
|
||||||
void setCloudsOpacity(float opacity);
|
void setCloudsOpacity(float opacity);
|
||||||
///< change opacity of the clouds
|
///< change opacity of the clouds
|
||||||
|
|
||||||
void setWeather(const MWWorld::WeatherResult& weather);
|
void setWeather(const MWWorld::WeatherResult& weather);
|
||||||
|
|
||||||
|
Ogre::SceneNode* getSunNode();
|
||||||
|
|
||||||
void sunEnable();
|
void sunEnable();
|
||||||
|
|
||||||
void sunDisable();
|
void sunDisable();
|
||||||
|
|
||||||
void setSunDirection(const Ogre::Vector3& direction);
|
void setSunDirection(const Ogre::Vector3& direction);
|
||||||
|
|
||||||
void setMasserDirection(const Ogre::Vector3& direction);
|
void setMasserDirection(const Ogre::Vector3& direction);
|
||||||
|
|
||||||
void setSecundaDirection(const Ogre::Vector3& direction);
|
void setSecundaDirection(const Ogre::Vector3& direction);
|
||||||
|
|
||||||
void setMasserFade(const float fade);
|
void setMasserFade(const float fade);
|
||||||
|
|
||||||
void setSecundaFade(const float fade);
|
void setSecundaFade(const float fade);
|
||||||
|
|
||||||
void masserEnable();
|
void masserEnable();
|
||||||
void masserDisable();
|
void masserDisable();
|
||||||
|
|
||||||
void secundaEnable();
|
void secundaEnable();
|
||||||
void secundaDisable();
|
void secundaDisable();
|
||||||
|
|
||||||
void setThunder(const float factor);
|
void setThunder(const float factor);
|
||||||
|
|
||||||
void setGlare(bool glare);
|
void setGlare(const float glare);
|
||||||
Ogre::Vector3 getRealSunPos();
|
Ogre::Vector3 getRealSunPos();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool mCreated;
|
||||||
|
|
||||||
MWWorld::Environment* mEnvironment;
|
MWWorld::Environment* mEnvironment;
|
||||||
float mHour;
|
float mHour;
|
||||||
int mDay;
|
int mDay;
|
||||||
@ -203,12 +210,12 @@ namespace MWRender
|
|||||||
|
|
||||||
float mRemainingTransitionTime;
|
float mRemainingTransitionTime;
|
||||||
|
|
||||||
float mGlareFade;
|
float mGlare; // target
|
||||||
|
float mGlareFade; // actual
|
||||||
|
|
||||||
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
|
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
|
||||||
|
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
bool mGlareEnabled;
|
|
||||||
bool mSunEnabled;
|
bool mSunEnabled;
|
||||||
bool mMasserEnabled;
|
bool mMasserEnabled;
|
||||||
bool mSecundaEnabled;
|
bool mSecundaEnabled;
|
||||||
|
510
apps/openmw/mwrender/terrain.cpp
Normal file
510
apps/openmw/mwrender/terrain.cpp
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
117
apps/openmw/mwrender/terrain.hpp
Normal file
117
apps/openmw/mwrender/terrain.hpp
Normal file
@ -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
|
1741
apps/openmw/mwrender/terrainmaterial.cpp
Normal file
1741
apps/openmw/mwrender/terrainmaterial.cpp
Normal file
File diff suppressed because it is too large
Load Diff
266
apps/openmw/mwrender/terrainmaterial.hpp
Normal file
266
apps/openmw/mwrender/terrainmaterial.hpp
Normal file
@ -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
|
95
apps/openmw/mwrender/water.cpp
Normal file
95
apps/openmw/mwrender/water.cpp
Normal file
@ -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
|
40
apps/openmw/mwrender/water.hpp
Normal file
40
apps/openmw/mwrender/water.hpp
Normal file
@ -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 opcodeCellChanged = 0x2000000;
|
||||||
const int opcodeCOC = 0x2000026;
|
const int opcodeCOC = 0x2000026;
|
||||||
const int opcodeCOE = 0x200008e;
|
const int opcodeCOE = 0x200008e;
|
||||||
const int opcodeGetInterior = 0x2000131;
|
const int opcodeGetInterior = 0x2000131;
|
||||||
const int opcodeGetPCCell = 0x2000136;
|
const int opcodeGetPCCell = 0x2000136;
|
||||||
|
const int opcodeGetWaterLevel = 0x2000141;
|
||||||
|
const int opcodeSetWaterLevel = 0x2000142;
|
||||||
|
const int opcodeModWaterLevel = 0x2000143;
|
||||||
|
|
||||||
void registerExtensions (Compiler::Extensions& extensions)
|
void registerExtensions (Compiler::Extensions& extensions)
|
||||||
{
|
{
|
||||||
@ -146,8 +205,11 @@ namespace MWScript
|
|||||||
extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
|
extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
|
||||||
extensions.registerInstruction ("coe", "ll", opcodeCOE);
|
extensions.registerInstruction ("coe", "ll", opcodeCOE);
|
||||||
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
|
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
|
||||||
|
extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel);
|
||||||
|
extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel);
|
||||||
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
|
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
|
||||||
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
|
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
|
||||||
|
extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||||
@ -157,6 +219,9 @@ namespace MWScript
|
|||||||
interpreter.installSegment5 (opcodeCOE, new OpCOE);
|
interpreter.installSegment5 (opcodeCOE, new OpCOE);
|
||||||
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
|
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
|
||||||
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
|
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
|
||||||
|
interpreter.installSegment5 (opcodeGetWaterLevel, new OpGetWaterLevel);
|
||||||
|
interpreter.installSegment5 (opcodeSetWaterLevel, new OpSetWaterLevel);
|
||||||
|
interpreter.installSegment5 (opcodeModWaterLevel, new OpModWaterLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,5 +123,8 @@ op 0x200013d: FadeOut
|
|||||||
op 0x200013e: FadeTo
|
op 0x200013e: FadeTo
|
||||||
op 0x200013f: GetCurrentWeather
|
op 0x200013f: GetCurrentWeather
|
||||||
op 0x2000140: ChangeWeather
|
op 0x2000140: ChangeWeather
|
||||||
op 0x2000141: OpPCJoinFaction
|
op 0x2000141: GetWaterLevel
|
||||||
opcodes 0x2000142-0x3ffffff unused
|
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 opcodeXBox = 0x200000c;
|
||||||
const int opcodeOnActivate = 0x200000d;
|
const int opcodeOnActivate = 0x200000d;
|
||||||
const int opcodeActivate = 0x2000075;
|
const int opcodeActivate = 0x2000075;
|
||||||
@ -187,6 +200,7 @@ namespace MWScript
|
|||||||
const int opcodeFadeIn = 0x200013c;
|
const int opcodeFadeIn = 0x200013c;
|
||||||
const int opcodeFadeOut = 0x200013d;
|
const int opcodeFadeOut = 0x200013d;
|
||||||
const int opcodeFadeTo = 0x200013e;
|
const int opcodeFadeTo = 0x200013e;
|
||||||
|
const int opcodeToggleWater = 0x2000144;
|
||||||
|
|
||||||
void registerExtensions (Compiler::Extensions& extensions)
|
void registerExtensions (Compiler::Extensions& extensions)
|
||||||
{
|
{
|
||||||
@ -204,6 +218,8 @@ namespace MWScript
|
|||||||
extensions.registerInstruction ("fadein", "f", opcodeFadeIn);
|
extensions.registerInstruction ("fadein", "f", opcodeFadeIn);
|
||||||
extensions.registerInstruction ("fadeout", "f", opcodeFadeOut);
|
extensions.registerInstruction ("fadeout", "f", opcodeFadeOut);
|
||||||
extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo);
|
extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo);
|
||||||
|
extensions.registerInstruction ("togglewater", "", opcodeToggleWater);
|
||||||
|
extensions.registerInstruction ("twa", "", opcodeToggleWater);
|
||||||
}
|
}
|
||||||
|
|
||||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||||
@ -220,6 +236,7 @@ namespace MWScript
|
|||||||
interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn);
|
interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn);
|
||||||
interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut);
|
interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut);
|
||||||
interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo);
|
interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo);
|
||||||
|
interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ namespace MWScript
|
|||||||
std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
|
std::string sound = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
runtime.pop();
|
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;
|
Interpreter::Type_Float pitch = runtime[0].mFloat;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop);
|
context.getSoundManager().playSound3D (ptr, sound, volume, pitch, mLoop ? MWSound::Play_Loop : 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -25,14 +25,20 @@ static void throwALCerror(ALCdevice *device)
|
|||||||
{
|
{
|
||||||
ALCenum err = alcGetError(device);
|
ALCenum err = alcGetError(device);
|
||||||
if(err != ALC_NO_ERROR)
|
if(err != ALC_NO_ERROR)
|
||||||
fail(alcGetString(device, err));
|
{
|
||||||
|
const ALCchar *errstring = alcGetString(device, err);
|
||||||
|
fail(errstring ? errstring : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void throwALerror()
|
static void throwALerror()
|
||||||
{
|
{
|
||||||
ALenum err = alGetError();
|
ALenum err = alGetError();
|
||||||
if(err != AL_NO_ERROR)
|
if(err != AL_NO_ERROR)
|
||||||
fail(alGetString(err));
|
{
|
||||||
|
const ALchar *errstring = alGetString(err);
|
||||||
|
fail(errstring ? errstring : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -89,7 +95,7 @@ public:
|
|||||||
|
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
virtual bool isPlaying();
|
virtual bool isPlaying();
|
||||||
virtual void update(const float *pos);
|
virtual void update();
|
||||||
|
|
||||||
void play();
|
void play();
|
||||||
bool process();
|
bool process();
|
||||||
@ -186,7 +192,6 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode
|
|||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
mOutput.mFreeSources.push_back(mSource);
|
|
||||||
alDeleteBuffers(sNumBuffers, mBuffers);
|
alDeleteBuffers(sNumBuffers, mBuffers);
|
||||||
alGetError();
|
alGetError();
|
||||||
throw;
|
throw;
|
||||||
@ -254,9 +259,19 @@ bool OpenAL_SoundStream::isPlaying()
|
|||||||
return !mIsFinished;
|
return !mIsFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenAL_SoundStream::update(const float *pos)
|
void OpenAL_SoundStream::update()
|
||||||
{
|
{
|
||||||
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
|
ALfloat gain = mVolume*mBaseVolume;
|
||||||
|
ALfloat pitch = mPitch;
|
||||||
|
if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
|
||||||
|
{
|
||||||
|
gain *= 0.9f;
|
||||||
|
pitch *= 0.7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourcef(mSource, AL_GAIN, gain);
|
||||||
|
alSourcef(mSource, AL_PITCH, pitch);
|
||||||
|
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]);
|
||||||
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||||
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||||
throwALerror();
|
throwALerror();
|
||||||
@ -313,15 +328,17 @@ bool OpenAL_SoundStream::process()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// A regular OpenAL sound
|
// A regular 2D OpenAL sound
|
||||||
//
|
//
|
||||||
class OpenAL_Sound : public Sound
|
class OpenAL_Sound : public Sound
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
OpenAL_Output &mOutput;
|
OpenAL_Output &mOutput;
|
||||||
|
|
||||||
ALuint mSource;
|
ALuint mSource;
|
||||||
ALuint mBuffer;
|
ALuint mBuffer;
|
||||||
|
|
||||||
|
private:
|
||||||
OpenAL_Sound(const OpenAL_Sound &rhs);
|
OpenAL_Sound(const OpenAL_Sound &rhs);
|
||||||
OpenAL_Sound& operator=(const OpenAL_Sound &rhs);
|
OpenAL_Sound& operator=(const OpenAL_Sound &rhs);
|
||||||
|
|
||||||
@ -331,7 +348,23 @@ public:
|
|||||||
|
|
||||||
virtual void stop();
|
virtual void stop();
|
||||||
virtual bool isPlaying();
|
virtual bool isPlaying();
|
||||||
virtual void update(const float *pos);
|
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)
|
OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf)
|
||||||
@ -363,9 +396,39 @@ bool OpenAL_Sound::isPlaying()
|
|||||||
return state==AL_PLAYING;
|
return state==AL_PLAYING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenAL_Sound::update(const float *pos)
|
void OpenAL_Sound::update()
|
||||||
{
|
{
|
||||||
alSource3f(mSource, AL_POSITION, pos[0], pos[2], -pos[1]);
|
ALfloat gain = mVolume*mBaseVolume;
|
||||||
|
ALfloat pitch = mPitch;
|
||||||
|
if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
|
||||||
|
{
|
||||||
|
gain *= 0.9f;
|
||||||
|
pitch *= 0.7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourcef(mSource, AL_GAIN, gain);
|
||||||
|
alSourcef(mSource, AL_PITCH, pitch);
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
ALfloat gain = mVolume*mBaseVolume;
|
||||||
|
ALfloat pitch = mPitch;
|
||||||
|
if(mPos.squaredDistance(mOutput.mPos) > mMaxDistance*mMaxDistance)
|
||||||
|
gain = 0.0f;
|
||||||
|
else if(!(mFlags&Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater)
|
||||||
|
{
|
||||||
|
gain *= 0.9f;
|
||||||
|
pitch *= 0.7f;
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourcef(mSource, AL_GAIN, gain);
|
||||||
|
alSourcef(mSource, AL_PITCH, pitch);
|
||||||
|
alSource3f(mSource, AL_POSITION, mPos[0], mPos[2], -mPos[1]);
|
||||||
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
alSource3f(mSource, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
||||||
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
alSource3f(mSource, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||||
throwALerror();
|
throwALerror();
|
||||||
@ -394,8 +457,7 @@ std::vector<std::string> OpenAL_Output::enumerate()
|
|||||||
|
|
||||||
void OpenAL_Output::init(const std::string &devname)
|
void OpenAL_Output::init(const std::string &devname)
|
||||||
{
|
{
|
||||||
if(mDevice || mContext)
|
deinit();
|
||||||
fail("Device already open");
|
|
||||||
|
|
||||||
mDevice = alcOpenDevice(devname.c_str());
|
mDevice = alcOpenDevice(devname.c_str());
|
||||||
if(!mDevice)
|
if(!mDevice)
|
||||||
@ -412,29 +474,39 @@ void OpenAL_Output::init(const std::string &devname)
|
|||||||
|
|
||||||
mContext = alcCreateContext(mDevice, NULL);
|
mContext = alcCreateContext(mDevice, NULL);
|
||||||
if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)
|
if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)
|
||||||
|
{
|
||||||
|
if(mContext)
|
||||||
|
alcDestroyContext(mContext);
|
||||||
|
mContext = 0;
|
||||||
fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice)));
|
fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice)));
|
||||||
|
}
|
||||||
|
|
||||||
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
|
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
|
||||||
throwALerror();
|
throwALerror();
|
||||||
|
|
||||||
ALCint maxmono, maxstereo;
|
ALCint maxmono=0, maxstereo=0;
|
||||||
alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);
|
alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);
|
||||||
alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);
|
alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);
|
||||||
throwALCerror(mDevice);
|
throwALCerror(mDevice);
|
||||||
|
|
||||||
mFreeSources.resize(std::min(maxmono+maxstereo, 256));
|
try
|
||||||
for(size_t i = 0;i < mFreeSources.size();i++)
|
|
||||||
{
|
{
|
||||||
ALuint src;
|
ALCuint maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256);
|
||||||
alGenSources(1, &src);
|
if (maxtotal == 0) // workaround for broken implementations
|
||||||
if(alGetError() != AL_NO_ERROR)
|
maxtotal = 256;
|
||||||
|
for(size_t i = 0;i < maxtotal;i++)
|
||||||
{
|
{
|
||||||
mFreeSources.resize(i);
|
ALuint src = 0;
|
||||||
break;
|
alGenSources(1, &src);
|
||||||
|
throwALerror();
|
||||||
|
mFreeSources.push_back(src);
|
||||||
}
|
}
|
||||||
mFreeSources[i] = src;
|
|
||||||
}
|
}
|
||||||
if(mFreeSources.size() == 0)
|
catch(std::exception &e)
|
||||||
|
{
|
||||||
|
std::cout <<"Error: "<<e.what()<<", trying to continue"<< std::endl;
|
||||||
|
}
|
||||||
|
if(mFreeSources.empty())
|
||||||
fail("Could not allocate any sources");
|
fail("Could not allocate any sources");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,10 +514,10 @@ void OpenAL_Output::deinit()
|
|||||||
{
|
{
|
||||||
mStreamThread->removeAll();
|
mStreamThread->removeAll();
|
||||||
|
|
||||||
if(!mFreeSources.empty())
|
while(!mFreeSources.empty())
|
||||||
{
|
{
|
||||||
alDeleteSources(mFreeSources.size(), mFreeSources.data());
|
alDeleteSources(1, &mFreeSources.front());
|
||||||
mFreeSources.clear();
|
mFreeSources.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
mBufferRefs.clear();
|
mBufferRefs.clear();
|
||||||
@ -561,17 +633,15 @@ void OpenAL_Output::bufferFinished(ALuint buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, bool loop)
|
SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags)
|
||||||
{
|
{
|
||||||
throwALerror();
|
boost::shared_ptr<OpenAL_Sound> sound;
|
||||||
|
|
||||||
std::auto_ptr<OpenAL_Sound> sound;
|
|
||||||
ALuint src=0, buf=0;
|
ALuint src=0, buf=0;
|
||||||
|
|
||||||
if(mFreeSources.empty())
|
if(mFreeSources.empty())
|
||||||
fail("No free sources");
|
fail("No free sources");
|
||||||
src = mFreeSources.back();
|
src = mFreeSources.front();
|
||||||
mFreeSources.pop_back();
|
mFreeSources.pop_front();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -595,37 +665,40 @@ Sound* OpenAL_Output::playSound(const std::string &fname, float volume, float pi
|
|||||||
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
|
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
|
||||||
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
|
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
|
||||||
|
|
||||||
|
if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater)
|
||||||
|
{
|
||||||
|
volume *= 0.9f;
|
||||||
|
pitch *= 0.7f;
|
||||||
|
}
|
||||||
alSourcef(src, AL_GAIN, volume);
|
alSourcef(src, AL_GAIN, volume);
|
||||||
alSourcef(src, AL_PITCH, pitch);
|
alSourcef(src, AL_PITCH, pitch);
|
||||||
|
|
||||||
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
|
alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||||
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
|
alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE);
|
||||||
throwALerror();
|
throwALerror();
|
||||||
|
|
||||||
alSourcei(src, AL_BUFFER, buf);
|
alSourcei(src, AL_BUFFER, buf);
|
||||||
alSourcePlay(src);
|
alSourcePlay(src);
|
||||||
throwALerror();
|
throwALerror();
|
||||||
|
|
||||||
return sound.release();
|
return sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch,
|
||||||
float min, float max, bool loop)
|
float min, float max, int flags)
|
||||||
{
|
{
|
||||||
throwALerror();
|
boost::shared_ptr<OpenAL_Sound> sound;
|
||||||
|
|
||||||
std::auto_ptr<OpenAL_Sound> sound;
|
|
||||||
ALuint src=0, buf=0;
|
ALuint src=0, buf=0;
|
||||||
|
|
||||||
if(mFreeSources.empty())
|
if(mFreeSources.empty())
|
||||||
fail("No free sources");
|
fail("No free sources");
|
||||||
src = mFreeSources.back();
|
src = mFreeSources.front();
|
||||||
mFreeSources.pop_back();
|
mFreeSources.pop_front();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
buf = getBuffer(fname);
|
buf = getBuffer(fname);
|
||||||
sound.reset(new OpenAL_Sound(*this, src, buf));
|
sound.reset(new OpenAL_Sound3D(*this, src, buf));
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
@ -636,7 +709,7 @@ Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, fl
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
|
alSource3f(src, AL_POSITION, pos.x, pos.z, -pos.y);
|
||||||
alSource3f(src, AL_DIRECTION, 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);
|
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
@ -644,35 +717,41 @@ Sound* OpenAL_Output::playSound3D(const std::string &fname, const float *pos, fl
|
|||||||
alSourcef(src, AL_MAX_DISTANCE, max);
|
alSourcef(src, AL_MAX_DISTANCE, max);
|
||||||
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
|
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
|
||||||
|
|
||||||
alSourcef(src, AL_GAIN, volume);
|
if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater)
|
||||||
|
{
|
||||||
|
volume *= 0.9f;
|
||||||
|
pitch *= 0.7f;
|
||||||
|
}
|
||||||
|
alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ?
|
||||||
|
0.0f : volume);
|
||||||
alSourcef(src, AL_PITCH, pitch);
|
alSourcef(src, AL_PITCH, pitch);
|
||||||
|
|
||||||
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
|
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||||
alSourcei(src, AL_LOOPING, (loop?AL_TRUE:AL_FALSE));
|
alSourcei(src, AL_LOOPING, (flags&Play_Loop) ? AL_TRUE : AL_FALSE);
|
||||||
throwALerror();
|
throwALerror();
|
||||||
|
|
||||||
alSourcei(src, AL_BUFFER, buf);
|
alSourcei(src, AL_BUFFER, buf);
|
||||||
alSourcePlay(src);
|
alSourcePlay(src);
|
||||||
throwALerror();
|
throwALerror();
|
||||||
|
|
||||||
return sound.release();
|
return sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Sound* OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch)
|
SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags)
|
||||||
{
|
{
|
||||||
throwALerror();
|
boost::shared_ptr<OpenAL_SoundStream> sound;
|
||||||
|
|
||||||
std::auto_ptr<OpenAL_SoundStream> sound;
|
|
||||||
ALuint src;
|
ALuint src;
|
||||||
|
|
||||||
if(mFreeSources.empty())
|
if(mFreeSources.empty())
|
||||||
fail("No free sources");
|
fail("No free sources");
|
||||||
src = mFreeSources.back();
|
src = mFreeSources.front();
|
||||||
mFreeSources.pop_back();
|
mFreeSources.pop_front();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if((flags&Play_Loop))
|
||||||
|
std::cout <<"Warning: cannot loop stream "<<fname<< std::endl;
|
||||||
DecoderPtr decoder = mManager.getDecoder();
|
DecoderPtr decoder = mManager.getDecoder();
|
||||||
decoder->open(fname);
|
decoder->open(fname);
|
||||||
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
|
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
|
||||||
@ -691,6 +770,11 @@ Sound* OpenAL_Output::streamSound(const std::string &fname, float volume, float
|
|||||||
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
|
alSourcef(src, AL_MAX_DISTANCE, 1000.0f);
|
||||||
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
|
alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
|
||||||
|
|
||||||
|
if(!(flags&Play_NoEnv) && mLastEnvironment == Env_Underwater)
|
||||||
|
{
|
||||||
|
volume *= 0.9f;
|
||||||
|
pitch *= 0.7f;
|
||||||
|
}
|
||||||
alSourcef(src, AL_GAIN, volume);
|
alSourcef(src, AL_GAIN, volume);
|
||||||
alSourcef(src, AL_PITCH, pitch);
|
alSourcef(src, AL_PITCH, pitch);
|
||||||
|
|
||||||
@ -699,70 +783,31 @@ Sound* OpenAL_Output::streamSound(const std::string &fname, float volume, float
|
|||||||
throwALerror();
|
throwALerror();
|
||||||
|
|
||||||
sound->play();
|
sound->play();
|
||||||
return sound.release();
|
return sound;
|
||||||
}
|
|
||||||
|
|
||||||
Sound* OpenAL_Output::streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
|
||||||
float min, float max)
|
|
||||||
{
|
|
||||||
throwALerror();
|
|
||||||
|
|
||||||
std::auto_ptr<OpenAL_SoundStream> sound;
|
|
||||||
ALuint src;
|
|
||||||
|
|
||||||
if(mFreeSources.empty())
|
|
||||||
fail("No free sources");
|
|
||||||
src = mFreeSources.back();
|
|
||||||
mFreeSources.pop_back();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DecoderPtr decoder = mManager.getDecoder();
|
|
||||||
decoder->open(fname);
|
|
||||||
sound.reset(new OpenAL_SoundStream(*this, src, decoder));
|
|
||||||
}
|
|
||||||
catch(std::exception &e)
|
|
||||||
{
|
|
||||||
mFreeSources.push_back(src);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
alSource3f(src, AL_POSITION, pos[0], pos[2], -pos[1]);
|
|
||||||
alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
|
|
||||||
alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
|
||||||
|
|
||||||
alSourcef(src, AL_REFERENCE_DISTANCE, min);
|
|
||||||
alSourcef(src, AL_MAX_DISTANCE, max);
|
|
||||||
alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f);
|
|
||||||
|
|
||||||
alSourcef(src, AL_GAIN, volume);
|
|
||||||
alSourcef(src, AL_PITCH, pitch);
|
|
||||||
|
|
||||||
alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
|
|
||||||
alSourcei(src, AL_LOOPING, AL_FALSE);
|
|
||||||
throwALerror();
|
|
||||||
|
|
||||||
sound->play();
|
|
||||||
return sound.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OpenAL_Output::updateListener(const float *pos, const float *atdir, const float *updir)
|
void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env)
|
||||||
{
|
{
|
||||||
float orient[6] = {
|
mPos = pos;
|
||||||
atdir[0], atdir[2], -atdir[1],
|
mLastEnvironment = env;
|
||||||
updir[0], updir[2], -updir[1]
|
|
||||||
};
|
|
||||||
|
|
||||||
alListener3f(AL_POSITION, pos[0], pos[2], -pos[1]);
|
if(mContext)
|
||||||
alListenerfv(AL_ORIENTATION, orient);
|
{
|
||||||
throwALerror();
|
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)
|
OpenAL_Output::OpenAL_Output(SoundManager &mgr)
|
||||||
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0),
|
: Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0),
|
||||||
mStreamThread(new StreamThread)
|
mLastEnvironment(Env_Normal), mStreamThread(new StreamThread)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,8 +21,9 @@ namespace MWSound
|
|||||||
ALCdevice *mDevice;
|
ALCdevice *mDevice;
|
||||||
ALCcontext *mContext;
|
ALCcontext *mContext;
|
||||||
|
|
||||||
typedef std::vector<ALuint> IDVec;
|
typedef std::deque<ALuint> IDDq;
|
||||||
IDVec mFreeSources;
|
IDDq mFreeSources;
|
||||||
|
IDDq mUnusedBuffers;
|
||||||
|
|
||||||
typedef std::map<std::string,ALuint> NameMap;
|
typedef std::map<std::string,ALuint> NameMap;
|
||||||
NameMap mBufferCache;
|
NameMap mBufferCache;
|
||||||
@ -30,27 +31,23 @@ namespace MWSound
|
|||||||
typedef std::map<ALuint,ALuint> IDRefMap;
|
typedef std::map<ALuint,ALuint> IDRefMap;
|
||||||
IDRefMap mBufferRefs;
|
IDRefMap mBufferRefs;
|
||||||
|
|
||||||
typedef std::deque<ALuint> IDDq;
|
|
||||||
IDDq mUnusedBuffers;
|
|
||||||
|
|
||||||
uint64_t mBufferCacheMemSize;
|
uint64_t mBufferCacheMemSize;
|
||||||
|
|
||||||
ALuint getBuffer(const std::string &fname);
|
ALuint getBuffer(const std::string &fname);
|
||||||
void bufferFinished(ALuint buffer);
|
void bufferFinished(ALuint buffer);
|
||||||
|
|
||||||
|
Environment mLastEnvironment;
|
||||||
|
|
||||||
virtual std::vector<std::string> enumerate();
|
virtual std::vector<std::string> enumerate();
|
||||||
virtual void init(const std::string &devname="");
|
virtual void init(const std::string &devname="");
|
||||||
virtual void deinit();
|
virtual void deinit();
|
||||||
|
|
||||||
virtual Sound *playSound(const std::string &fname, float volume, float pitch, bool loop);
|
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags);
|
||||||
virtual Sound *playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
|
||||||
float min, float max, bool loop);
|
float volume, float pitch, float min, float max, int flags);
|
||||||
|
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags);
|
||||||
|
|
||||||
virtual Sound *streamSound(const std::string &fname, float volume, float pitch);
|
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env);
|
||||||
virtual Sound *streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
|
||||||
float min, float max);
|
|
||||||
|
|
||||||
virtual void updateListener(const float *pos, const float *atdir, const float *updir);
|
|
||||||
|
|
||||||
OpenAL_Output& operator=(const OpenAL_Output &rhs);
|
OpenAL_Output& operator=(const OpenAL_Output &rhs);
|
||||||
OpenAL_Output(const OpenAL_Output &rhs);
|
OpenAL_Output(const OpenAL_Output &rhs);
|
||||||
@ -62,6 +59,7 @@ namespace MWSound
|
|||||||
std::auto_ptr<StreamThread> mStreamThread;
|
std::auto_ptr<StreamThread> mStreamThread;
|
||||||
|
|
||||||
friend class OpenAL_Sound;
|
friend class OpenAL_Sound;
|
||||||
|
friend class OpenAL_Sound3D;
|
||||||
friend class OpenAL_SoundStream;
|
friend class OpenAL_SoundStream;
|
||||||
friend class SoundManager;
|
friend class SoundManager;
|
||||||
};
|
};
|
||||||
|
@ -1,19 +1,40 @@
|
|||||||
#ifndef GAME_SOUND_SOUND_H
|
#ifndef GAME_SOUND_SOUND_H
|
||||||
#define GAME_SOUND_SOUND_H
|
#define GAME_SOUND_SOUND_H
|
||||||
|
|
||||||
|
#include <OgreVector3.h>
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
{
|
{
|
||||||
class Sound
|
class Sound
|
||||||
{
|
{
|
||||||
virtual void stop() = 0;
|
virtual void update() = 0;
|
||||||
virtual bool isPlaying() = 0;
|
|
||||||
virtual void update(const float *pos) = 0;
|
|
||||||
|
|
||||||
Sound& operator=(const Sound &rhs);
|
Sound& operator=(const Sound &rhs);
|
||||||
Sound(const Sound &rhs);
|
Sound(const Sound &rhs);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Ogre::Vector3 mPos;
|
||||||
|
float mVolume; /* NOTE: Real volume = mVolume*mBaseVolume */
|
||||||
|
float mBaseVolume;
|
||||||
|
float mPitch;
|
||||||
|
float mMinDistance;
|
||||||
|
float mMaxDistance;
|
||||||
|
int mFlags;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Sound() { }
|
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)
|
||||||
|
, mPitch(1.0f)
|
||||||
|
, mMinDistance(20.0f) /* 1 * min_range_scale */
|
||||||
|
, mMaxDistance(12750.0f) /* 255 * max_range_scale */
|
||||||
|
, mFlags(Play_Normal)
|
||||||
|
{ }
|
||||||
virtual ~Sound() { }
|
virtual ~Sound() { }
|
||||||
|
|
||||||
friend class OpenAL_Output;
|
friend class OpenAL_Output;
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <OgreVector3.h>
|
||||||
|
|
||||||
|
#include "soundmanager.hpp"
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
namespace MWSound
|
namespace MWSound
|
||||||
@ -20,19 +24,23 @@ namespace MWSound
|
|||||||
virtual void init(const std::string &devname="") = 0;
|
virtual void init(const std::string &devname="") = 0;
|
||||||
virtual void deinit() = 0;
|
virtual void deinit() = 0;
|
||||||
|
|
||||||
virtual Sound *playSound(const std::string &fname, float volume, float pitch, bool loop) = 0;
|
virtual SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0;
|
||||||
virtual Sound *playSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
virtual SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos,
|
||||||
float min, float max, bool loop) = 0;
|
float volume, float pitch, float min, float max, int flags) = 0;
|
||||||
virtual Sound *streamSound(const std::string &fname, float volume, float pitch) = 0;
|
virtual SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0;
|
||||||
virtual Sound *streamSound3D(const std::string &fname, const float *pos, float volume, float pitch,
|
|
||||||
float min, float max) = 0;
|
|
||||||
|
|
||||||
virtual void updateListener(const float *pos, const float *atdir, const float *updir) = 0;
|
virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0;
|
||||||
|
|
||||||
Sound_Output& operator=(const Sound_Output &rhs);
|
Sound_Output& operator=(const Sound_Output &rhs);
|
||||||
Sound_Output(const Sound_Output &rhs);
|
Sound_Output(const Sound_Output &rhs);
|
||||||
|
|
||||||
Sound_Output(SoundManager &mgr) : mManager(mgr) { }
|
protected:
|
||||||
|
Ogre::Vector3 mPos;
|
||||||
|
|
||||||
|
Sound_Output(SoundManager &mgr)
|
||||||
|
: mManager(mgr)
|
||||||
|
, mPos(0.0f, 0.0f, 0.0f)
|
||||||
|
{ }
|
||||||
public:
|
public:
|
||||||
virtual ~Sound_Output() { }
|
virtual ~Sound_Output() { }
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ namespace MWSound
|
|||||||
SoundManager::SoundManager(bool useSound, MWWorld::Environment& environment)
|
SoundManager::SoundManager(bool useSound, MWWorld::Environment& environment)
|
||||||
: mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
|
: mResourceMgr(Ogre::ResourceGroupManager::getSingleton())
|
||||||
, mEnvironment(environment)
|
, mEnvironment(environment)
|
||||||
|
, mOutput(new DEFAULT_OUTPUT(*this))
|
||||||
|
|
||||||
{
|
{
|
||||||
if(!useSound)
|
if(!useSound)
|
||||||
return;
|
return;
|
||||||
@ -50,8 +52,6 @@ namespace MWSound
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mOutput.reset(new DEFAULT_OUTPUT(*this));
|
|
||||||
|
|
||||||
std::vector<std::string> names = mOutput->enumerate();
|
std::vector<std::string> names = mOutput->enumerate();
|
||||||
std::cout <<"Enumerated output devices:"<< std::endl;
|
std::cout <<"Enumerated output devices:"<< std::endl;
|
||||||
for(size_t i = 0;i < names.size();i++)
|
for(size_t i = 0;i < names.size();i++)
|
||||||
@ -62,14 +62,11 @@ namespace MWSound
|
|||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
std::cout <<"Sound init failed: "<<e.what()<< std::endl;
|
std::cout <<"Sound init failed: "<<e.what()<< std::endl;
|
||||||
mOutput.reset();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundManager::~SoundManager()
|
SoundManager::~SoundManager()
|
||||||
{
|
{
|
||||||
mLooseSounds.clear();
|
|
||||||
mActiveSounds.clear();
|
mActiveSounds.clear();
|
||||||
mMusic.reset();
|
mMusic.reset();
|
||||||
mOutput.reset();
|
mOutput.reset();
|
||||||
@ -91,10 +88,7 @@ namespace MWSound
|
|||||||
if(snd == NULL)
|
if(snd == NULL)
|
||||||
throw std::runtime_error(std::string("Failed to lookup sound ")+soundId);
|
throw std::runtime_error(std::string("Failed to lookup sound ")+soundId);
|
||||||
|
|
||||||
if(snd->data.volume == 0)
|
volume *= pow(10.0, (snd->data.volume/255.0*3348.0 - 3348.0) / 2000.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)
|
if(snd->data.minRange == 0 && snd->data.maxRange == 0)
|
||||||
{
|
{
|
||||||
@ -109,21 +103,20 @@ namespace MWSound
|
|||||||
max = std::max(min, max);
|
max = std::max(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string("Sound/")+snd->sound;
|
return "Sound/"+snd->sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
|
bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const
|
||||||
{
|
{
|
||||||
SoundMap::const_iterator snditer = mActiveSounds.find(ptr);
|
SoundMap::const_iterator snditer = mActiveSounds.begin();
|
||||||
if(snditer == mActiveSounds.end())
|
while(snditer != mActiveSounds.end())
|
||||||
return false;
|
{
|
||||||
|
if(snditer->second.first == ptr && snditer->second.second == id)
|
||||||
IDMap::const_iterator iditer = snditer->second.find(id);
|
return snditer->first->isPlaying();
|
||||||
if(iditer == snditer->second.end())
|
snditer++;
|
||||||
return false;
|
}
|
||||||
|
return false;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -139,9 +132,10 @@ namespace MWSound
|
|||||||
std::cout <<"Playing "<<filename<< std::endl;
|
std::cout <<"Playing "<<filename<< std::endl;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(mMusic)
|
stopMusic();
|
||||||
mMusic->stop();
|
mMusic = mOutput->streamSound(filename, 0.4f, 1.0f, Play_NoEnv);
|
||||||
mMusic.reset(mOutput->streamSound(filename, 0.4f, 1.0f));
|
mMusic->mBaseVolume = 0.4f;
|
||||||
|
mMusic->mFlags = Play_NoEnv;
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
@ -182,11 +176,17 @@ namespace MWSound
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// The range values are not tested
|
// 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 ESM::Position &pos = ptr.getCellRef().pos;
|
||||||
std::string filePath = std::string("Sound/")+filename;
|
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
|
||||||
|
|
||||||
SoundPtr sound(mOutput->playSound3D(filePath, pos.pos, 1.0f, 1.0f, 100.0f, 20000.0f, false));
|
SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f,
|
||||||
mActiveSounds[ptr]["_say_sound"] = sound;
|
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)
|
||||||
{
|
{
|
||||||
@ -200,86 +200,105 @@ namespace MWSound
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SoundManager::playSound(const std::string& soundId, float volume, float pitch, bool loop)
|
SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode)
|
||||||
{
|
{
|
||||||
float min, max;
|
SoundPtr sound;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string file = lookup(soundId, volume, min, max);
|
float basevol = 1.0f; /* TODO: volume settings */
|
||||||
Sound *sound = mOutput->playSound(file, volume, pitch, loop);
|
float min, max;
|
||||||
mLooseSounds[soundId] = SoundPtr(sound);
|
std::string file = lookup(soundId, basevol, min, max);
|
||||||
|
|
||||||
|
sound = mOutput->playSound(file, volume*basevol, pitch, mode);
|
||||||
|
sound->mVolume = volume;
|
||||||
|
sound->mBaseVolume = basevol;
|
||||||
|
sound->mPitch = pitch;
|
||||||
|
sound->mMinDistance = min;
|
||||||
|
sound->mMaxDistance = max;
|
||||||
|
sound->mFlags = mode;
|
||||||
|
|
||||||
|
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||||
}
|
}
|
||||||
|
return sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
|
SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId,
|
||||||
float volume, float pitch, bool loop, bool untracked)
|
float volume, float pitch, int mode)
|
||||||
{
|
{
|
||||||
float min, max;
|
SoundPtr sound;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Look up the sound in the ESM data
|
// 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 ESM::Position &pos = ptr.getCellRef().pos;
|
||||||
std::string file = lookup(soundId, volume, min, max);
|
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
|
||||||
|
|
||||||
SoundPtr sound(mOutput->playSound3D(file, pos.pos, volume, pitch, min, max, loop));
|
sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode);
|
||||||
if(untracked) mLooseSounds[soundId] = sound;
|
sound->mPos = objpos;
|
||||||
else mActiveSounds[ptr][soundId] = sound;
|
sound->mVolume = volume;
|
||||||
|
sound->mBaseVolume = basevol;
|
||||||
|
sound->mPitch = pitch;
|
||||||
|
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)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||||
}
|
}
|
||||||
|
return sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId)
|
void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId)
|
||||||
{
|
{
|
||||||
// Stop a sound and remove it from the list. If soundId="" then
|
|
||||||
// stop all its sounds.
|
|
||||||
SoundMap::iterator snditer = mActiveSounds.find(ptr);
|
|
||||||
if(snditer == mActiveSounds.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(!soundId.empty())
|
|
||||||
{
|
|
||||||
IDMap::iterator iditer = snditer->second.find(soundId);
|
|
||||||
if(iditer != snditer->second.end())
|
|
||||||
{
|
|
||||||
iditer->second->stop();
|
|
||||||
snditer->second.erase(iditer);
|
|
||||||
if(snditer->second.empty())
|
|
||||||
mActiveSounds.erase(snditer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IDMap::iterator iditer = snditer->second.begin();
|
|
||||||
while(iditer != snditer->second.end())
|
|
||||||
{
|
|
||||||
iditer->second->stop();
|
|
||||||
iditer++;
|
|
||||||
}
|
|
||||||
mActiveSounds.erase(snditer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundManager::stopSound(MWWorld::Ptr::CellStore *cell)
|
|
||||||
{
|
|
||||||
// Remove all references to objects belonging to a given cell
|
|
||||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||||
while(snditer != mActiveSounds.end())
|
while(snditer != mActiveSounds.end())
|
||||||
{
|
{
|
||||||
if(snditer->first.getCell() == cell)
|
if(snditer->second.first == ptr && snditer->second.second == soundId)
|
||||||
{
|
{
|
||||||
IDMap::iterator iditer = snditer->second.begin();
|
snditer->first->stop();
|
||||||
while(iditer != snditer->second.end())
|
mActiveSounds.erase(snditer++);
|
||||||
{
|
}
|
||||||
iditer->second->stop();
|
else
|
||||||
iditer++;
|
snditer++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundManager::stopSound3D(MWWorld::Ptr ptr)
|
||||||
|
{
|
||||||
|
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||||
|
while(snditer != mActiveSounds.end())
|
||||||
|
{
|
||||||
|
if(snditer->second.first == ptr)
|
||||||
|
{
|
||||||
|
snditer->first->stop();
|
||||||
|
mActiveSounds.erase(snditer++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
snditer++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SoundManager::stopSound(const MWWorld::Ptr::CellStore *cell)
|
||||||
|
{
|
||||||
|
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||||
|
while(snditer != mActiveSounds.end())
|
||||||
|
{
|
||||||
|
if(snditer->second.first != MWWorld::Ptr() &&
|
||||||
|
snditer->second.first.getCell() == cell)
|
||||||
|
{
|
||||||
|
snditer->first->stop();
|
||||||
mActiveSounds.erase(snditer++);
|
mActiveSounds.erase(snditer++);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -289,11 +308,17 @@ namespace MWSound
|
|||||||
|
|
||||||
void SoundManager::stopSound(const std::string& soundId)
|
void SoundManager::stopSound(const std::string& soundId)
|
||||||
{
|
{
|
||||||
IDMap::iterator iditer = mLooseSounds.find(soundId);
|
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||||
if(iditer != mLooseSounds.end())
|
while(snditer != mActiveSounds.end())
|
||||||
{
|
{
|
||||||
iditer->second->stop();
|
if(snditer->second.first == MWWorld::Ptr() &&
|
||||||
mLooseSounds.erase(iditer);
|
snditer->second.second == soundId)
|
||||||
|
{
|
||||||
|
snditer->first->stop();
|
||||||
|
mActiveSounds.erase(snditer++);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
snditer++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,16 +329,14 @@ namespace MWSound
|
|||||||
|
|
||||||
void SoundManager::updateObject(MWWorld::Ptr ptr)
|
void SoundManager::updateObject(MWWorld::Ptr ptr)
|
||||||
{
|
{
|
||||||
SoundMap::iterator snditer = mActiveSounds.find(ptr);
|
|
||||||
if(snditer == mActiveSounds.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const ESM::Position &pos = ptr.getCellRef().pos;
|
const ESM::Position &pos = ptr.getCellRef().pos;
|
||||||
IDMap::iterator iditer = snditer->second.begin();
|
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
|
||||||
while(iditer != snditer->second.end())
|
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||||
|
while(snditer != mActiveSounds.end())
|
||||||
{
|
{
|
||||||
iditer->second->update(pos.pos);
|
if(snditer->second.first == ptr)
|
||||||
iditer++;
|
snditer->first->setPosition(objpos);
|
||||||
|
snditer++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,45 +407,37 @@ namespace MWSound
|
|||||||
if(!isMusicPlaying())
|
if(!isMusicPlaying())
|
||||||
startRandomTitle();
|
startRandomTitle();
|
||||||
|
|
||||||
|
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
|
||||||
Ogre::Camera *cam = mEnvironment.mWorld->getPlayer().getRenderer()->getCamera();
|
Ogre::Camera *cam = mEnvironment.mWorld->getPlayer().getRenderer()->getCamera();
|
||||||
Ogre::Vector3 nPos, nDir, nUp;
|
Ogre::Vector3 nPos, nDir, nUp;
|
||||||
nPos = cam->getRealPosition();
|
nPos = cam->getRealPosition();
|
||||||
nDir = cam->getRealDirection();
|
nDir = cam->getRealDirection();
|
||||||
nUp = cam->getRealUp();
|
nUp = cam->getRealUp();
|
||||||
|
|
||||||
|
Environment env = Env_Normal;
|
||||||
|
if(nPos.y < current->cell->water)
|
||||||
|
env = Env_Underwater;
|
||||||
|
|
||||||
// The output handler is expecting vectors oriented like the game
|
// The output handler is expecting vectors oriented like the game
|
||||||
// (that is, -Z goes down, +Y goes forward), but that's not what we
|
// (that is, -Z goes down, +Y goes forward), but that's not what we
|
||||||
// get from Ogre's camera, so we have to convert.
|
// get from Ogre's camera, so we have to convert.
|
||||||
float pos[3] = { nPos[0], -nPos[2], nPos[1] };
|
const Ogre::Vector3 pos(nPos[0], -nPos[2], nPos[1]);
|
||||||
float at[3] = { nDir[0], -nDir[2], nDir[1] };
|
const Ogre::Vector3 at(nDir[0], -nDir[2], nDir[1]);
|
||||||
float up[3] = { nUp[0], -nUp[2], nUp[1] };
|
const Ogre::Vector3 up(nUp[0], -nUp[2], nUp[1]);
|
||||||
mOutput->updateListener(pos, at, up);
|
|
||||||
|
mOutput->updateListener(pos, at, up, env);
|
||||||
|
|
||||||
// Check if any sounds are finished playing, and trash them
|
// Check if any sounds are finished playing, and trash them
|
||||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||||
while(snditer != mActiveSounds.end())
|
while(snditer != mActiveSounds.end())
|
||||||
{
|
{
|
||||||
IDMap::iterator iditer = snditer->second.begin();
|
if(!snditer->first->isPlaying())
|
||||||
while(iditer != snditer->second.end())
|
|
||||||
{
|
|
||||||
if(!iditer->second->isPlaying())
|
|
||||||
snditer->second.erase(iditer++);
|
|
||||||
else
|
|
||||||
iditer++;
|
|
||||||
}
|
|
||||||
if(snditer->second.empty())
|
|
||||||
mActiveSounds.erase(snditer++);
|
mActiveSounds.erase(snditer++);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
snditer->first->update();
|
||||||
snditer++;
|
snditer++;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDMap::iterator iditer = mLooseSounds.begin();
|
|
||||||
while(iditer != mLooseSounds.end())
|
|
||||||
{
|
|
||||||
if(!iditer->second->isPlaying())
|
|
||||||
mLooseSounds.erase(iditer++);
|
|
||||||
else
|
|
||||||
iditer++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
#define GAME_SOUND_SOUNDMANAGER_H
|
#define GAME_SOUND_SOUNDMANAGER_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <OgreResourceGroupManager.h>
|
#include <OgreResourceGroupManager.h>
|
||||||
|
|
||||||
#include <components/files/filelibrary.hpp>
|
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
|
||||||
@ -28,6 +28,25 @@ namespace MWSound
|
|||||||
class Sound;
|
class Sound;
|
||||||
|
|
||||||
typedef boost::shared_ptr<Sound_Decoder> DecoderPtr;
|
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; }
|
||||||
|
|
||||||
|
enum Environment {
|
||||||
|
Env_Normal,
|
||||||
|
Env_Underwater,
|
||||||
|
};
|
||||||
|
|
||||||
class SoundManager
|
class SoundManager
|
||||||
{
|
{
|
||||||
@ -40,11 +59,9 @@ namespace MWSound
|
|||||||
boost::shared_ptr<Sound> mMusic;
|
boost::shared_ptr<Sound> mMusic;
|
||||||
std::string mCurrentPlaylist;
|
std::string mCurrentPlaylist;
|
||||||
|
|
||||||
typedef boost::shared_ptr<Sound> SoundPtr;
|
typedef std::pair<MWWorld::Ptr,std::string> PtrIDPair;
|
||||||
typedef std::map<std::string,SoundPtr> IDMap;
|
typedef std::map<SoundPtr,PtrIDPair> SoundMap;
|
||||||
typedef std::map<MWWorld::Ptr,IDMap> SoundMap;
|
|
||||||
SoundMap mActiveSounds;
|
SoundMap mActiveSounds;
|
||||||
IDMap mLooseSounds;
|
|
||||||
|
|
||||||
std::string lookup(const std::string &soundId,
|
std::string lookup(const std::string &soundId,
|
||||||
float &volume, float &min, float &max);
|
float &volume, float &min, float &max);
|
||||||
@ -88,19 +105,20 @@ namespace MWSound
|
|||||||
bool sayDone(MWWorld::Ptr reference) const;
|
bool sayDone(MWWorld::Ptr reference) const;
|
||||||
///< Is actor not speaking?
|
///< Is actor not speaking?
|
||||||
|
|
||||||
void playSound(const std::string& soundId, float volume, float pitch, bool loop=false);
|
SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal);
|
||||||
///< Play a sound, independently of 3D-position
|
///< Play a sound, independently of 3D-position
|
||||||
|
|
||||||
void playSound3D(MWWorld::Ptr reference, const std::string& soundId,
|
SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId,
|
||||||
float volume, float pitch, bool loop,
|
float volume, float pitch, int mode=Play_Normal);
|
||||||
bool untracked=false);
|
|
||||||
///< Play a sound from an object
|
///< Play a sound from an object
|
||||||
|
|
||||||
void stopSound3D(MWWorld::Ptr reference, const std::string& soundId="");
|
void stopSound3D(MWWorld::Ptr reference, const std::string& soundId);
|
||||||
///< Stop the given object from playing the given sound, If no soundId is given,
|
///< Stop the given object from playing the given sound,
|
||||||
/// all sounds for this reference will stop.
|
|
||||||
|
|
||||||
void stopSound(MWWorld::Ptr::CellStore *cell);
|
void stopSound3D(MWWorld::Ptr reference);
|
||||||
|
///< Stop the given object from playing all sounds.
|
||||||
|
|
||||||
|
void stopSound(const MWWorld::Ptr::CellStore *cell);
|
||||||
///< Stop all sounds for the given cell.
|
///< Stop all sounds for the given cell.
|
||||||
|
|
||||||
void stopSound(const std::string& soundId);
|
void stopSound(const std::string& soundId);
|
||||||
|
@ -50,6 +50,28 @@ namespace MWWorld
|
|||||||
|
|
||||||
return mEngine->rayTest(from,to);
|
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)
|
bool PhysicsSystem::castRay(const Vector3& from, const Vector3& to)
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,11 @@ namespace MWWorld
|
|||||||
bool toggleCollisionMode();
|
bool toggleCollisionMode();
|
||||||
|
|
||||||
std::pair<std::string, float> getFacedHandle (MWWorld::World& world);
|
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
|
// cast ray, return true if it hit something
|
||||||
bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to);
|
bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to);
|
||||||
|
|
||||||
|
@ -54,9 +54,11 @@ void insertCellRefList(MWRender::RenderingManager& rendering, MWWorld::Environme
|
|||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
void Scene::update (float duration){
|
void Scene::update (float duration){
|
||||||
mRendering.update (duration);
|
mRendering.update (duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::unloadCell (CellStoreCollection::iterator iter)
|
void Scene::unloadCell (CellStoreCollection::iterator iter)
|
||||||
{
|
{
|
||||||
std::cout << "Unloading cell\n";
|
std::cout << "Unloading cell\n";
|
||||||
@ -79,6 +81,7 @@ namespace MWWorld
|
|||||||
mPhysics->removeObject (node->getName());
|
mPhysics->removeObject (node->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mRendering.removeCell(*iter);
|
mRendering.removeCell(*iter);
|
||||||
//mPhysics->removeObject("Unnamed_43");
|
//mPhysics->removeObject("Unnamed_43");
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ namespace MWWorld
|
|||||||
mEnvironment.mSoundManager->stopSound (*iter);
|
mEnvironment.mSoundManager->stopSound (*iter);
|
||||||
mActiveCells.erase(*iter);
|
mActiveCells.erase(*iter);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +105,7 @@ namespace MWWorld
|
|||||||
mActiveCells.insert(cell);
|
mActiveCells.insert(cell);
|
||||||
if(result.second){
|
if(result.second){
|
||||||
insertCell(*cell, mEnvironment);
|
insertCell(*cell, mEnvironment);
|
||||||
mRendering.cellAdded (cell);
|
mRendering.cellAdded(cell);
|
||||||
mRendering.configureAmbient(*cell);
|
mRendering.configureAmbient(*cell);
|
||||||
mRendering.requestMap(cell);
|
mRendering.requestMap(cell);
|
||||||
mRendering.configureAmbient(*cell);
|
mRendering.configureAmbient(*cell);
|
||||||
@ -192,6 +196,7 @@ namespace MWWorld
|
|||||||
|
|
||||||
mCurrentCell = *iter;
|
mCurrentCell = *iter;
|
||||||
|
|
||||||
|
|
||||||
// adjust player
|
// adjust player
|
||||||
playerCellChange (mWorld->getExterior(X, Y), position, adjustPlayerPos);
|
playerCellChange (mWorld->getExterior(X, Y), position, adjustPlayerPos);
|
||||||
|
|
||||||
@ -199,6 +204,7 @@ namespace MWWorld
|
|||||||
mWorld->adjustSky();
|
mWorld->adjustSky();
|
||||||
|
|
||||||
mCellChanged = true;
|
mCellChanged = true;
|
||||||
|
mRendering.waterAdded(mCurrentCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
//We need the ogre renderer and a scene node.
|
//We need the ogre renderer and a scene node.
|
||||||
@ -238,6 +244,7 @@ namespace MWWorld
|
|||||||
Ptr::CellStore *cell = mWorld->getInterior(cellName);
|
Ptr::CellStore *cell = mWorld->getInterior(cellName);
|
||||||
|
|
||||||
loadCell (cell);
|
loadCell (cell);
|
||||||
|
|
||||||
|
|
||||||
// adjust player
|
// adjust player
|
||||||
mCurrentCell = cell;
|
mCurrentCell = cell;
|
||||||
@ -250,6 +257,8 @@ namespace MWWorld
|
|||||||
mWorld->adjustSky();
|
mWorld->adjustSky();
|
||||||
|
|
||||||
mCellChanged = true;
|
mCellChanged = true;
|
||||||
|
|
||||||
|
mRendering.waterAdded(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::changeToExteriorCell (const ESM::Position& position)
|
void Scene::changeToExteriorCell (const ESM::Position& position)
|
||||||
|
@ -146,10 +146,10 @@ namespace MWWorld
|
|||||||
mRendering->skySetDate (mGlobalVariables->getInt ("day"),
|
mRendering->skySetDate (mGlobalVariables->getInt ("day"),
|
||||||
mGlobalVariables->getInt ("month"));
|
mGlobalVariables->getInt ("month"));
|
||||||
|
|
||||||
mRendering->getSkyManager()->enable();
|
mRendering->skyEnable();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mRendering->getSkyManager()->disable();
|
mRendering->skyDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
World::World (OEngine::Render::OgreRenderer& renderer,
|
World::World (OEngine::Render::OgreRenderer& renderer,
|
||||||
@ -157,7 +157,8 @@ namespace MWWorld
|
|||||||
const std::string& master, const boost::filesystem::path& resDir,
|
const std::string& master, const boost::filesystem::path& resDir,
|
||||||
bool newGame, Environment& environment, const std::string& encoding)
|
bool newGame, Environment& environment, const std::string& encoding)
|
||||||
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
|
: 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);
|
mPhysics = new PhysicsSystem(renderer);
|
||||||
mPhysEngine = mPhysics->getEngine();
|
mPhysEngine = mPhysics->getEngine();
|
||||||
@ -498,13 +499,21 @@ namespace MWWorld
|
|||||||
|
|
||||||
std::string World::getFacedHandle()
|
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() ||
|
if (result.first.empty() ||
|
||||||
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i)
|
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
return result.first;
|
return result.first;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// updated every few frames in update()
|
||||||
|
return mFacedHandle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::deleteObject (Ptr ptr)
|
void World::deleteObject (Ptr ptr)
|
||||||
@ -531,9 +540,10 @@ namespace MWWorld
|
|||||||
ptr.getRefData().getPosition().pos[0] = x;
|
ptr.getRefData().getPosition().pos[0] = x;
|
||||||
ptr.getRefData().getPosition().pos[1] = y;
|
ptr.getRefData().getPosition().pos[1] = y;
|
||||||
ptr.getRefData().getPosition().pos[2] = z;
|
ptr.getRefData().getPosition().pos[2] = z;
|
||||||
|
|
||||||
if (ptr==mPlayer->getPlayer())
|
if (ptr==mPlayer->getPlayer())
|
||||||
{
|
{
|
||||||
|
//std::cout << "X:" << ptr.getRefData().getPosition().pos[0] << " Z: " << ptr.getRefData().getPosition().pos[1] << "\n";
|
||||||
|
|
||||||
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
|
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
|
||||||
if (currentCell)
|
if (currentCell)
|
||||||
{
|
{
|
||||||
@ -705,13 +715,82 @@ namespace MWWorld
|
|||||||
|
|
||||||
mWeatherManager->update (duration);
|
mWeatherManager->update (duration);
|
||||||
|
|
||||||
// cast a ray from player to sun to detect if the sun is visible
|
if (!mRendering->occlusionQuerySupported())
|
||||||
// this is temporary until we find a better place to put this code
|
{
|
||||||
// currently its here because we need to access the physics system
|
// cast a ray from player to sun to detect if the sun is visible
|
||||||
float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
|
// this is temporary until we find a better place to put this code
|
||||||
Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
|
// currently its here because we need to access the physics system
|
||||||
sun = Vector3(sun.x, -sun.z, sun.y);
|
float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
|
||||||
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
|
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
|
bool World::isCellExterior() const
|
||||||
@ -754,4 +833,15 @@ namespace MWWorld
|
|||||||
{
|
{
|
||||||
return mRendering->getFader();
|
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);
|
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;
|
int getDaysPerMonth (int month) const;
|
||||||
|
|
||||||
@ -112,6 +118,9 @@ namespace MWWorld
|
|||||||
Ptr::CellStore *getExterior (int x, int y);
|
Ptr::CellStore *getExterior (int x, int y);
|
||||||
|
|
||||||
Ptr::CellStore *getInterior (const std::string& name);
|
Ptr::CellStore *getInterior (const std::string& name);
|
||||||
|
|
||||||
|
void setWaterHeight(const float height);
|
||||||
|
void toggleWater();
|
||||||
|
|
||||||
void adjustSky();
|
void adjustSky();
|
||||||
|
|
||||||
|
@ -51,13 +51,13 @@ find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h
|
|||||||
# Find the libraries
|
# Find the libraries
|
||||||
|
|
||||||
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics)
|
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics)
|
||||||
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_d)
|
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_Debug BulletDynamics_d)
|
||||||
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision)
|
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision)
|
||||||
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_d)
|
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d)
|
||||||
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY LinearMath BulletMath)
|
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath)
|
||||||
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG LinearMath_d BulletMath_d)
|
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_debug LinearMath_d)
|
||||||
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody)
|
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody)
|
||||||
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_d)
|
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletSoftBody_d)
|
||||||
|
|
||||||
|
|
||||||
# handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if
|
# handle the QUIETLY and REQUIRED arguments and set BULLET_FOUND to TRUE if
|
||||||
|
@ -41,7 +41,21 @@ struct ciLessBoost : std::binary_function<std::string, std::string, bool>
|
|||||||
{
|
{
|
||||||
bool operator() (const std::string & s1, const std::string & s2) const {
|
bool operator() (const std::string & s1, const std::string & s2) const {
|
||||||
//case insensitive version of is_less
|
//case insensitive version of is_less
|
||||||
return lexicographical_compare(s1, s2, boost::algorithm::is_iless());
|
return boost::ilexicographical_compare(s1, s2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pathComparer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string find;
|
||||||
|
|
||||||
|
public:
|
||||||
|
pathComparer(const std::string& toFind) : find(toFind) { }
|
||||||
|
|
||||||
|
bool operator() (const std::string& other)
|
||||||
|
{
|
||||||
|
return boost::iequals(find, other);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,16 +69,62 @@ class DirArchive: public Ogre::FileSystemArchive
|
|||||||
std::map<std::string, std::vector<std::string>, ciLessBoost> m;
|
std::map<std::string, std::vector<std::string>, ciLessBoost> m;
|
||||||
unsigned int cutoff;
|
unsigned int cutoff;
|
||||||
|
|
||||||
bool comparePortion(std::string file1, std::string file2, int start, int size) const
|
bool findFile(const String& filename, std::string& copy) const
|
||||||
{
|
{
|
||||||
for(int i = start; i < start+size; i++)
|
|
||||||
{
|
{
|
||||||
char one = file1.at(i);
|
String passed = filename;
|
||||||
char two = file2.at(i);
|
if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<'
|
||||||
if(tolower(one) != tolower(two) )
|
|| filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':'
|
||||||
return false;
|
|| filename.at(filename.length() - 1) == '|')
|
||||||
|
{
|
||||||
|
passed = filename.substr(0, filename.length() - 2);
|
||||||
|
}
|
||||||
|
if(filename.at(filename.length() - 2) == '>')
|
||||||
|
passed = filename.substr(0, filename.length() - 6);
|
||||||
|
copy = passed;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
std::replace(copy.begin(), copy.end(), '\\', '/');
|
||||||
|
|
||||||
|
if(copy.at(0) == '/')
|
||||||
|
copy.erase(0, 1);
|
||||||
|
|
||||||
|
if(fsstrict == true)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
std::string folder;
|
||||||
|
int delimiter = 0;
|
||||||
|
size_t lastSlash = copy.rfind('/');
|
||||||
|
if (lastSlash != std::string::npos)
|
||||||
|
{
|
||||||
|
folder = copy.substr(0, lastSlash);
|
||||||
|
delimiter = lastSlash+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> current;
|
||||||
|
{
|
||||||
|
std::map<std::string,std::vector<std::string>,ciLessBoost>::const_iterator found = m.find(folder);
|
||||||
|
|
||||||
|
if (found == m.end())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current = found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>::iterator find = std::lower_bound(current.begin(), current.end(), copy, ciLessBoost());
|
||||||
|
if (find != current.end() && !ciLessBoost()(copy, current.front()))
|
||||||
|
{
|
||||||
|
if (!boost::iequals(copy, *find))
|
||||||
|
if ((find = std::find_if(current.begin(), current.end(), pathComparer(copy))) == current.end()) //\todo Check if this line is actually needed
|
||||||
|
return false;
|
||||||
|
|
||||||
|
copy = *find;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -83,16 +143,14 @@ class DirArchive: public Ogre::FileSystemArchive
|
|||||||
//need to cut off first
|
//need to cut off first
|
||||||
boost::filesystem::directory_iterator dir_iter(d), dir_end;
|
boost::filesystem::directory_iterator dir_iter(d), dir_end;
|
||||||
std::vector<std::string> filesind;
|
std::vector<std::string> filesind;
|
||||||
boost::filesystem::path f;
|
|
||||||
for(;dir_iter != dir_end; dir_iter++)
|
for(;dir_iter != dir_end; dir_iter++)
|
||||||
{
|
{
|
||||||
if(boost::filesystem::is_directory(*dir_iter))
|
if(boost::filesystem::is_directory(*dir_iter))
|
||||||
populateMap(*dir_iter);
|
populateMap(*dir_iter);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
std::string s = dir_iter->path().string();
|
||||||
f = *dir_iter;
|
std::replace(s.begin(), s.end(), '\\', '/');
|
||||||
std::string s = f.string();
|
|
||||||
|
|
||||||
std::string small;
|
std::string small;
|
||||||
if(cutoff < s.size())
|
if(cutoff < s.size())
|
||||||
@ -103,14 +161,17 @@ class DirArchive: public Ogre::FileSystemArchive
|
|||||||
filesind.push_back(small);
|
filesind.push_back(small);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::sort(filesind.begin(), filesind.end(), ciLessBoost());
|
||||||
|
|
||||||
std::string small;
|
std::string small;
|
||||||
std::string original = d.string();
|
std::string original = d.string();
|
||||||
|
std::replace(original.begin(), original.end(), '\\', '/');
|
||||||
if(cutoff < original.size())
|
if(cutoff < original.size())
|
||||||
small = original.substr(cutoff, original.size() - cutoff);
|
small = original.substr(cutoff, original.size() - cutoff);
|
||||||
else
|
else
|
||||||
small = original.substr(cutoff - 1, original.size() - cutoff);
|
small = original.substr(cutoff - 1, original.size() - cutoff);
|
||||||
m[small] = filesind;
|
|
||||||
|
|
||||||
|
m[small] = filesind;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isCaseSensitive() const { return fsstrict; }
|
bool isCaseSensitive() const { return fsstrict; }
|
||||||
@ -120,97 +181,21 @@ class DirArchive: public Ogre::FileSystemArchive
|
|||||||
void unload() {}
|
void unload() {}
|
||||||
|
|
||||||
bool exists(const String& filename) {
|
bool exists(const String& filename) {
|
||||||
std::string copy = filename;
|
std::string copy;
|
||||||
|
|
||||||
|
if (findFile(filename, copy))
|
||||||
|
|
||||||
for (unsigned int i = 0; i < filename.size(); i++)
|
|
||||||
{
|
|
||||||
if(copy.at(i) == '\\' ){
|
|
||||||
copy.replace(i, 1, "/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(copy.at(0) == '\\' || copy.at(0) == '/')
|
|
||||||
{
|
|
||||||
copy.erase(0, 1);
|
|
||||||
}
|
|
||||||
if(fsstrict == true)
|
|
||||||
{
|
|
||||||
//std::cout << "fsstrict " << copy << "\n";
|
|
||||||
return FileSystemArchive::exists(copy);
|
return FileSystemArchive::exists(copy);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int last = copy.size() - 1;
|
|
||||||
int i = last;
|
|
||||||
|
|
||||||
for (;last >= 0; i--)
|
|
||||||
{
|
|
||||||
if(copy.at(i) == '/' || copy.at(i) == '\\')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string folder = copy.substr(0, i); //folder with no slash
|
|
||||||
|
|
||||||
std::vector<std::string>& current = m[folder];
|
|
||||||
|
|
||||||
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
|
|
||||||
{
|
|
||||||
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
|
|
||||||
return FileSystemArchive::exists(*iter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataStreamPtr open(const String& filename, bool readonly = true) const
|
DataStreamPtr open(const String& filename, bool readonly = true) const
|
||||||
{
|
{
|
||||||
std::map<std::string, std::vector<std::string>, ciLessBoost> mlocal = m;
|
std::string copy;
|
||||||
std::string copy = filename;
|
|
||||||
|
|
||||||
|
if (findFile(filename, copy))
|
||||||
|
|
||||||
for (unsigned int i = 0; i < filename.size(); i++)
|
|
||||||
{
|
|
||||||
if(copy.at(i) == '\\' ){
|
|
||||||
copy.replace(i, 1, "/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(copy.at(0) == '\\' || copy.at(0) == '/')
|
|
||||||
{
|
|
||||||
copy.erase(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(fsstrict == true)
|
|
||||||
{
|
|
||||||
return FileSystemArchive::open(copy, readonly);
|
return FileSystemArchive::open(copy, readonly);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int last = copy.size() - 1;
|
|
||||||
int i = last;
|
|
||||||
|
|
||||||
for (;last >= 0; i--)
|
|
||||||
{
|
|
||||||
if(copy.at(i) == '/' || copy.at(i) == '\\')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string folder = copy.substr(0, i); //folder with no slash
|
|
||||||
std::vector<std::string> current = mlocal[folder];
|
|
||||||
|
|
||||||
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
|
|
||||||
{
|
|
||||||
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
|
|
||||||
return FileSystemArchive::open(*iter, readonly);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataStreamPtr p;
|
DataStreamPtr p;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@ -256,8 +241,12 @@ public:
|
|||||||
return DataStreamPtr(new Mangle2OgreStream(strm));
|
return DataStreamPtr(new Mangle2OgreStream(strm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool exists(const String& filename) {
|
||||||
|
return cexists(filename);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the file exists.
|
// Check if the file exists.
|
||||||
bool exists(const String& filename) {
|
bool cexists(const String& filename) const {
|
||||||
String passed = filename;
|
String passed = filename;
|
||||||
if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<'
|
if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<'
|
||||||
|| filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':'
|
|| filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':'
|
||||||
@ -268,7 +257,7 @@ public:
|
|||||||
if(filename.at(filename.length() - 2) == '>')
|
if(filename.at(filename.length() - 2) == '>')
|
||||||
passed = filename.substr(0, filename.length() - 6);
|
passed = filename.substr(0, filename.length() - 6);
|
||||||
|
|
||||||
return arc.exists(passed.c_str());
|
return arc.exists(passed.c_str());
|
||||||
}
|
}
|
||||||
time_t getModifiedTime(const String&) { return 0; }
|
time_t getModifiedTime(const String&) { return 0; }
|
||||||
|
|
||||||
@ -308,6 +297,29 @@ return arc.exists(passed.c_str());
|
|||||||
located in BSAs. So instead we channel it through exists() and
|
located in BSAs. So instead we channel it through exists() and
|
||||||
set up a single-element result list if the file is found.
|
set up a single-element result list if the file is found.
|
||||||
*/
|
*/
|
||||||
|
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
|
||||||
|
bool dirs = false) const
|
||||||
|
{
|
||||||
|
FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList());
|
||||||
|
|
||||||
|
// Check if the file exists (only works for single files - wild
|
||||||
|
// cards and recursive search isn't implemented.)
|
||||||
|
if(cexists(pattern))
|
||||||
|
{
|
||||||
|
FileInfo fi;
|
||||||
|
fi.archive = this;
|
||||||
|
fi.filename = pattern;
|
||||||
|
// It apparently doesn't matter that we return bogus
|
||||||
|
// information
|
||||||
|
fi.path = "";
|
||||||
|
fi.compressedSize = fi.uncompressedSize = 0;
|
||||||
|
|
||||||
|
ptr->push_back(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
|
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
|
||||||
bool dirs = false)
|
bool dirs = false)
|
||||||
{
|
{
|
||||||
@ -315,7 +327,7 @@ return arc.exists(passed.c_str());
|
|||||||
|
|
||||||
// Check if the file exists (only works for single files - wild
|
// Check if the file exists (only works for single files - wild
|
||||||
// cards and recursive search isn't implemented.)
|
// cards and recursive search isn't implemented.)
|
||||||
if(exists(pattern))
|
if(cexists(pattern))
|
||||||
{
|
{
|
||||||
FileInfo fi;
|
FileInfo fi;
|
||||||
fi.archive = this;
|
fi.archive = this;
|
||||||
|
@ -148,9 +148,9 @@ void BSAFile::readHeader()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the index of a given file name, or -1 if not found
|
/// Get the index of a given file name, or -1 if not found
|
||||||
int BSAFile::getIndex(const char *str)
|
int BSAFile::getIndex(const char *str) const
|
||||||
{
|
{
|
||||||
Lookup::iterator it;
|
Lookup::const_iterator it;
|
||||||
it = lookup.find(str);
|
it = lookup.find(str);
|
||||||
|
|
||||||
if(it == lookup.end()) return -1;
|
if(it == lookup.end()) return -1;
|
||||||
|
@ -93,7 +93,7 @@ class BSAFile
|
|||||||
void readHeader();
|
void readHeader();
|
||||||
|
|
||||||
/// Get the index of a given file name, or -1 if not found
|
/// Get the index of a given file name, or -1 if not found
|
||||||
int getIndex(const char *str);
|
int getIndex(const char *str) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ class BSAFile
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/// Check if a file exists
|
/// Check if a file exists
|
||||||
bool exists(const char *file) { return getIndex(file) != -1; }
|
bool exists(const char *file) const { return getIndex(file) != -1; }
|
||||||
|
|
||||||
/** Open a file contained in the archive. Throws an exception if the
|
/** Open a file contained in the archive. Throws an exception if the
|
||||||
file doesn't exist.
|
file doesn't exist.
|
||||||
|
@ -21,8 +21,13 @@ void Cell::load(ESMReader &esm)
|
|||||||
if (data.flags & Interior)
|
if (data.flags & Interior)
|
||||||
{
|
{
|
||||||
// Interior cells
|
// Interior cells
|
||||||
|
if (esm.isNextSub("INTV"))
|
||||||
if (esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
|
{
|
||||||
|
int waterl;
|
||||||
|
esm.getHT(waterl);
|
||||||
|
water = (float) waterl;
|
||||||
|
}
|
||||||
|
else if (esm.isNextSub("WHGT"))
|
||||||
esm.getHT(water);
|
esm.getHT(water);
|
||||||
|
|
||||||
// Quasi-exterior cells have a region (which determines the
|
// Quasi-exterior cells have a region (which determines the
|
||||||
|
@ -114,11 +114,26 @@ struct Cell
|
|||||||
ESM_Context context; // File position
|
ESM_Context context; // File position
|
||||||
DATAstruct data;
|
DATAstruct data;
|
||||||
AMBIstruct ambi;
|
AMBIstruct ambi;
|
||||||
int water; // Water level
|
float water; // Water level
|
||||||
int mapColor;
|
int mapColor;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm);
|
||||||
|
|
||||||
|
bool isExterior() const
|
||||||
|
{
|
||||||
|
return !(data.flags & Interior);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getGridX() const
|
||||||
|
{
|
||||||
|
return data.gridX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getGridY() const
|
||||||
|
{
|
||||||
|
return data.gridY;
|
||||||
|
}
|
||||||
|
|
||||||
// Restore the given reader to the stored position. Will try to open
|
// Restore the given reader to the stored position. Will try to open
|
||||||
// the file matching the stored file name. If you want to read from
|
// the file matching the stored file name. If you want to read from
|
||||||
// somewhere other than the file system, you need to pre-open the
|
// somewhere other than the file system, you need to pre-open the
|
||||||
|
@ -4,6 +4,8 @@ namespace ESM
|
|||||||
{
|
{
|
||||||
void Land::load(ESMReader &esm)
|
void Land::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
mEsm = &esm;
|
||||||
|
|
||||||
// Get the grid location
|
// Get the grid location
|
||||||
esm.getSubNameIs("INTV");
|
esm.getSubNameIs("INTV");
|
||||||
esm.getSubHeaderIs(8);
|
esm.getSubHeaderIs(8);
|
||||||
@ -19,14 +21,117 @@ void Land::load(ESMReader &esm)
|
|||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
// Skip these here. Load the actual data when the cell is loaded.
|
// Skip these here. Load the actual data when the cell is loaded.
|
||||||
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
|
if (esm.isNextSub("VNML"))
|
||||||
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
|
{
|
||||||
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
|
esm.skipHSubSize(12675);
|
||||||
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
|
cnt++;
|
||||||
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
|
}
|
||||||
|
if (esm.isNextSub("VHGT"))
|
||||||
|
{
|
||||||
|
esm.skipHSubSize(4232);
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
if (esm.isNextSub("WNAM"))
|
||||||
|
{
|
||||||
|
esm.skipHSubSize(81);
|
||||||
|
}
|
||||||
|
if (esm.isNextSub("VCLR"))
|
||||||
|
{
|
||||||
|
esm.skipHSubSize(12675);
|
||||||
|
}
|
||||||
|
if (esm.isNextSub("VTEX"))
|
||||||
|
{
|
||||||
|
esm.skipHSubSize(512);
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
// We need all three of VNML, VHGT and VTEX in order to use the
|
// We need all three of VNML, VHGT and VTEX in order to use the
|
||||||
// landscape.
|
// landscape.
|
||||||
hasData = (cnt == 3);
|
hasData = (cnt == 3);
|
||||||
|
|
||||||
|
dataLoaded = false;
|
||||||
|
landData = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Land::loadData()
|
||||||
|
{
|
||||||
|
if (dataLoaded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
landData = new LandData;
|
||||||
|
|
||||||
|
if (hasData)
|
||||||
|
{
|
||||||
|
mEsm->restoreContext(context);
|
||||||
|
|
||||||
|
//esm.getHNExact(landData->normals, sizeof(VNML), "VNML");
|
||||||
|
if (mEsm->isNextSub("VNML"))
|
||||||
|
{
|
||||||
|
mEsm->skipHSubSize(12675);
|
||||||
|
}
|
||||||
|
|
||||||
|
VHGT rawHeights;
|
||||||
|
|
||||||
|
mEsm->getHNExact(&rawHeights, sizeof(VHGT), "VHGT");
|
||||||
|
int currentHeightOffset = rawHeights.heightOffset;
|
||||||
|
for (int y = 0; y < LAND_SIZE; y++)
|
||||||
|
{
|
||||||
|
currentHeightOffset += rawHeights.heightData[y * LAND_SIZE];
|
||||||
|
landData->heights[y * LAND_SIZE] = currentHeightOffset * HEIGHT_SCALE;
|
||||||
|
|
||||||
|
int tempOffset = currentHeightOffset;
|
||||||
|
for (int x = 1; x < LAND_SIZE; x++)
|
||||||
|
{
|
||||||
|
tempOffset += rawHeights.heightData[y * LAND_SIZE + x];
|
||||||
|
landData->heights[x + y * LAND_SIZE] = tempOffset * HEIGHT_SCALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mEsm->isNextSub("WNAM"))
|
||||||
|
{
|
||||||
|
mEsm->skipHSubSize(81);
|
||||||
|
}
|
||||||
|
if (mEsm->isNextSub("VCLR"))
|
||||||
|
{
|
||||||
|
landData->usingColours = true;
|
||||||
|
mEsm->getHExact(&landData->colours, 3*LAND_NUM_VERTS);
|
||||||
|
}else{
|
||||||
|
landData->usingColours = false;
|
||||||
|
}
|
||||||
|
//TODO fix magic numbers
|
||||||
|
uint16_t vtex[512];
|
||||||
|
mEsm->getHNExact(&vtex, 512, "VTEX");
|
||||||
|
|
||||||
|
int readPos = 0; //bit ugly, but it works
|
||||||
|
for ( int y1 = 0; y1 < 4; y1++ )
|
||||||
|
for ( int x1 = 0; x1 < 4; x1++ )
|
||||||
|
for ( int y2 = 0; y2 < 4; y2++)
|
||||||
|
for ( int x2 = 0; x2 < 4; x2++ )
|
||||||
|
landData->textures[(y1*4+y2)*16+(x1*4+x2)] = vtex[readPos++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
landData->usingColours = false;
|
||||||
|
memset(&landData->textures, 0, 512 * sizeof(uint16_t));
|
||||||
|
for (int i = 0; i < LAND_NUM_VERTS; i++)
|
||||||
|
{
|
||||||
|
landData->heights[i] = -256.0f * HEIGHT_SCALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Land::unloadData()
|
||||||
|
{
|
||||||
|
if (dataLoaded)
|
||||||
|
{
|
||||||
|
delete landData;
|
||||||
|
landData = NULL;
|
||||||
|
dataLoaded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,66 @@ struct Land
|
|||||||
|
|
||||||
// File context. This allows the ESM reader to be 'reset' to this
|
// File context. This allows the ESM reader to be 'reset' to this
|
||||||
// location later when we are ready to load the full data set.
|
// location later when we are ready to load the full data set.
|
||||||
|
ESMReader* mEsm;
|
||||||
ESM_Context context;
|
ESM_Context context;
|
||||||
|
|
||||||
bool hasData;
|
bool hasData;
|
||||||
|
|
||||||
|
bool dataLoaded;
|
||||||
|
|
||||||
|
// number of vertices per side
|
||||||
|
static const int LAND_SIZE = 65;
|
||||||
|
|
||||||
|
// cell terrain size in world coords
|
||||||
|
static const int REAL_SIZE = 8192;
|
||||||
|
|
||||||
|
// total number of vertices
|
||||||
|
static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE;
|
||||||
|
|
||||||
|
static const int HEIGHT_SCALE = 8;
|
||||||
|
|
||||||
|
//number of textures per side of land
|
||||||
|
static const int LAND_TEXTURE_SIZE = 16;
|
||||||
|
|
||||||
|
//total number of textures per land
|
||||||
|
static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE;
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
struct VHGT
|
||||||
|
{
|
||||||
|
float heightOffset;
|
||||||
|
int8_t heightData[LAND_NUM_VERTS];
|
||||||
|
short unknown1;
|
||||||
|
char unknown2;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef uint8_t VNML[LAND_NUM_VERTS * 3];
|
||||||
|
|
||||||
|
struct LandData
|
||||||
|
{
|
||||||
|
float heightOffset;
|
||||||
|
float heights[LAND_NUM_VERTS];
|
||||||
|
//float normals[LAND_NUM_VERTS * 3];
|
||||||
|
uint16_t textures[LAND_NUM_TEXTURES];
|
||||||
|
|
||||||
|
bool usingColours;
|
||||||
|
char colours[3 * LAND_NUM_VERTS];
|
||||||
|
};
|
||||||
|
|
||||||
|
LandData *landData;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually loads data
|
||||||
|
*/
|
||||||
|
void loadData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees memory allocated for land data
|
||||||
|
*/
|
||||||
|
void unloadData();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -95,12 +95,17 @@ namespace ESMS
|
|||||||
State_Unloaded, State_Preloaded, State_Loaded
|
State_Unloaded, State_Preloaded, State_Loaded
|
||||||
};
|
};
|
||||||
|
|
||||||
CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) {}
|
CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded)
|
||||||
|
{
|
||||||
|
mWaterLevel = cell->water;
|
||||||
|
}
|
||||||
|
|
||||||
const ESM::Cell *cell;
|
const ESM::Cell *cell;
|
||||||
State mState;
|
State mState;
|
||||||
std::vector<std::string> mIds;
|
std::vector<std::string> mIds;
|
||||||
|
|
||||||
|
float mWaterLevel;
|
||||||
|
|
||||||
// Lists for each individual object type
|
// Lists for each individual object type
|
||||||
CellRefList<Activator, D> activators;
|
CellRefList<Activator, D> activators;
|
||||||
CellRefList<Potion, D> potions;
|
CellRefList<Potion, D> potions;
|
||||||
|
@ -201,15 +201,21 @@ namespace ESMS
|
|||||||
|
|
||||||
// TODO: For multiple ESM/ESP files we need one list per file.
|
// TODO: For multiple ESM/ESP files we need one list per file.
|
||||||
std::vector<LandTexture> ltex;
|
std::vector<LandTexture> ltex;
|
||||||
int count;
|
|
||||||
|
|
||||||
LTexList() : count(0)
|
LTexList()
|
||||||
{
|
{
|
||||||
// More than enough to hold Morrowind.esm.
|
// More than enough to hold Morrowind.esm.
|
||||||
ltex.reserve(128);
|
ltex.reserve(128);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getSize() { return count; }
|
const LandTexture* search(size_t index) const
|
||||||
|
{
|
||||||
|
assert(index < ltex.size());
|
||||||
|
return <ex.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSize() { return ltex.size(); }
|
||||||
|
int getSize() const { return ltex.size(); }
|
||||||
|
|
||||||
virtual void listIdentifier (std::vector<std::string>& identifier) const {}
|
virtual void listIdentifier (std::vector<std::string>& identifier) const {}
|
||||||
|
|
||||||
@ -233,12 +239,18 @@ namespace ESMS
|
|||||||
*/
|
*/
|
||||||
struct LandList : RecList
|
struct LandList : RecList
|
||||||
{
|
{
|
||||||
virtual ~LandList() {}
|
virtual ~LandList()
|
||||||
|
{
|
||||||
|
for ( LandMap::iterator itr = lands.begin(); itr != lands.end(); ++itr )
|
||||||
|
{
|
||||||
|
delete itr->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Map containing all landscapes
|
// Map containing all landscapes
|
||||||
typedef std::map<int, Land*> LandsCol;
|
typedef std::pair<int, int> LandCoord;
|
||||||
typedef std::map<int, LandsCol> Lands;
|
typedef std::map<LandCoord, Land*> LandMap;
|
||||||
Lands lands;
|
LandMap lands;
|
||||||
|
|
||||||
int count;
|
int count;
|
||||||
LandList() : count(0) {}
|
LandList() : count(0) {}
|
||||||
@ -247,17 +259,15 @@ namespace ESMS
|
|||||||
virtual void listIdentifier (std::vector<std::string>& identifier) const {}
|
virtual void listIdentifier (std::vector<std::string>& identifier) const {}
|
||||||
|
|
||||||
// Find land for the given coordinates. Return null if no data.
|
// Find land for the given coordinates. Return null if no data.
|
||||||
const Land *search(int x, int y) const
|
Land *search(int x, int y) const
|
||||||
{
|
{
|
||||||
Lands::const_iterator it = lands.find(x);
|
LandMap::const_iterator itr = lands.find(std::make_pair<int, int>(x, y));
|
||||||
if(it==lands.end())
|
if ( itr == lands.end() )
|
||||||
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
LandsCol::const_iterator it2 = it->second.find(y);
|
return itr->second;
|
||||||
if(it2 == it->second.end())
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return it2->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(ESMReader &esm, const std::string &id)
|
void load(ESMReader &esm, const std::string &id)
|
||||||
@ -266,11 +276,11 @@ namespace ESMS
|
|||||||
|
|
||||||
// Create the structure and load it. This actually skips the
|
// Create the structure and load it. This actually skips the
|
||||||
// landscape data and remembers the file position for later.
|
// landscape data and remembers the file position for later.
|
||||||
Land *land = new Land;
|
Land *land = new Land();
|
||||||
land->load(esm);
|
land->load(esm);
|
||||||
|
|
||||||
// Store the structure
|
// Store the structure
|
||||||
lands[land->X][land->Y] = land;
|
lands[std::make_pair<int, int>(land->X, land->Y)] = land;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ namespace ESMS
|
|||||||
recLists[REC_GLOB] = &globals;
|
recLists[REC_GLOB] = &globals;
|
||||||
recLists[REC_GMST] = &gameSettings;
|
recLists[REC_GMST] = &gameSettings;
|
||||||
recLists[REC_INGR] = &ingreds;
|
recLists[REC_INGR] = &ingreds;
|
||||||
//recLists[REC_LAND] = &lands;
|
recLists[REC_LAND] = &lands;
|
||||||
recLists[REC_LEVC] = &creatureLists;
|
recLists[REC_LEVC] = &creatureLists;
|
||||||
recLists[REC_LEVI] = &itemLists;
|
recLists[REC_LEVI] = &itemLists;
|
||||||
recLists[REC_LIGH] = &lights;
|
recLists[REC_LIGH] = &lights;
|
||||||
|
@ -30,4 +30,9 @@ namespace Files
|
|||||||
|
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Files::PathContainer& Collections::getPaths() const
|
||||||
|
{
|
||||||
|
return mDirectories;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ namespace Files
|
|||||||
/// leading dot and must be all lower-case.
|
/// leading dot and must be all lower-case.
|
||||||
const MultiDirCollection& getCollection(const std::string& extension) const;
|
const MultiDirCollection& getCollection(const std::string& extension) const;
|
||||||
|
|
||||||
|
const Files::PathContainer& getPaths() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<std::string, MultiDirCollection> MultiDirCollectionContainer;
|
typedef std::map<std::string, MultiDirCollection> MultiDirCollectionContainer;
|
||||||
Files::PathContainer mDirectories;
|
Files::PathContainer mDirectories;
|
||||||
|
@ -1368,7 +1368,7 @@ void NIFLoader::loadResource(Resource *resource)
|
|||||||
|
|
||||||
if (!vfs->isFile(resourceName))
|
if (!vfs->isFile(resourceName))
|
||||||
{
|
{
|
||||||
warn("File not found.");
|
warn("File "+resourceName+" not found.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
files/CMakeLists.txt
Normal file
13
files/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
project(resources)
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/caustic_0.png "${OpenMW_BINARY_DIR}/resources/water/caustic_0.png" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_Fresnel.cg "${OpenMW_BINARY_DIR}/resources/water/Example_Fresnel.cg" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_FresnelPS.asm "${OpenMW_BINARY_DIR}/resources/water/Example_FresnelPS.asm" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassFP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassFP.cg" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassVP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassVP.cg" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/perlinvolume.dds "${OpenMW_BINARY_DIR}/resources/water/perlinvolume.dds" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Water02.jpg "${OpenMW_BINARY_DIR}/resources/water/Water02.jpg" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.compositor "${OpenMW_BINARY_DIR}/resources/water/water.compositor" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/waves2.dds "${OpenMW_BINARY_DIR}/resources/water/waves2.dds" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Examples-Water.material "${OpenMW_BINARY_DIR}/resources/water/Examples-Water.material" COPYONLY)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/WaterNormal1.tga "${OpenMW_BINARY_DIR}/resources/water/WaterNormal1.tga" COPYONLY)
|
Binary file not shown.
@ -1,34 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<MyGUI type="Layout">
|
<MyGUI type="Layout">
|
||||||
<Widget type="Window" skin="MW_Dialog" layer="Windows" position="0 0 588 433" name="_Main">
|
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 588 433" name="_Main">
|
||||||
<!-- HEADER -->
|
|
||||||
<Widget type="TextBox" skin="HeaderText" position="0 0 588 18" name="NpcName" align="ALIGN_LEFT ALIGN_TOP">
|
|
||||||
<Property key="Caption" value="Name"/>
|
|
||||||
<Property key="TextAlign" value="ALIGN_CENTER"/>
|
|
||||||
</Widget>
|
|
||||||
|
|
||||||
<!-- The Dialogue history -->
|
<!-- The Dialogue history -->
|
||||||
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="8 39 400 375" name="History" align="ALIGN_LEFT ALIGN_TOP STRETCH">
|
<Widget type="DialogueHistory" skin="MW_TextBoxEdit" position="8 8 415 381" name="History" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH">
|
||||||
<Property key="Static" value="true"/>
|
<Property key="Static" value="true"/>
|
||||||
<Property key="WordWrap" value="true"/>
|
<Property key="WordWrap" value="true"/>
|
||||||
<Property key="MultiLine" value="1" />
|
<Property key="MultiLine" value="1" />
|
||||||
<Property key="VisibleVScroll" value="1" />
|
<Property key="VisibleVScroll" value="1" />
|
||||||
<!-- invisible box for receiving mouse events -->
|
<!-- invisible box for receiving mouse events -->
|
||||||
<Widget type="Widget" skin="" position="0 0 400 375" name="EventBox" align="ALIGN_LEFT ALIGN_TOP STRETCH"/>
|
<Widget type="Widget" skin="" position="0 0 400 375" name="EventBox" align="ALIGN_LEFT ALIGN_TOP ALIGN_STRETCH"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<!-- The disposition bar-->
|
<!-- The disposition bar-->
|
||||||
<Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="432 39 132 18"
|
<Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="432 8 132 18"
|
||||||
align="Right Top" name="Disposition">
|
align="Right Top" name="Disposition">
|
||||||
<Widget type="EditBox" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>
|
<Widget type="EditBox" skin="MW_DispositionEdit" position_real = "0.25 0 0.5 1" name = "DispositionText"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
<!-- The list of topics -->
|
<!-- The list of topics -->
|
||||||
<Widget type="ListBox" skin="MW_List" position="432 62 132 318" name="TopicsList">
|
<Widget type="ListBox" skin="MW_List" position="432 31 132 328" name="TopicsList" align="Right VStretch">
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|
||||||
<!-- The Goodbye button -->
|
<!-- The Goodbye button -->
|
||||||
<Widget type="Button" skin="MW_Button" position="432 387 132 23" name="ByeButton">
|
<Widget type="Button" skin="MW_Button" position="432 366 132 23" name="ByeButton" align="Right Bottom">
|
||||||
<Property key="Caption" value="Goodbye"/>
|
<Property key="Caption" value="Goodbye"/>
|
||||||
</Widget>
|
</Widget>
|
||||||
</Widget>
|
</Widget>
|
||||||
|
@ -294,7 +294,7 @@
|
|||||||
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
<Property key="TextAlign" value = "ALIGN_CENTER" />
|
||||||
<Property key="TextColour" value = "0.8 0.8 0.8" />
|
<Property key="TextColour" value = "0.8 0.8 0.8" />
|
||||||
|
|
||||||
<Child type="Widget" skin="DialogBG" offset = "4 4 248 46" align = "ALIGN_STRETCH" name = "Client"/>
|
<Child type="Widget" skin="BlackBG" offset = "4 4 248 46" align = "ALIGN_STRETCH" name = "Client"/>
|
||||||
|
|
||||||
<!-- Outer borders -->
|
<!-- Outer borders -->
|
||||||
<Child type="Widget" skin="DB_T" offset="4 0 248 4" align="ALIGN_TOP ALIGN_HSTRETCH" name="Border">
|
<Child type="Widget" skin="DB_T" offset="4 0 248 4" align="ALIGN_TOP ALIGN_HSTRETCH" name="Border">
|
||||||
|
116
files/water/Example_Fresnel.cg
Normal file
116
files/water/Example_Fresnel.cg
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Vertex program for fresnel reflections / refractions
|
||||||
|
void main_vp(
|
||||||
|
float4 pos : POSITION,
|
||||||
|
float4 normal : NORMAL,
|
||||||
|
float2 tex : TEXCOORD0,
|
||||||
|
|
||||||
|
out float4 oPos : POSITION,
|
||||||
|
out float3 noiseCoord : TEXCOORD0,
|
||||||
|
out float4 projectionCoord : TEXCOORD1,
|
||||||
|
out float3 oEyeDir : TEXCOORD2,
|
||||||
|
out float3 oNormal : TEXCOORD3,
|
||||||
|
|
||||||
|
uniform float4x4 worldViewProjMatrix,
|
||||||
|
uniform float3 eyePosition, // object space
|
||||||
|
uniform float timeVal,
|
||||||
|
uniform float scale, // the amount to scale the noise texture by
|
||||||
|
uniform float scroll, // the amount by which to scroll the noise
|
||||||
|
uniform float noise // the noise perturb as a factor of the time
|
||||||
|
)
|
||||||
|
{
|
||||||
|
oPos = mul(worldViewProjMatrix, pos);
|
||||||
|
// Projective texture coordinates, adjust for mapping
|
||||||
|
float4x4 scalemat = float4x4(0.5, 0, 0, 0.5,
|
||||||
|
0,-0.5, 0, 0.5,
|
||||||
|
0, 0, 0.5, 0.5,
|
||||||
|
0, 0, 0, 1);
|
||||||
|
projectionCoord = mul(scalemat, oPos);
|
||||||
|
// Noise map coords
|
||||||
|
noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
|
||||||
|
noiseCoord.z = noise * timeVal;
|
||||||
|
|
||||||
|
oEyeDir = normalize(pos.xyz - eyePosition);
|
||||||
|
oNormal = normal.rgb;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragment program for distorting a texture using a 3D noise texture
|
||||||
|
void main_fp(
|
||||||
|
float3 noiseCoord : TEXCOORD0,
|
||||||
|
float4 projectionCoord : TEXCOORD1,
|
||||||
|
float3 eyeDir : TEXCOORD2,
|
||||||
|
float3 normal : TEXCOORD3,
|
||||||
|
|
||||||
|
out float4 col : COLOR,
|
||||||
|
|
||||||
|
uniform float4 tintColour,
|
||||||
|
uniform float noiseScale,
|
||||||
|
uniform float fresnelBias,
|
||||||
|
uniform float fresnelScale,
|
||||||
|
uniform float fresnelPower,
|
||||||
|
uniform sampler2D waterTex : register(s0),
|
||||||
|
uniform sampler2D noiseMap : register(s1),
|
||||||
|
uniform sampler2D reflectMap : register(s2),
|
||||||
|
uniform sampler2D refractMap : register(s3)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Do the tex projection manually so we can distort _after_
|
||||||
|
float2 final = projectionCoord.xy / projectionCoord.w;
|
||||||
|
|
||||||
|
// Noise
|
||||||
|
float3 noiseNormal = (tex2D(noiseMap, (noiseCoord.xy / 5)).rgb - 0.5).rbg * noiseScale;
|
||||||
|
final += noiseNormal.xz;
|
||||||
|
|
||||||
|
// Fresnel
|
||||||
|
//normal = normalize(normal + noiseNormal.xz);
|
||||||
|
float fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);
|
||||||
|
|
||||||
|
// Reflection / refraction
|
||||||
|
float4 reflectionColour = tex2D(reflectMap, final);
|
||||||
|
float4 refractionColour = tex2D(refractMap, final) + tintColour;
|
||||||
|
|
||||||
|
// Final colour
|
||||||
|
col = lerp(refractionColour, reflectionColour, fresnel) * tex2D(waterTex, noiseNormal) / 3 ;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Old version to match ATI PS 1.3 implementation
|
||||||
|
void main_vp_old(
|
||||||
|
float4 pos : POSITION,
|
||||||
|
float4 normal : NORMAL,
|
||||||
|
float2 tex : TEXCOORD0,
|
||||||
|
|
||||||
|
out float4 oPos : POSITION,
|
||||||
|
out float fresnel : COLOR,
|
||||||
|
out float3 noiseCoord : TEXCOORD0,
|
||||||
|
out float4 projectionCoord : TEXCOORD1,
|
||||||
|
|
||||||
|
uniform float4x4 worldViewProjMatrix,
|
||||||
|
uniform float3 eyePosition, // object space
|
||||||
|
uniform float fresnelBias,
|
||||||
|
uniform float fresnelScale,
|
||||||
|
uniform float fresnelPower,
|
||||||
|
uniform float timeVal,
|
||||||
|
uniform float scale, // the amount to scale the noise texture by
|
||||||
|
uniform float scroll, // the amount by which to scroll the noise
|
||||||
|
uniform float noise // the noise perturb as a factor of the time
|
||||||
|
)
|
||||||
|
{
|
||||||
|
oPos = mul(worldViewProjMatrix, pos);
|
||||||
|
// Projective texture coordinates, adjust for mapping
|
||||||
|
float4x4 scalemat = float4x4(0.5, 0, 0, 0.5,
|
||||||
|
0,-0.5, 0, 0.5,
|
||||||
|
0, 0, 0.5, 0.5,
|
||||||
|
0, 0, 0, 1);
|
||||||
|
projectionCoord = mul(scalemat, oPos);
|
||||||
|
// Noise map coords
|
||||||
|
noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
|
||||||
|
noiseCoord.z = noise * timeVal;
|
||||||
|
|
||||||
|
// calc fresnel factor (reflection coefficient)
|
||||||
|
float3 eyeDir = normalize(pos.xyz - eyePosition);
|
||||||
|
fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);
|
||||||
|
|
||||||
|
}
|
72
files/water/Example_FresnelPS.asm
Normal file
72
files/water/Example_FresnelPS.asm
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
ps.1.4
|
||||||
|
// conversion from Cg generated ARB_fragment_program to ps.1.4 by NFZ
|
||||||
|
// command line args: -profile arbfp1 -entry main_fp
|
||||||
|
// program main_fp
|
||||||
|
// c0 : distortionRange
|
||||||
|
// c1 : tintColour
|
||||||
|
// testure 0 : noiseMap
|
||||||
|
// texture 1 : reflectMap
|
||||||
|
// texture 2 : refractMap
|
||||||
|
// v0.x : fresnel
|
||||||
|
// t0.xyz : noiseCoord
|
||||||
|
// t1.xyw : projectionCoord
|
||||||
|
|
||||||
|
def c2, 2, 1, 0, 0
|
||||||
|
|
||||||
|
// Cg: distort.x = tex3D(noiseMap, noiseCoord).x;
|
||||||
|
// arbfp1: TEX R0.x, fragment.texcoord[0], texture[0], 3D;
|
||||||
|
// sample noise map using noiseCoord in TEX unit 0
|
||||||
|
|
||||||
|
texld r0, t0.xyz
|
||||||
|
|
||||||
|
// get projected texture coordinates from TEX coord 1
|
||||||
|
// will be used in phase 2
|
||||||
|
|
||||||
|
texcrd r1.xy, t1_dw.xyw
|
||||||
|
mov r1.z, c2.y
|
||||||
|
|
||||||
|
// Cg: distort.y = tex3D(noiseMap, noiseCoord + yoffset).x;
|
||||||
|
// arbfp1: ADD R1.xyz, fragment.texcoord[0], c1;
|
||||||
|
// arbfp1: TEX R1.x, R1, texture[0], 3D;
|
||||||
|
// arbfp1: MOV R0.y, R1.x;
|
||||||
|
|
||||||
|
// Cg: distort = (distort * 2 - 1) * distortionRange;
|
||||||
|
// arbfp1: MAD R0.xy, R0, c0.x, -c0.y;
|
||||||
|
// arbfp1: MUL R0.xy, R0, u0.x;
|
||||||
|
// (distort * 2 - 1) same as 2*(distort -.5) so use _bx2
|
||||||
|
|
||||||
|
|
||||||
|
// Cg: final = projectionCoord.xy / projectionCoord.w;
|
||||||
|
// Cg: final += distort;
|
||||||
|
// arbfp1: RCP R0.w, fragment.texcoord[1].w;
|
||||||
|
// arbfp1: MAD R0.xy, fragment.texcoord[1], R0.w, R0;
|
||||||
|
// final = (distort * projectionCoord.w) + projectionCoord.xy
|
||||||
|
// for ps.1.4 have to re-arrange things a bit to perturb projected texture coordinates
|
||||||
|
|
||||||
|
mad r0.xyz, r0_bx2, c0.x, r1
|
||||||
|
|
||||||
|
phase
|
||||||
|
|
||||||
|
// do dependant texture reads
|
||||||
|
// Cg: reflectionColour = tex2D(reflectMap, final);
|
||||||
|
// arbfp1: TEX R0, R0, texture[1], 2D;
|
||||||
|
// sampe reflectMap using dependant read : texunit 1
|
||||||
|
|
||||||
|
texld r1, r0.xyz
|
||||||
|
|
||||||
|
// Cg: refractionColour = tex2D(refractMap, final) + tintColour;
|
||||||
|
// arbfp1: TEX R1, R0, texture[2], 2D;
|
||||||
|
// sample refractMap : texunit 2
|
||||||
|
|
||||||
|
texld r2, r0.xyz
|
||||||
|
|
||||||
|
// adding tintColour that is in global c1
|
||||||
|
// arbfp1: ADD R1, R1, u1;
|
||||||
|
|
||||||
|
add r2, r2, c1
|
||||||
|
|
||||||
|
// Cg: col = lerp(refractionColour, reflectionColour, fresnel);
|
||||||
|
// arbfp1: ADD R0, R0, -R1;
|
||||||
|
// arbfp1: MAD result.color, fragment.color.primary.x, R0, R1;
|
||||||
|
|
||||||
|
lrp r0, v0.x, r1, r2
|
149
files/water/Examples-Water.material
Normal file
149
files/water/Examples-Water.material
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
|
||||||
|
vertex_program Water/GlassVP cg
|
||||||
|
{
|
||||||
|
source GlassVP.cg
|
||||||
|
entry_point glass_vp
|
||||||
|
profiles vs_1_1 arbvp1
|
||||||
|
|
||||||
|
default_params
|
||||||
|
{
|
||||||
|
param_named_auto worldViewProj worldviewproj_matrix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fragment_program Water/GlassFP cg
|
||||||
|
{
|
||||||
|
source GlassFP.cg
|
||||||
|
entry_point main_ps
|
||||||
|
profiles ps_2_0 arbfp1
|
||||||
|
}
|
||||||
|
|
||||||
|
material Water/Compositor
|
||||||
|
{
|
||||||
|
technique
|
||||||
|
{
|
||||||
|
pass
|
||||||
|
{
|
||||||
|
depth_check off
|
||||||
|
vertex_program_ref Water/GlassVP
|
||||||
|
{
|
||||||
|
param_named_auto timeVal time 0.25
|
||||||
|
param_named scale float 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment_program_ref Water/GlassFP
|
||||||
|
{
|
||||||
|
param_named tintColour float4 0 0.35 0.35 1
|
||||||
|
}
|
||||||
|
|
||||||
|
texture_unit RT
|
||||||
|
{
|
||||||
|
tex_coord_set 0
|
||||||
|
tex_address_mode clamp
|
||||||
|
filtering linear linear linear
|
||||||
|
}
|
||||||
|
|
||||||
|
texture_unit
|
||||||
|
{
|
||||||
|
texture WaterNormal1.tga 2d
|
||||||
|
tex_coord_set 1
|
||||||
|
//tex_address_mode clamp
|
||||||
|
filtering linear linear linear
|
||||||
|
}
|
||||||
|
texture_unit
|
||||||
|
{
|
||||||
|
texture caustic_0.png 2d
|
||||||
|
tex_coord_set 2
|
||||||
|
//tex_address_mode clamp
|
||||||
|
filtering linear linear linear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vertex_program Water/RefractReflectVP cg
|
||||||
|
{
|
||||||
|
source Example_Fresnel.cg
|
||||||
|
entry_point main_vp
|
||||||
|
profiles vs_1_1 arbvp1
|
||||||
|
}
|
||||||
|
vertex_program Water/RefractReflectVPold cg
|
||||||
|
{
|
||||||
|
source Example_Fresnel.cg
|
||||||
|
entry_point main_vp_old
|
||||||
|
profiles vs_1_1 arbvp1
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment_program Water/RefractReflectFP cg
|
||||||
|
{
|
||||||
|
source Example_Fresnel.cg
|
||||||
|
entry_point main_fp
|
||||||
|
// sorry, ps_1_1 and fp20 can't do this
|
||||||
|
profiles ps_2_0 arbfp1
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment_program Water/RefractReflectPS asm
|
||||||
|
{
|
||||||
|
source Example_FresnelPS.asm
|
||||||
|
// sorry, only for ps_1_4 :)
|
||||||
|
syntax ps_1_4
|
||||||
|
|
||||||
|
}
|
||||||
|
material Examples/Water0
|
||||||
|
{
|
||||||
|
|
||||||
|
technique
|
||||||
|
{
|
||||||
|
pass
|
||||||
|
{
|
||||||
|
//
|
||||||
|
|
||||||
|
depth_write off
|
||||||
|
vertex_program_ref Water/RefractReflectVP
|
||||||
|
{
|
||||||
|
param_named_auto worldViewProjMatrix worldviewproj_matrix
|
||||||
|
param_named_auto eyePosition camera_position_object_space
|
||||||
|
param_named_auto timeVal time 0.15
|
||||||
|
param_named scroll float 1
|
||||||
|
param_named scale float 1
|
||||||
|
param_named noise float 1
|
||||||
|
// scroll and noisePos will need updating per frame
|
||||||
|
}
|
||||||
|
fragment_program_ref Water/RefractReflectFP
|
||||||
|
{
|
||||||
|
param_named fresnelBias float -0.1
|
||||||
|
param_named fresnelScale float 0.8
|
||||||
|
param_named fresnelPower float 20
|
||||||
|
param_named tintColour float4 1 1 1 1
|
||||||
|
param_named noiseScale float 0.05
|
||||||
|
}
|
||||||
|
// Water
|
||||||
|
scene_blend alpha_blend
|
||||||
|
texture_unit
|
||||||
|
{
|
||||||
|
|
||||||
|
// Water texture
|
||||||
|
texture Water02.jpg
|
||||||
|
// min / mag filtering, no mip
|
||||||
|
filtering linear linear none
|
||||||
|
alpha_op_ex source1 src_manual src_current 0.9
|
||||||
|
|
||||||
|
}
|
||||||
|
// Noise
|
||||||
|
texture_unit
|
||||||
|
{
|
||||||
|
alpha_op_ex source1 src_manual src_current 0.9
|
||||||
|
// Perlin noise volume
|
||||||
|
texture waves2.dds
|
||||||
|
// min / mag filtering, no mip
|
||||||
|
filtering linear linear none
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
15
files/water/GlassFP.cg
Normal file
15
files/water/GlassFP.cg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
sampler RT : register(s0);
|
||||||
|
sampler NormalMap : register(s1);
|
||||||
|
sampler CausticMap : register(s2);
|
||||||
|
|
||||||
|
float4 main_ps(float2 iTexCoord : TEXCOORD0,
|
||||||
|
float3 noiseCoord : TEXCOORD1,
|
||||||
|
uniform float4 tintColour) : COLOR
|
||||||
|
{
|
||||||
|
float4 normal = tex2D(NormalMap, noiseCoord);
|
||||||
|
|
||||||
|
|
||||||
|
return tex2D(RT, iTexCoord + normal.xy * 0.05) +
|
||||||
|
(tex2D(CausticMap, noiseCoord) / 5) +
|
||||||
|
tintColour ;
|
||||||
|
}
|
24
files/water/GlassVP.cg
Normal file
24
files/water/GlassVP.cg
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
void glass_vp
|
||||||
|
(
|
||||||
|
in float4 inPos : POSITION,
|
||||||
|
|
||||||
|
out float4 pos : POSITION,
|
||||||
|
out float2 uv0 : TEXCOORD0,
|
||||||
|
out float4 noiseCoord : TEXCOORD1,
|
||||||
|
|
||||||
|
uniform float4x4 worldViewProj,
|
||||||
|
uniform float timeVal,
|
||||||
|
uniform float scale
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Use standardise transform, so work accord with render system specific (RS depth, requires texture flipping, etc)
|
||||||
|
pos = mul(worldViewProj, inPos);
|
||||||
|
|
||||||
|
// The input positions adjusted by texel offsets, so clean up inaccuracies
|
||||||
|
inPos.xy = sign(inPos.xy);
|
||||||
|
|
||||||
|
// Convert to image-space
|
||||||
|
uv0 = (float2(inPos.x, -inPos.y) + 1.0f) * 0.5f;
|
||||||
|
noiseCoord = (pos + timeVal) * scale;
|
||||||
|
}
|
||||||
|
|
BIN
files/water/Water02.jpg
Normal file
BIN
files/water/Water02.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
BIN
files/water/WaterNormal1.tga
Normal file
BIN
files/water/WaterNormal1.tga
Normal file
Binary file not shown.
After Width: | Height: | Size: 192 KiB |
BIN
files/water/caustic_0.png
Normal file
BIN
files/water/caustic_0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
files/water/perlinvolume.dds
Normal file
BIN
files/water/perlinvolume.dds
Normal file
Binary file not shown.
21
files/water/water.compositor
Normal file
21
files/water/water.compositor
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
compositor Water
|
||||||
|
{
|
||||||
|
technique
|
||||||
|
{
|
||||||
|
texture rt0 target_width target_height PF_R8G8B8
|
||||||
|
|
||||||
|
target rt0 { input previous }
|
||||||
|
|
||||||
|
target_output
|
||||||
|
{
|
||||||
|
// Start with clear output
|
||||||
|
input none
|
||||||
|
|
||||||
|
pass render_quad
|
||||||
|
{
|
||||||
|
material Water/Compositor
|
||||||
|
input 0 rt0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
files/water/waves2.dds
Normal file
BIN
files/water/waves2.dds
Normal file
Binary file not shown.
@ -63,17 +63,17 @@ size_t BulletShape::calculateSize() const
|
|||||||
|
|
||||||
|
|
||||||
//=============================================================================================================
|
//=============================================================================================================
|
||||||
template<> BulletShapeManager *Ogre::Singleton<BulletShapeManager>::ms_Singleton = 0;
|
template<> BulletShapeManager *Ogre::Singleton<BulletShapeManager>::msSingleton = 0;
|
||||||
|
|
||||||
BulletShapeManager *BulletShapeManager::getSingletonPtr()
|
BulletShapeManager *BulletShapeManager::getSingletonPtr()
|
||||||
{
|
{
|
||||||
return ms_Singleton;
|
return msSingleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
BulletShapeManager &BulletShapeManager::getSingleton()
|
BulletShapeManager &BulletShapeManager::getSingleton()
|
||||||
{
|
{
|
||||||
assert(ms_Singleton);
|
assert(msSingleton);
|
||||||
return(*ms_Singleton);
|
return(*msSingleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
BulletShapeManager::BulletShapeManager()
|
BulletShapeManager::BulletShapeManager()
|
||||||
|
@ -151,7 +151,8 @@ namespace Physic
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader)
|
PhysicEngine::PhysicEngine(BulletShapeLoader* shapeLoader) :
|
||||||
|
mDebugActive(0)
|
||||||
{
|
{
|
||||||
// Set up the collision configuration and dispatcher
|
// Set up the collision configuration and dispatcher
|
||||||
collisionConfiguration = new btDefaultCollisionConfiguration();
|
collisionConfiguration = new btDefaultCollisionConfiguration();
|
||||||
@ -203,6 +204,13 @@ namespace Physic
|
|||||||
createDebugRendering();
|
createDebugRendering();
|
||||||
}
|
}
|
||||||
mDebugDrawer->setDebugMode(mode);
|
mDebugDrawer->setDebugMode(mode);
|
||||||
|
mDebugActive = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PhysicEngine::toggleDebugRendering()
|
||||||
|
{
|
||||||
|
setDebugRenderingMode(!mDebugActive);
|
||||||
|
return mDebugActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicEngine::~PhysicEngine()
|
PhysicEngine::~PhysicEngine()
|
||||||
@ -418,4 +426,35 @@ namespace Physic
|
|||||||
|
|
||||||
return std::pair<std::string,float>(name,d);
|
return std::pair<std::string,float>(name,d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector< std::pair<float, std::string> > PhysicEngine::rayTest2(btVector3& from, btVector3& to)
|
||||||
|
{
|
||||||
|
MyRayResultCallback resultCallback1;
|
||||||
|
resultCallback1.m_collisionFilterMask = COL_WORLD;
|
||||||
|
dynamicsWorld->rayTest(from, to, resultCallback1);
|
||||||
|
std::vector< std::pair<float, btCollisionObject*> > results = resultCallback1.results;
|
||||||
|
|
||||||
|
MyRayResultCallback resultCallback2;
|
||||||
|
resultCallback2.m_collisionFilterMask = COL_ACTOR_INTERNAL|COL_ACTOR_EXTERNAL;
|
||||||
|
dynamicsWorld->rayTest(from, to, resultCallback2);
|
||||||
|
std::vector< std::pair<float, btCollisionObject*> > actorResults = resultCallback2.results;
|
||||||
|
|
||||||
|
std::vector< std::pair<float, std::string> > results2;
|
||||||
|
|
||||||
|
for (std::vector< std::pair<float, btCollisionObject*> >::iterator it=results.begin();
|
||||||
|
it != results.end(); ++it)
|
||||||
|
{
|
||||||
|
results2.push_back( std::make_pair( (*it).first, static_cast<RigidBody&>(*(*it).second).mName ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector< std::pair<float, btCollisionObject*> >::iterator it=actorResults.begin();
|
||||||
|
it != actorResults.end(); ++it)
|
||||||
|
{
|
||||||
|
results2.push_back( std::make_pair( (*it).first, static_cast<PairCachingGhostObject&>(*(*it).second).mName ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(results2.begin(), results2.end(), MyRayResultCallback::cmp);
|
||||||
|
|
||||||
|
return results2;
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
@ -199,11 +199,18 @@ namespace Physic
|
|||||||
*/
|
*/
|
||||||
void setDebugRenderingMode(int mode);
|
void setDebugRenderingMode(int mode);
|
||||||
|
|
||||||
|
bool toggleDebugRendering();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
|
* Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
|
||||||
*/
|
*/
|
||||||
std::pair<std::string,float> rayTest(btVector3& from,btVector3& to);
|
std::pair<std::string,float> rayTest(btVector3& from,btVector3& to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all objects hit by a ray.
|
||||||
|
*/
|
||||||
|
std::vector< std::pair<float, std::string> > rayTest2(btVector3& from, btVector3& to);
|
||||||
|
|
||||||
//event list of non player object
|
//event list of non player object
|
||||||
std::list<PhysicEvent> NPEventList;
|
std::list<PhysicEvent> NPEventList;
|
||||||
|
|
||||||
@ -230,6 +237,26 @@ namespace Physic
|
|||||||
//debug rendering
|
//debug rendering
|
||||||
BtOgre::DebugDrawer* mDebugDrawer;
|
BtOgre::DebugDrawer* mDebugDrawer;
|
||||||
bool isDebugCreated;
|
bool isDebugCreated;
|
||||||
|
bool mDebugActive;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct MyRayResultCallback : public btCollisionWorld::RayResultCallback
|
||||||
|
{
|
||||||
|
virtual btScalar addSingleResult( btCollisionWorld::LocalRayResult& rayResult, bool bNormalInWorldSpace)
|
||||||
|
{
|
||||||
|
results.push_back( std::make_pair(rayResult.m_hitFraction, rayResult.m_collisionObject) );
|
||||||
|
return rayResult.m_hitFraction;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cmp( const std::pair<float, std::string>& i, const std::pair<float, std::string>& j )
|
||||||
|
{
|
||||||
|
if( i.first > j.first ) return false;
|
||||||
|
if( j.first > i.first ) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector < std::pair<float, btCollisionObject*> > results;
|
||||||
};
|
};
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
@ -148,6 +148,7 @@ Bug #207: Ogre.log not written
|
|||||||
Bug #209: Sounds do not play
|
Bug #209: Sounds do not play
|
||||||
Bug #210: Ogre crash at Dren plantation
|
Bug #210: Ogre crash at Dren plantation
|
||||||
Bug #214: Unsupported file format version
|
Bug #214: Unsupported file format version
|
||||||
|
Bug #222: Launcher is writing openmw.cfg file to wrong location
|
||||||
Feature #9: NPC Dialogue Window
|
Feature #9: NPC Dialogue Window
|
||||||
Feature #16/42: New sky/weather implementation
|
Feature #16/42: New sky/weather implementation
|
||||||
Feature #40: Fading
|
Feature #40: Fading
|
||||||
|
Loading…
x
Reference in New Issue
Block a user