forked from teamnwah/openmw-tes3coop
Merge branch 'master' of https://github.com/zinnschlag/openmw.git into inventoryGUI
Conflicts: apps/openmw/CMakeLists.txt
This commit is contained in:
commit
a38db39e88
274 changed files with 14152 additions and 6076 deletions
123
Bitstream Vera License.txt
Normal file
123
Bitstream Vera License.txt
Normal file
|
@ -0,0 +1,123 @@
|
|||
Bitstream Vera Fonts Copyright
|
||||
|
||||
The fonts have a generous copyright, allowing derivative works (as
|
||||
long as "Bitstream" or "Vera" are not in the names), and full
|
||||
redistribution (so long as they are not *sold* by themselves). They
|
||||
can be be bundled, redistributed and sold with any software.
|
||||
|
||||
The fonts are distributed under the following copyright:
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||
Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute
|
||||
the Font Software, including without limitation the rights to use,
|
||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||
Software, and to permit persons to whom the Font Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font
|
||||
Software without prior written authorization from the Gnome Foundation
|
||||
or Bitstream Inc., respectively. For further information, contact:
|
||||
fonts at gnome dot org.
|
||||
|
||||
Copyright FAQ
|
||||
=============
|
||||
|
||||
1. I don't understand the resale restriction... What gives?
|
||||
|
||||
Bitstream is giving away these fonts, but wishes to ensure its
|
||||
competitors can't just drop the fonts as is into a font sale system
|
||||
and sell them as is. It seems fair that if Bitstream can't make money
|
||||
from the Bitstream Vera fonts, their competitors should not be able to
|
||||
do so either. You can sell the fonts as part of any software package,
|
||||
however.
|
||||
|
||||
2. I want to package these fonts separately for distribution and
|
||||
sale as part of a larger software package or system. Can I do so?
|
||||
|
||||
Yes. A RPM or Debian package is a "larger software package" to begin
|
||||
with, and you aren't selling them independently by themselves.
|
||||
See 1. above.
|
||||
|
||||
3. Are derivative works allowed?
|
||||
Yes!
|
||||
|
||||
4. Can I change or add to the font(s)?
|
||||
Yes, but you must change the name(s) of the font(s).
|
||||
|
||||
5. Under what terms are derivative works allowed?
|
||||
|
||||
You must change the name(s) of the fonts. This is to ensure the
|
||||
quality of the fonts, both to protect Bitstream and Gnome. We want to
|
||||
ensure that if an application has opened a font specifically of these
|
||||
names, it gets what it expects (though of course, using fontconfig,
|
||||
substitutions could still could have occurred during font
|
||||
opening). You must include the Bitstream copyright. Additional
|
||||
copyrights can be added, as per copyright law. Happy Font Hacking!
|
||||
|
||||
6. If I have improvements for Bitstream Vera, is it possible they might get
|
||||
adopted in future versions?
|
||||
|
||||
Yes. The contract between the Gnome Foundation and Bitstream has
|
||||
provisions for working with Bitstream to ensure quality additions to
|
||||
the Bitstream Vera font family. Please contact us if you have such
|
||||
additions. Note, that in general, we will want such additions for the
|
||||
entire family, not just a single font, and that you'll have to keep
|
||||
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
|
||||
glyphs to the font, they must be stylistically in keeping with Vera's
|
||||
design. Vera cannot become a "ransom note" font. Jim Lyles will be
|
||||
providing a document describing the design elements used in Vera, as a
|
||||
guide and aid for people interested in contributing to Vera.
|
||||
|
||||
7. I want to sell a software package that uses these fonts: Can I do so?
|
||||
|
||||
Sure. Bundle the fonts with your software and sell your software
|
||||
with the fonts. That is the intent of the copyright.
|
||||
|
||||
8. If applications have built the names "Bitstream Vera" into them,
|
||||
can I override this somehow to use fonts of my choosing?
|
||||
|
||||
This depends on exact details of the software. Most open source
|
||||
systems and software (e.g., Gnome, KDE, etc.) are now converting to
|
||||
use fontconfig (see www.fontconfig.org) to handle font configuration,
|
||||
selection and substitution; it has provisions for overriding font
|
||||
names and subsituting alternatives. An example is provided by the
|
||||
supplied local.conf file, which chooses the family Bitstream Vera for
|
||||
"sans", "serif" and "monospace". Other software (e.g., the XFree86
|
||||
core server) has other mechanisms for font substitution.
|
139
CMakeLists.txt
139
CMakeLists.txt
|
@ -4,9 +4,6 @@ if (APPLE)
|
|||
set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app")
|
||||
|
||||
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}")
|
||||
|
||||
# using 10.6 sdk
|
||||
set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk")
|
||||
endif (APPLE)
|
||||
|
||||
# Macros
|
||||
|
@ -27,11 +24,17 @@ set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VE
|
|||
|
||||
configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp")
|
||||
|
||||
option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE)
|
||||
option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE)
|
||||
|
||||
# Apps and tools
|
||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||
option(BUILD_LAUNCHER "build Launcher" ON)
|
||||
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
||||
|
||||
# Sound source selection
|
||||
option(USE_AUDIERE "use Audiere for sound" OFF)
|
||||
option(USE_FFMPEG "use ffmpeg for sound" OFF)
|
||||
option(USE_AUDIERE "use audiere for sound" OFF)
|
||||
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
|
||||
|
||||
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
||||
|
@ -99,6 +102,7 @@ set(OENGINE_OGRE
|
|||
${LIBDIR}/openengine/ogre/renderer.cpp
|
||||
${LIBDIR}/openengine/ogre/mouselook.cpp
|
||||
${LIBDIR}/openengine/ogre/fader.cpp
|
||||
${LIBDIR}/openengine/ogre/imagerotate.cpp
|
||||
)
|
||||
set(OENGINE_GUI
|
||||
${LIBDIR}/openengine/gui/events.cpp
|
||||
|
@ -120,54 +124,41 @@ set(OENGINE_BULLET
|
|||
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
|
||||
)
|
||||
|
||||
# Sound setup
|
||||
if (USE_AUDIERE)
|
||||
set(MANGLE_SOUND_OUTPUT
|
||||
${LIBDIR}/mangle/sound/sources/audiere_source.cpp
|
||||
${LIBDIR}/mangle/sound/sources/sample_reader.cpp
|
||||
${LIBDIR}/mangle/stream/clients/audiere_file.cpp)
|
||||
find_package(Audiere REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${AUDIERE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${AUDIERE_LIBRARY})
|
||||
set(SOUND_DEFINE -DOPENMW_USE_AUDIERE)
|
||||
endif (USE_AUDIERE)
|
||||
|
||||
if (USE_FFMPEG)
|
||||
set(MANGLE_SOUND_OUTPUT
|
||||
${LIBDIR}/mangle/sound/sources/ffmpeg_source.cpp)
|
||||
find_package(FFMPEG REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${FFMPEG_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${FFMPEG_LIBRARIES})
|
||||
set(SOUND_DEFINE -DOPENMW_USE_FFMPEG)
|
||||
endif (USE_FFMPEG)
|
||||
|
||||
if (USE_MPG123)
|
||||
set(MANGLE_SOUND_OUTPUT
|
||||
${LIBDIR}/mangle/sound/sources/mpg123_source.cpp
|
||||
${LIBDIR}/mangle/sound/sources/libsndfile.cpp
|
||||
${LIBDIR}/mangle/sound/sources/sample_reader.cpp)
|
||||
find_package(MPG123 REQUIRED)
|
||||
find_package(SNDFILE REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
|
||||
set(SOUND_DEFINE -DOPENMW_USE_MPG123)
|
||||
endif (USE_MPG123)
|
||||
|
||||
set(OENGINE_SOUND
|
||||
# Mangle and OEngine sound files are sort of intertwined, so put
|
||||
# them together here
|
||||
${LIBDIR}/openengine/sound/sndmanager.cpp
|
||||
${LIBDIR}/mangle/sound/outputs/openal_out.cpp
|
||||
${MANGLE_SOUND_OUTPUT}
|
||||
)
|
||||
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_SOUND} ${OENGINE_BULLET})
|
||||
set(OENGINE_ALL ${OENGINE_OGRE} ${OENGINE_GUI} ${OENGINE_BULLET})
|
||||
source_group(libs\\openengine FILES ${OENGINE_ALL})
|
||||
|
||||
set(OPENMW_LIBS ${MANGLE_ALL} ${OENGINE_ALL})
|
||||
set(OPENMW_LIBS_HEADER)
|
||||
|
||||
# Sound setup
|
||||
set(SOUND_INPUT_INCLUDES "")
|
||||
set(SOUND_INPUT_LIBRARY "")
|
||||
set(SOUND_DEFINE "")
|
||||
if (USE_FFMPEG)
|
||||
find_package(FFMPEG REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG)
|
||||
endif (USE_FFMPEG)
|
||||
|
||||
if (USE_AUDIERE)
|
||||
find_package(Audiere REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE)
|
||||
endif (USE_AUDIERE)
|
||||
|
||||
if (USE_MPG123)
|
||||
find_package(MPG123 REQUIRED)
|
||||
find_package(SNDFILE REQUIRED)
|
||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
|
||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
|
||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
|
||||
endif (USE_MPG123)
|
||||
|
||||
# Platform specific
|
||||
if (WIN32)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(PLATFORM_INCLUDE_DIR "platform")
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
else (WIN32)
|
||||
|
@ -185,7 +176,7 @@ endif (APPLE)
|
|||
|
||||
# Dependencies
|
||||
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package (Threads)
|
||||
endif()
|
||||
|
@ -206,6 +197,7 @@ ENDIF(WIN32)
|
|||
ENDIF(OGRE_STATIC)
|
||||
include_directories("."
|
||||
${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE ${OGRE_PLUGIN_INCLUDE_DIRS}
|
||||
${OGRE_Terrain_INCLUDE_DIR}
|
||||
${OIS_INCLUDE_DIRS} ${Boost_INCLUDE_DIR}
|
||||
${PLATFORM_INCLUDE_DIR}
|
||||
${MYGUI_INCLUDE_DIRS}
|
||||
|
@ -225,6 +217,7 @@ if(APPLE)
|
|||
"Plugin_ParticleFX")
|
||||
endif(APPLE)
|
||||
|
||||
add_subdirectory( files/)
|
||||
add_subdirectory( files/mygui )
|
||||
|
||||
# Specify build paths
|
||||
|
@ -237,6 +230,12 @@ endif (APPLE)
|
|||
|
||||
# Other files
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
|
||||
"${OpenMW_BINARY_DIR}/settings-default.cfg")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/transparency-overrides.cfg
|
||||
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
|
||||
"${OpenMW_BINARY_DIR}/openmw.cfg")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
||||
|
@ -250,6 +249,9 @@ endif (WIN32)
|
|||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.linux
|
||||
"${OpenMW_BINARY_DIR}/plugins.cfg")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
||||
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
|
@ -263,8 +265,14 @@ if (APPLE)
|
|||
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
|
||||
|
||||
# prepare plugins
|
||||
if (${CMAKE_BUILD_TYPE} MATCHES "Release" OR
|
||||
${CMAKE_BUILD_TYPE} MATCHES "RelWithDebugInfo")
|
||||
if (${CMAKE_BUILD_TYPE} MATCHES "Release")
|
||||
set(OPENMW_RELEASE_BUILD 1)
|
||||
endif()
|
||||
if (${CMAKE_BUILD_TYPE} MATCHES "RelWithDebugInfo")
|
||||
set(OPENMW_RELEASE_BUILD 1)
|
||||
endif()
|
||||
|
||||
if (${OPENMW_RELEASE_BUILD})
|
||||
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL})
|
||||
else()
|
||||
set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG})
|
||||
|
@ -280,7 +288,16 @@ endif (APPLE)
|
|||
|
||||
# Compiler settings
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-unused-but-set-parameter -Wno-reorder)
|
||||
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder)
|
||||
|
||||
# Silence warnings in OGRE headers. Remove once OGRE got fixed!
|
||||
add_definitions (-Wno-ignored-qualifiers)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||
OUTPUT_VARIABLE GCC_VERSION)
|
||||
if ("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||
add_definitions (-Wno-unused-but-set-parameter)
|
||||
endif("${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||
endif (CMAKE_COMPILER_IS_GNUCC)
|
||||
|
||||
if(DPKG_PROGRAM)
|
||||
|
@ -298,10 +315,12 @@ if(DPKG_PROGRAM)
|
|||
endif()
|
||||
|
||||
#Install icon and desktop file
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/openmw.desktop" DESTINATION "share/applications/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "share/applications/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "share/pixmaps/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
|
||||
#Install global configuration files
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "../etc/openmw/" RENAME "openmw.cfg" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
|
||||
|
||||
|
@ -319,7 +338,7 @@ if(DPKG_PROGRAM)
|
|||
Data files from the original game is required to run it.")
|
||||
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
|
||||
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher")
|
||||
SET(CPACK_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_SECTION "Games")
|
||||
|
@ -340,6 +359,10 @@ if(WIN32)
|
|||
FILE(GLOB files "${OpenMW_BINARY_DIR}/Release/*.*")
|
||||
INSTALL(FILES ${files} DESTINATION ".")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
|
||||
INSTALL(FILES
|
||||
"${OpenMW_SOURCE_DIR}/readme.txt"
|
||||
"${OpenMW_BINARY_DIR}/settings-default.cfg"
|
||||
DESTINATION ".")
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
|
||||
|
||||
SET(CPACK_GENERATOR "NSIS")
|
||||
|
@ -349,7 +372,12 @@ if(WIN32)
|
|||
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO})
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;esmtool;Esmtool;omwlauncher;OpenMW Launcher")
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher")
|
||||
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
|
||||
SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
Delete \\\"$SMPROGRAMS\\\\$MUI_TEMP\\\\Readme.lnk\\\"
|
||||
")
|
||||
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
|
||||
SET(CPACK_RESOURCE_FILE_LICENSE "${OpenMW_SOURCE_DIR}/GPL3.txt")
|
||||
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||
|
@ -393,16 +421,18 @@ add_subdirectory (components)
|
|||
# Apps and tools
|
||||
add_subdirectory( apps/openmw )
|
||||
|
||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||
if (BUILD_ESMTOOL)
|
||||
add_subdirectory( apps/esmtool )
|
||||
endif()
|
||||
|
||||
option(BUILD_LAUNCHER "build Launcher inspector" ON)
|
||||
if (BUILD_LAUNCHER)
|
||||
add_subdirectory( apps/launcher )
|
||||
endif()
|
||||
|
||||
if (BUILD_MWINIIMPORTER)
|
||||
add_subdirectory( apps/mwiniimporter )
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (MSVC)
|
||||
if (USE_DEBUG_CONSOLE)
|
||||
|
@ -492,6 +522,7 @@ if (APPLE)
|
|||
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||
|
||||
install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||
install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
||||
|
|
93
OFL.txt
Normal file
93
OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright (c) 2010, 2011 Georg Duffner (http://www.georgduffner.at)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
@ -95,5 +95,5 @@ else()
|
|||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
|
||||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg")
|
||||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
|
||||
endif()
|
||||
|
|
|
@ -225,7 +225,7 @@ void DataFilesPage::setupDataFiles()
|
|||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(tr("<br><b>Could not find the Data Files location</b><br><br> \
|
||||
The directory containing the Data Files was not found.<br><br> \
|
||||
The directory containing the data files was not found.<br><br> \
|
||||
Press \"Browse...\" to specify the location manually.<br>"));
|
||||
|
||||
QAbstractButton *dirSelectButton =
|
||||
|
@ -1057,16 +1057,8 @@ void DataFilesPage::writeConfig(QString profile)
|
|||
return;
|
||||
}
|
||||
|
||||
// Prepare the OpenMW config
|
||||
QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "openmw.cfg").string());
|
||||
QFile file(config);
|
||||
|
||||
if (!file.exists()) {
|
||||
config = QString::fromStdString((mCfgMgr.getUserPath() / "openmw.cfg").string());
|
||||
}
|
||||
|
||||
// Open the config as a QFile
|
||||
file.setFileName(config);
|
||||
// Open the OpenMW config as a QFile
|
||||
QFile file(QString::fromStdString((mCfgMgr.getUserPath() / "openmw.cfg").string()));
|
||||
|
||||
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||
// File cannot be opened or created
|
||||
|
|
|
@ -45,9 +45,28 @@ MainDialog::MainDialog()
|
|||
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setMinimumSize(QSize(575, 575));
|
||||
|
||||
// Install the stylesheet font
|
||||
QFile file;
|
||||
QFontDatabase fontDatabase;
|
||||
|
||||
const QStringList fonts = fontDatabase.families();
|
||||
|
||||
// Check if the font is installed
|
||||
if (!fonts.contains("EB Garamond")) {
|
||||
|
||||
QString font = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
|
||||
file.setFileName(font);
|
||||
|
||||
if (!file.exists()) {
|
||||
font = QString::fromStdString((mCfgMgr.getLocalPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
|
||||
}
|
||||
|
||||
fontDatabase.addApplicationFont(font);
|
||||
}
|
||||
|
||||
// Load the stylesheet
|
||||
QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string());
|
||||
QFile file(config);
|
||||
file.setFileName(config);
|
||||
|
||||
if (!file.exists()) {
|
||||
file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string()));
|
||||
|
@ -175,6 +194,7 @@ void MainDialog::play()
|
|||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
QString game = dir.absoluteFilePath("openmw");
|
||||
QFile file(game);
|
||||
game = "\"" + game + "\"";
|
||||
#else
|
||||
QString game = "./openmw";
|
||||
QFile file(game);
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 50 KiB |
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
|
||||
)
|
||||
|
216
apps/mwiniimporter/importer.cpp
Normal file
216
apps/mwiniimporter/importer.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
#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" },
|
||||
{ "nosound", "General:Disable Audio" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
const char *fallback[] = {
|
||||
"Weather:Sunrise Time",
|
||||
"Weather:Sunset Time",
|
||||
0
|
||||
};
|
||||
|
||||
for(int i=0; map[i][0]; i++) {
|
||||
mMergeMap.insert(std::make_pair<std::string, std::string>(map[i][0], map[i][1]));
|
||||
}
|
||||
|
||||
for(int i=0; fallback[i]; i++) {
|
||||
mMergeFallback.push_back(fallback[i]);
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
for(std::vector<std::string>::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); vc++) {
|
||||
cfg.erase(it->first);
|
||||
insertMultistrmap(cfg, it->first, *vc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) {
|
||||
cfg.erase("fallback");
|
||||
|
||||
multistrmap::iterator cfgIt;
|
||||
multistrmap::iterator iniIt;
|
||||
for(std::vector<std::string>::iterator it=mMergeFallback.begin(); it!=mMergeFallback.end(); it++) {
|
||||
if((iniIt = ini.find(*it)) != ini.end()) {
|
||||
for(std::vector<std::string>::iterator vc = iniIt->second.begin(); vc != iniIt->second.end(); vc++) {
|
||||
std::string value(*it);
|
||||
std::replace( value.begin(), value.end(), ' ', '_' );
|
||||
std::replace( value.begin(), value.end(), ':', '_' );
|
||||
value.append(",").append(vc->substr(0,vc->length()-1));
|
||||
insertMultistrmap(cfg, "fallback", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::string value) {
|
||||
multistrmap::iterator it = cfg.find(key);
|
||||
if(it == cfg.end()) {
|
||||
cfg.insert(std::make_pair<std::string, std::vector<std::string> >(key, std::vector<std::string>() ));
|
||||
}
|
||||
cfg[key].push_back(value);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
34
apps/mwiniimporter/importer.hpp
Normal file
34
apps/mwiniimporter/importer.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#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 mergeFallback(multistrmap &cfg, multistrmap &ini);
|
||||
void importGameFiles(multistrmap &cfg, multistrmap &ini);
|
||||
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
|
||||
|
||||
private:
|
||||
void insertMultistrmap(multistrmap &cfg, std::string key, std::string value);
|
||||
std::string numberToString(int n);
|
||||
bool mVerbose;
|
||||
strmap mMergeMap;
|
||||
std::vector<std::string> mMergeFallback;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
75
apps/mwiniimporter/main.cpp
Normal file
75
apps/mwiniimporter/main.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#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> inifile configfile\nAllowed options");
|
||||
bpo::positional_options_description p_desc;
|
||||
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")
|
||||
;
|
||||
p_desc.add("ini", 1).add("cfg", 1);
|
||||
|
||||
bpo::variables_map vm;
|
||||
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
|
||||
.options(desc)
|
||||
.positional(p_desc)
|
||||
.run();
|
||||
|
||||
bpo::store(parsed, vm);
|
||||
|
||||
if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
|
||||
std::cout << desc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bpo::notify(vm);
|
||||
|
||||
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"));
|
||||
|
||||
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
|
||||
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
||||
|
||||
importer.merge(cfg, ini);
|
||||
importer.mergeFallback(cfg, ini);
|
||||
|
||||
if(vm.count("game-files")) {
|
||||
importer.importGameFiles(cfg, ini);
|
||||
}
|
||||
|
||||
std::cout << "write to: " << outputFile << std::endl;
|
||||
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
||||
importer.writeToFile(file, cfg);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
|
|||
|
||||
add_openmw_dir (mwrender
|
||||
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
|
||||
renderinginterface localmap
|
||||
renderinginterface localmap occlusionquery terrain terrainmaterial water shadows shaderhelper
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
@ -25,6 +25,7 @@ add_openmw_dir (mwinput
|
|||
add_openmw_dir (mwgui
|
||||
layouts text_input widgets race class birth review window_manager console dialogue
|
||||
dialogue_history window_base stats_window messagebox journalwindow charactercreation container
|
||||
map_window window_pinnable_base cursorreplace
|
||||
)
|
||||
|
||||
add_openmw_dir (mwdialogue
|
||||
|
@ -39,7 +40,7 @@ add_openmw_dir (mwscript
|
|||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
soundmanager
|
||||
soundmanager openal_output audiere_decoder mpgsnd_decoder ffmpeg_decoder
|
||||
)
|
||||
|
||||
add_openmw_dir (mwworld
|
||||
|
@ -54,7 +55,7 @@ add_openmw_dir (mwclass
|
|||
)
|
||||
|
||||
add_openmw_dir (mwmechanics
|
||||
mechanicsmanager stat creaturestats magiceffects movement
|
||||
mechanicsmanager stat creaturestats magiceffects movement actors drawstate spells
|
||||
)
|
||||
|
||||
# Main executable
|
||||
|
@ -82,6 +83,7 @@ add_definitions(${SOUND_DEFINE})
|
|||
|
||||
target_link_libraries(openmw
|
||||
${OGRE_LIBRARIES}
|
||||
${OGRE_Terrain_LIBRARY}
|
||||
${OGRE_STATIC_PLUGINS}
|
||||
${OIS_LIBRARIES}
|
||||
${Boost_LIBRARIES}
|
||||
|
@ -89,7 +91,7 @@ target_link_libraries(openmw
|
|||
${SOUND_INPUT_LIBRARY}
|
||||
${BULLET_LIBRARIES}
|
||||
${MYGUI_LIBRARIES}
|
||||
MyGUI.OgrePlatform #TODO MyGUI ogre platform is not added by the find script
|
||||
${MYGUI_PLATFORM_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <components/esm/esm_reader.hpp>
|
||||
#include <components/files/fixedpath.hpp>
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/nifoverrides/nifoverrides.hpp>
|
||||
|
||||
#include <components/nifbullet/bullet_nif_loader.hpp>
|
||||
#include <components/nifogre/ogre_nif_loader.hpp>
|
||||
|
@ -27,6 +29,7 @@
|
|||
#include "mwinput/inputmanager.hpp"
|
||||
|
||||
#include "mwgui/window_manager.hpp"
|
||||
#include "mwgui/cursorreplace.hpp"
|
||||
|
||||
#include "mwscript/scriptmanager.hpp"
|
||||
#include "mwscript/compilercontext.hpp"
|
||||
|
@ -82,12 +85,20 @@ void OMW::Engine::updateFocusReport (float duration)
|
|||
|
||||
if (!handle.empty())
|
||||
{
|
||||
MWWorld::Ptr ptr = mEnvironment.mWorld->getPtrViaHandle (handle);
|
||||
// the faced handle is not updated immediately, so on a cell change it might
|
||||
// point to an object that doesn't exist anymore
|
||||
// therefore, we are catching the "Unknown Ogre handle" exception that occurs in this case
|
||||
try
|
||||
{
|
||||
MWWorld::Ptr ptr = mEnvironment.mWorld->getPtrViaHandle (handle);
|
||||
|
||||
if (!ptr.isEmpty()){
|
||||
name = MWWorld::Class::get (ptr).getName (ptr);
|
||||
if (!ptr.isEmpty()){
|
||||
name = MWWorld::Class::get (ptr).getName (ptr);
|
||||
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{}
|
||||
}
|
||||
|
||||
if (name!=mFocusName)
|
||||
|
@ -115,13 +126,12 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
|
|||
{
|
||||
mEnvironment.mFrameDuration = evt.timeSinceLastFrame;
|
||||
|
||||
// update input
|
||||
mEnvironment.mInputManager->update();
|
||||
|
||||
// sound
|
||||
if (mUseSound)
|
||||
{
|
||||
mEnvironment.mSoundManager->playPlaylist();
|
||||
|
||||
mEnvironment.mSoundManager->update (evt.timeSinceLastFrame);
|
||||
}
|
||||
|
||||
// update GUI
|
||||
Ogre::RenderWindow* window = mOgre->getWindow();
|
||||
|
@ -152,7 +162,8 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
|
|||
|
||||
// update actors
|
||||
std::vector<std::pair<std::string, Ogre::Vector3> > movement;
|
||||
mEnvironment.mMechanicsManager->update (movement);
|
||||
mEnvironment.mMechanicsManager->update (movement, mEnvironment.mFrameDuration,
|
||||
mEnvironment.mWindowManager->getMode()!=MWGui::GM_Game);
|
||||
|
||||
if (mEnvironment.mWindowManager->getMode()==MWGui::GM_Game)
|
||||
mEnvironment.mWorld->doPhysics (movement, mEnvironment.mFrameDuration);
|
||||
|
@ -208,13 +219,18 @@ OMW::Engine::~Engine()
|
|||
void OMW::Engine::loadBSA()
|
||||
{
|
||||
const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
|
||||
std::string dataDirectory;
|
||||
|
||||
for (Files::MultiDirCollection::TIter iter(bsa.begin()); iter!=bsa.end(); ++iter)
|
||||
{
|
||||
std::cout << "Adding " << iter->second.string() << std::endl;
|
||||
Bsa::addBSA(iter->second.string());
|
||||
}
|
||||
|
||||
dataDirectory = iter->second.parent_path().string();
|
||||
const Files::PathContainer& dataDirs = mFileCollections.getPaths();
|
||||
std::string dataDirectory;
|
||||
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||
{
|
||||
dataDirectory = iter->string();
|
||||
std::cout << "Data dir " << dataDirectory << std::endl;
|
||||
Bsa::addDir(dataDirectory, mFSStrict);
|
||||
}
|
||||
|
@ -312,6 +328,36 @@ void OMW::Engine::go()
|
|||
{
|
||||
boost::filesystem::create_directories(configPath);
|
||||
}
|
||||
|
||||
// Create the settings manager and load default settings file
|
||||
Settings::Manager settings;
|
||||
const std::string localdefault = mCfgMgr.getLocalPath().string() + "/settings-default.cfg";
|
||||
const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/settings-default.cfg";
|
||||
|
||||
// prefer local
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
settings.loadDefault(localdefault);
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
settings.loadDefault(globaldefault);
|
||||
|
||||
// load user settings if they exist, otherwise just load the default settings as user settings
|
||||
const std::string settingspath = mCfgMgr.getUserPath().string() + "/settings.cfg";
|
||||
if (boost::filesystem::exists(settingspath))
|
||||
settings.loadUser(settingspath);
|
||||
else if (boost::filesystem::exists(localdefault))
|
||||
settings.loadUser(localdefault);
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
settings.loadUser(globaldefault);
|
||||
|
||||
mFpsLevel = settings.getInt("fps", "HUD");
|
||||
|
||||
// load nif overrides
|
||||
NifOverrides::Overrides nifOverrides;
|
||||
if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg"))
|
||||
nifOverrides.loadTransparencyOverrides(mCfgMgr.getLocalPath().string() + "/transparency-overrides.cfg");
|
||||
else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg"))
|
||||
nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg");
|
||||
|
||||
mOgre->configure(!boost::filesystem::is_regular_file(mCfgMgr.getOgreConfigPath()),
|
||||
mCfgMgr.getOgreConfigPath().string(),
|
||||
mCfgMgr.getLogPath().string(),
|
||||
|
@ -319,16 +365,25 @@ void OMW::Engine::go()
|
|||
|
||||
// This has to be added BEFORE MyGUI is initialized, as it needs
|
||||
// to find core.xml here.
|
||||
|
||||
//addResourcesDirectory(mResDir);
|
||||
|
||||
addResourcesDirectory(mResDir / "mygui");
|
||||
addResourcesDirectory(mResDir / "water");
|
||||
addResourcesDirectory(mResDir / "gbuffer");
|
||||
addResourcesDirectory(mResDir / "shadows");
|
||||
|
||||
// Create the window
|
||||
mOgre->createWindow("OpenMW");
|
||||
|
||||
loadBSA();
|
||||
|
||||
// cursor replacer (converts the cursor from the bsa so they can be used by mygui)
|
||||
MWGui::CursorReplace replacer;
|
||||
|
||||
// Create the world
|
||||
mEnvironment.mWorld = new MWWorld::World (*mOgre, mFileCollections, mMaster,
|
||||
mResDir, mNewGame, mEnvironment, mEncoding);
|
||||
mResDir, mNewGame, mEnvironment, mEncoding, mFallbackMap);
|
||||
|
||||
// Create window manager - this manages all the MW-specific GUI windows
|
||||
MWScript::registerExtensions (mExtensions);
|
||||
|
@ -337,10 +392,7 @@ void OMW::Engine::go()
|
|||
mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"));
|
||||
|
||||
// Create sound system
|
||||
mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(),
|
||||
mOgre->getCamera(),
|
||||
mDataDirs,
|
||||
mUseSound, mFSStrict, mEnvironment);
|
||||
mEnvironment.mSoundManager = new MWSound::SoundManager(mUseSound, mEnvironment);
|
||||
|
||||
// Create script system
|
||||
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
|
||||
|
@ -405,6 +457,9 @@ void OMW::Engine::go()
|
|||
// Start the main rendering loop
|
||||
mOgre->start();
|
||||
|
||||
// Save user settings
|
||||
settings.saveUser(settingspath);
|
||||
|
||||
std::cout << "Quitting peacefully.\n";
|
||||
}
|
||||
|
||||
|
@ -418,10 +473,21 @@ void OMW::Engine::activate()
|
|||
if (handle.empty())
|
||||
return;
|
||||
|
||||
MWWorld::Ptr ptr = mEnvironment.mWorld->getPtrViaHandle (handle);
|
||||
// the faced handle is not updated immediately, so on a cell change it might
|
||||
// point to an object that doesn't exist anymore
|
||||
// therefore, we are catching the "Unknown Ogre handle" exception that occurs in this case
|
||||
MWWorld::Ptr ptr;
|
||||
try
|
||||
{
|
||||
ptr = mEnvironment.mWorld->getPtrViaHandle (handle);
|
||||
|
||||
if (ptr.isEmpty())
|
||||
if (ptr.isEmpty())
|
||||
return;
|
||||
}
|
||||
catch (std::runtime_error&)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MWScript::InterpreterContext interpreterContext (mEnvironment,
|
||||
&ptr.getRefData().getLocals(), ptr);
|
||||
|
@ -487,3 +553,8 @@ void OMW::Engine::setEncoding(const std::string& encoding)
|
|||
{
|
||||
mEncoding = encoding;
|
||||
}
|
||||
|
||||
void OMW::Engine::setFallbackValues(std::map<std::string,std::string> fallbackMap)
|
||||
{
|
||||
mFallbackMap = fallbackMap;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ namespace OMW
|
|||
bool mReportFocus;
|
||||
float mFocusTDiff;
|
||||
std::string mFocusName;
|
||||
std::map<std::string,std::string> mFallbackMap;
|
||||
|
||||
MWWorld::Environment mEnvironment;
|
||||
Compiler::Extensions mExtensions;
|
||||
|
@ -163,6 +164,8 @@ namespace OMW
|
|||
|
||||
void setAnimationVerbose(bool animverbose);
|
||||
|
||||
void setFallbackValues(std::map<std::string,std::string> map);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager& mCfgMgr;
|
||||
};
|
||||
|
|
|
@ -54,6 +54,41 @@ inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string
|
|||
|
||||
using namespace std;
|
||||
|
||||
struct FallbackMap {
|
||||
std::map<std::string,std::string> mMap;
|
||||
};
|
||||
|
||||
void validate(boost::any &v, std::vector<std::string> const &tokens, FallbackMap*, int)
|
||||
{
|
||||
if(v.empty())
|
||||
{
|
||||
v = boost::any(FallbackMap());
|
||||
}
|
||||
|
||||
FallbackMap *map = boost::any_cast<FallbackMap>(&v);
|
||||
|
||||
std::map<std::string,std::string>::iterator mapIt;
|
||||
for(std::vector<std::string>::const_iterator it=tokens.begin(); it != tokens.end(); it++)
|
||||
{
|
||||
int sep = it->find(",");
|
||||
if(sep < 1 || sep == (int)it->length()-1)
|
||||
#if (BOOST_VERSION < 104200)
|
||||
throw boost::program_options::validation_error("invalid value");
|
||||
#else
|
||||
throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value);
|
||||
#endif
|
||||
|
||||
std::string key(it->substr(0,sep));
|
||||
std::string value(it->substr(sep+1));
|
||||
|
||||
if((mapIt = map->mMap.find(key)) == map->mMap.end())
|
||||
{
|
||||
map->mMap.insert(std::make_pair<std::string,std::string>(key,value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Parses application command line and calls \ref Cfg::ConfigurationManager
|
||||
* to parse configuration files.
|
||||
|
@ -92,39 +127,40 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
("plugin", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
||||
->multitoken(), "plugin file(s)")
|
||||
|
||||
("fps", boost::program_options::value<int>()->implicit_value(1)
|
||||
->default_value(0), "fps counter detail (0 = off, 1 = fps counter, 2 = full detail)")
|
||||
|
||||
("anim-verbose", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("anim-verbose", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "output animation indices files")
|
||||
|
||||
("debug", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("debug", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "debug mode")
|
||||
|
||||
("nosound", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("nosound", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "disable all sounds")
|
||||
|
||||
("script-verbose", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("script-verbose", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "verbose script output")
|
||||
|
||||
("new-game", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("new-game", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "activate char gen/new game mechanics")
|
||||
|
||||
("script-all", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("script-all", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "compile all scripts (excluding dialogue scripts) at startup")
|
||||
|
||||
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("fs-strict", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "strict file system handling (no case folding)")
|
||||
|
||||
( "encoding", boost::program_options::value<std::string>()->
|
||||
( "encoding", bpo::value<std::string>()->
|
||||
default_value("win1252"),
|
||||
"Character encoding used in OpenMW game messages:\n"
|
||||
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||
|
||||
("report-focus", boost::program_options::value<bool>()->implicit_value(true)
|
||||
("report-focus", bpo::value<bool>()->implicit_value(true)
|
||||
->default_value(false), "write name of focussed object to cout")
|
||||
|
||||
("fallback", bpo::value<FallbackMap>()->default_value(FallbackMap(), "")
|
||||
->multitoken()->composing(), "fallback values")
|
||||
|
||||
;
|
||||
|
||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
|
||||
|
@ -225,13 +261,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
engine.setNewGame(variables["new-game"].as<bool>());
|
||||
|
||||
// other settings
|
||||
engine.showFPS(variables["fps"].as<int>());
|
||||
engine.setDebugMode(variables["debug"].as<bool>());
|
||||
engine.setSoundUsage(!variables["nosound"].as<bool>());
|
||||
engine.setScriptsVerbosity(variables["script-verbose"].as<bool>());
|
||||
engine.setCompileAll(variables["script-all"].as<bool>());
|
||||
engine.setReportFocus(variables["report-focus"].as<bool>());
|
||||
engine.setAnimationVerbose(variables["anim-verbose"].as<bool>());
|
||||
engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Apparatus::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -70,6 +70,14 @@ namespace MWClass
|
|||
return ref->base->script;
|
||||
}
|
||||
|
||||
int Apparatus::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Apparatus, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Apparatus>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Apparatus::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Apparatus);
|
||||
|
|
|
@ -25,6 +25,9 @@ namespace MWClass
|
|||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr) const;
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Armor::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -160,6 +160,14 @@ namespace MWClass
|
|||
return ESM::Skill::HeavyArmor;
|
||||
}
|
||||
|
||||
int Armor::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Armor, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Armor>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Armor::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Armor);
|
||||
|
|
|
@ -40,6 +40,9 @@ namespace MWClass
|
|||
/// Return the index of the skill this item corresponds to when equiopped or -1, if there is
|
||||
/// no such skill.
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace MWClass
|
|||
{
|
||||
// TODO implement reading
|
||||
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -72,6 +72,14 @@ namespace MWClass
|
|||
return ref->base->script;
|
||||
}
|
||||
|
||||
int Book::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Book, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Book>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Book::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Book);
|
||||
|
|
|
@ -25,6 +25,9 @@ namespace MWClass
|
|||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Clothing::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -123,6 +123,14 @@ namespace MWClass
|
|||
return -1;
|
||||
}
|
||||
|
||||
int Clothing::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Clothing, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Clothing>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Clothing::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Clothing);
|
||||
|
|
|
@ -34,6 +34,9 @@ namespace MWClass
|
|||
/// Return the index of the skill this item corresponds to when equiopped or -1, if there is
|
||||
/// no such skill.
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace MWClass
|
|||
{
|
||||
// TODO check for key
|
||||
std::cout << "Locked container" << std::endl;
|
||||
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
|
||||
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0);
|
||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||
}
|
||||
else
|
||||
|
@ -103,7 +103,7 @@ namespace MWClass
|
|||
{
|
||||
// Trap activation goes here
|
||||
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
|
||||
environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0, false);
|
||||
environment.mSoundManager->playSound3D (ptr, trapActivationSound, 1.0, 1.0);
|
||||
ptr.getCellRef().trap = "";
|
||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace MWClass
|
|||
// TODO check for key
|
||||
// TODO report failure to player (message, sound?). Look up behaviour of original MW.
|
||||
std::cout << "Locked!" << std::endl;
|
||||
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0, false);
|
||||
environment.mSoundManager->playSound3D (ptr, lockedSound, 1.0, 1.0);
|
||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace MWClass
|
|||
{
|
||||
// Trap activation
|
||||
std::cout << "Activated trap: " << ptr.getCellRef().trap << std::endl;
|
||||
environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0, false);
|
||||
environment.mSoundManager->playSound3D(ptr, trapActivationSound, 1.0, 1.0);
|
||||
ptr.getCellRef().trap = "";
|
||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ namespace MWClass
|
|||
// TODO return action for rotating the door
|
||||
|
||||
// This is a little pointless, but helps with testing
|
||||
environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0, false);
|
||||
environment.mSoundManager->playSound3D (ptr, openSound, 1.0, 1.0);
|
||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Ingredient::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -68,6 +68,14 @@ namespace MWClass
|
|||
return ref->base->script;
|
||||
}
|
||||
|
||||
int Ingredient::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Ingredient, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Ingredient>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Ingredient::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Ingredient);
|
||||
|
|
|
@ -25,6 +25,9 @@ namespace MWClass
|
|||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace MWClass
|
|||
|
||||
if (!ref->base->sound.empty())
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, true);
|
||||
environment.mSoundManager->playSound3D (ptr, ref->base->sound, 1.0, 1.0, MWSound::Play_Loop);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ namespace MWClass
|
|||
if (!(ref->base->data.flags & ESM::Light::Carry))
|
||||
return boost::shared_ptr<MWWorld::Action> (new MWWorld::NullAction);
|
||||
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -110,6 +110,14 @@ namespace MWClass
|
|||
return std::make_pair (slots, false);
|
||||
}
|
||||
|
||||
int Light::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Light, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Light>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Light::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Light);
|
||||
|
|
|
@ -34,6 +34,9 @@ namespace MWClass
|
|||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||
/// stay stacked when equipped?
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Lockpick::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -81,6 +81,14 @@ namespace MWClass
|
|||
return std::make_pair (slots, false);
|
||||
}
|
||||
|
||||
int Lockpick::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Tool, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Tool>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Lockpick::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Lockpick);
|
||||
|
|
|
@ -29,6 +29,9 @@ namespace MWClass
|
|||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||
/// stay stacked when equipped?
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -70,6 +70,14 @@ namespace MWClass
|
|||
return ref->base->script;
|
||||
}
|
||||
|
||||
int Miscellaneous::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Miscellaneous>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Miscellaneous::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Miscellaneous);
|
||||
|
|
|
@ -25,6 +25,9 @@ namespace MWClass
|
|||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -53,28 +53,40 @@ namespace MWClass
|
|||
// NPC stats
|
||||
if (!ref->base->faction.empty())
|
||||
{
|
||||
// TODO research how initial rank is stored. The information in loadnpc.hpp are at
|
||||
// best very unclear.
|
||||
data->mNpcStats.mFactionRank[ref->base->faction] = 0;
|
||||
if(ref->base->npdt52.gold != -10)
|
||||
{
|
||||
data->mNpcStats.mFactionRank[ref->base->faction] = (int)ref->base->npdt52.rank;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->mNpcStats.mFactionRank[ref->base->faction] = (int)ref->base->npdt12.rank;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<27; ++i)
|
||||
data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]);
|
||||
if(ref->base->npdt52.gold != -10)
|
||||
{
|
||||
for (int i=0; i<27; ++i)
|
||||
data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]);
|
||||
|
||||
// creature stats
|
||||
data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength);
|
||||
data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence);
|
||||
data->mCreatureStats.mAttributes[2].set (ref->base->npdt52.willpower);
|
||||
data->mCreatureStats.mAttributes[3].set (ref->base->npdt52.agility);
|
||||
data->mCreatureStats.mAttributes[4].set (ref->base->npdt52.speed);
|
||||
data->mCreatureStats.mAttributes[5].set (ref->base->npdt52.endurance);
|
||||
data->mCreatureStats.mAttributes[6].set (ref->base->npdt52.personality);
|
||||
data->mCreatureStats.mAttributes[7].set (ref->base->npdt52.luck);
|
||||
data->mCreatureStats.mDynamic[0].set (ref->base->npdt52.health);
|
||||
data->mCreatureStats.mDynamic[1].set (ref->base->npdt52.mana);
|
||||
data->mCreatureStats.mDynamic[2].set (ref->base->npdt52.fatigue);
|
||||
// creature stats
|
||||
data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength);
|
||||
data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence);
|
||||
data->mCreatureStats.mAttributes[2].set (ref->base->npdt52.willpower);
|
||||
data->mCreatureStats.mAttributes[3].set (ref->base->npdt52.agility);
|
||||
data->mCreatureStats.mAttributes[4].set (ref->base->npdt52.speed);
|
||||
data->mCreatureStats.mAttributes[5].set (ref->base->npdt52.endurance);
|
||||
data->mCreatureStats.mAttributes[6].set (ref->base->npdt52.personality);
|
||||
data->mCreatureStats.mAttributes[7].set (ref->base->npdt52.luck);
|
||||
data->mCreatureStats.mDynamic[0].set (ref->base->npdt52.health);
|
||||
data->mCreatureStats.mDynamic[1].set (ref->base->npdt52.mana);
|
||||
data->mCreatureStats.mDynamic[2].set (ref->base->npdt52.fatigue);
|
||||
|
||||
data->mCreatureStats.mLevel = ref->base->npdt52.level;
|
||||
data->mCreatureStats.mLevel = ref->base->npdt52.level;
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: do something with npdt12 maybe:p
|
||||
}
|
||||
|
||||
// \todo add initial container content
|
||||
|
||||
|
@ -281,7 +293,7 @@ namespace MWClass
|
|||
void Npc::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Npc);
|
||||
|
||||
std::cout << "class npc:" << typeid (ESM::NPC).name();
|
||||
registerClass (typeid (ESM::NPC).name(), instance);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Potion::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -70,6 +70,14 @@ namespace MWClass
|
|||
return ref->base->script;
|
||||
}
|
||||
|
||||
int Potion::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Potion, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Potion>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Potion::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Potion);
|
||||
|
|
|
@ -25,6 +25,9 @@ namespace MWClass
|
|||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Probe::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -80,6 +80,14 @@ namespace MWClass
|
|||
return std::make_pair (slots, false);
|
||||
}
|
||||
|
||||
int Probe::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Probe, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Probe>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Probe::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Probe);
|
||||
|
|
|
@ -29,6 +29,9 @@ namespace MWClass
|
|||
///< \return first: Return IDs of the slot this object can be equipped in; second: can object
|
||||
/// stay stacked when equipped?
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Repair::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -70,6 +70,14 @@ namespace MWClass
|
|||
return ref->base->script;
|
||||
}
|
||||
|
||||
int Repair::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Repair, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Repair>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Repair::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Repair);
|
||||
|
|
|
@ -25,6 +25,9 @@ namespace MWClass
|
|||
virtual std::string getScript (const MWWorld::Ptr& ptr) const;
|
||||
///< Return name of the script attached to ptr
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace MWClass
|
|||
boost::shared_ptr<MWWorld::Action> Weapon::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||
{
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, false, true);
|
||||
environment.mSoundManager->playSound3D (ptr, getUpSoundId(ptr, environment), 1.0, 1.0, MWSound::Play_NoTrack);
|
||||
|
||||
return boost::shared_ptr<MWWorld::Action> (
|
||||
new MWWorld::ActionTake (ptr));
|
||||
|
@ -139,6 +139,14 @@ namespace MWClass
|
|||
return -1;
|
||||
}
|
||||
|
||||
int Weapon::getValue (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::Weapon, MWWorld::RefData> *ref =
|
||||
ptr.get<ESM::Weapon>();
|
||||
|
||||
return ref->base->data.value;
|
||||
}
|
||||
|
||||
void Weapon::registerSelf()
|
||||
{
|
||||
boost::shared_ptr<Class> instance (new Weapon);
|
||||
|
|
|
@ -40,6 +40,9 @@ namespace MWClass
|
|||
/// Return the index of the skill this item corresponds to when equiopped or -1, if there is
|
||||
/// no such skill.
|
||||
|
||||
virtual int getValue (const MWWorld::Ptr& ptr) const;
|
||||
///< Return trade value of the object. Throws an exception, if the object can't be traded.
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getUpSoundId (const MWWorld::Ptr& ptr, const MWWorld::Environment& environment) const;
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#include "../mwscript/interpretercontext.hpp"
|
||||
#include <components/compiler/scriptparser.hpp>
|
||||
|
||||
#include "../mwclass/npc.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string toLower (const std::string& name)
|
||||
|
@ -109,16 +112,15 @@ namespace
|
|||
switch (world.getGlobalVariableType (name))
|
||||
{
|
||||
case 's':
|
||||
|
||||
return selectCompare (comp, value, world.getGlobalVariable (name).mShort);
|
||||
return selectCompare (comp, world.getGlobalVariable (name).mShort, value);
|
||||
|
||||
case 'l':
|
||||
|
||||
return selectCompare (comp, value, world.getGlobalVariable (name).mLong);
|
||||
return selectCompare (comp, world.getGlobalVariable (name).mLong, value);
|
||||
|
||||
case 'f':
|
||||
|
||||
return selectCompare (comp, value, world.getGlobalVariable (name).mFloat);
|
||||
return selectCompare (comp, world.getGlobalVariable (name).mFloat, value);
|
||||
|
||||
case ' ':
|
||||
|
||||
|
@ -178,7 +180,17 @@ namespace MWDialogue
|
|||
break;
|
||||
|
||||
case 46://Same faction
|
||||
if(!selectCompare<int,int>(comp,0,select.i)) return false;
|
||||
{
|
||||
MWMechanics::NpcStats PCstats = MWWorld::Class::get(mEnvironment.mWorld->getPlayer().getPlayer()).getNpcStats(mEnvironment.mWorld->getPlayer().getPlayer());
|
||||
MWMechanics::NpcStats NPCstats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
int sameFaction = 0;
|
||||
if(!NPCstats.mFactionRank.empty())
|
||||
{
|
||||
std::string NPCFaction = NPCstats.mFactionRank.begin()->first;
|
||||
if(PCstats.mFactionRank.find(NPCFaction) != PCstats.mFactionRank.end()) sameFaction = 1;
|
||||
}
|
||||
if(!selectCompare<int,int>(comp,sameFaction,select.i)) return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 48://Detected
|
||||
|
@ -190,7 +202,6 @@ namespace MWDialogue
|
|||
break;
|
||||
|
||||
case 50://choice
|
||||
|
||||
if(choice)
|
||||
{
|
||||
if(!selectCompare<int,int>(comp,mChoice,select.i)) return false;
|
||||
|
@ -270,7 +281,7 @@ namespace MWDialogue
|
|||
{
|
||||
case '1': // function
|
||||
|
||||
return true; // TODO implement functions
|
||||
return true; // Done elsewhere.
|
||||
|
||||
case '2': // global
|
||||
|
||||
|
@ -444,9 +455,6 @@ namespace MWDialogue
|
|||
if (toLower (info.actor)!=MWWorld::Class::get (actor).getId (actor))
|
||||
return false;
|
||||
|
||||
//PC Faction
|
||||
if(!info.pcFaction.empty()) return false;
|
||||
|
||||
//NPC race
|
||||
if (!info.race.empty())
|
||||
{
|
||||
|
@ -474,26 +482,37 @@ namespace MWDialogue
|
|||
//NPC faction
|
||||
if (!info.npcFaction.empty())
|
||||
{
|
||||
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> *cellRef = actor.get<ESM::NPC>();
|
||||
|
||||
if (!cellRef)
|
||||
return false;
|
||||
|
||||
if (toLower (info.npcFaction)!=toLower (cellRef->base->faction))
|
||||
return false;
|
||||
|
||||
//check NPC rank
|
||||
if(cellRef->base->npdt52.gold != -10)
|
||||
//MWWorld::Class npcClass = MWWorld::Class::get(actor);
|
||||
MWMechanics::NpcStats stats = MWWorld::Class::get(actor).getNpcStats(actor);
|
||||
std::map<std::string,int>::iterator it = stats.mFactionRank.find(info.npcFaction);
|
||||
if(it!=stats.mFactionRank.end())
|
||||
{
|
||||
if(cellRef->base->npdt52.rank < info.data.rank) return false;
|
||||
//check rank
|
||||
if(it->second < (int)info.data.rank) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(cellRef->base->npdt12.rank < info.data.rank) return false;
|
||||
//not in the faction
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO check player faction
|
||||
if(!info.pcFaction.empty())
|
||||
{
|
||||
MWMechanics::NpcStats stats = MWWorld::Class::get(mEnvironment.mWorld->getPlayer().getPlayer()).getNpcStats(mEnvironment.mWorld->getPlayer().getPlayer());
|
||||
std::map<std::string,int>::iterator it = stats.mFactionRank.find(info.pcFaction);
|
||||
if(it!=stats.mFactionRank.end())
|
||||
{
|
||||
//check rank
|
||||
if(it->second < (int)info.data.PCrank) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//not in the faction
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//check gender
|
||||
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData>* npc = actor.get<ESM::NPC>();
|
||||
|
@ -528,6 +547,13 @@ namespace MWDialogue
|
|||
mChoice = -1;
|
||||
mIsInChoice = false;
|
||||
mCompilerContext.setExtensions (&extensions);
|
||||
mDialogueMap.clear();
|
||||
actorKnownTopics.clear();
|
||||
ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
|
||||
for(ESMS::RecListT<ESM::Dialogue>::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++)
|
||||
{
|
||||
mDialogueMap[it->first] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void DialogueManager::addTopic(std::string topic)
|
||||
|
@ -563,13 +589,7 @@ namespace MWDialogue
|
|||
|
||||
mActor = actor;
|
||||
|
||||
mDialogueMap.clear();
|
||||
actorKnownTopics.clear();
|
||||
ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
|
||||
for(ESMS::RecListT<ESM::Dialogue>::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++)
|
||||
{
|
||||
mDialogueMap[it->first] = it->second;
|
||||
}
|
||||
|
||||
//initialise the GUI
|
||||
mEnvironment.mInputManager->setGuiMode(MWGui::GM_Dialogue);
|
||||
|
@ -582,6 +602,7 @@ namespace MWDialogue
|
|||
//greeting
|
||||
bool greetingFound = false;
|
||||
//ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
|
||||
ESMS::RecListT<ESM::Dialogue>::MapType dialogueList = mEnvironment.mWorld->getStore().dialogs.list;
|
||||
for(ESMS::RecListT<ESM::Dialogue>::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++)
|
||||
{
|
||||
ESM::Dialogue ndialogue = it->second;
|
||||
|
@ -656,6 +677,7 @@ namespace MWDialogue
|
|||
|
||||
void DialogueManager::executeScript(std::string script)
|
||||
{
|
||||
std::cout << script;
|
||||
std::vector<Interpreter::Type_Code> code;
|
||||
if(compile(script,code))
|
||||
{
|
||||
|
@ -797,4 +819,19 @@ namespace MWDialogue
|
|||
mChoiceMap[question] = choice;
|
||||
mIsInChoice = true;
|
||||
}
|
||||
|
||||
std::string DialogueManager::getFaction()
|
||||
{
|
||||
std::string factionID("");
|
||||
MWMechanics::NpcStats stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
|
||||
if(stats.mFactionRank.empty())
|
||||
{
|
||||
std::cout << "No faction for this actor!";
|
||||
}
|
||||
else
|
||||
{
|
||||
factionID = stats.mFactionRank.begin()->first;
|
||||
}
|
||||
return factionID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,6 +63,9 @@ namespace MWDialogue
|
|||
|
||||
void askQuestion(std::string question,int choice);
|
||||
|
||||
///get the faction of the actor you are talking with
|
||||
std::string getFaction();
|
||||
|
||||
//calbacks for the GUI
|
||||
void keywordSelected(std::string keyword);
|
||||
void goodbyeSelected();
|
||||
|
|
16
apps/openmw/mwgui/cursorreplace.cpp
Normal file
16
apps/openmw/mwgui/cursorreplace.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "cursorreplace.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <openengine/ogre/imagerotate.hpp>
|
||||
|
||||
#include <OgreResourceGroupManager.h>
|
||||
#include <OgreRoot.h>
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
CursorReplace::CursorReplace()
|
||||
{
|
||||
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90);
|
||||
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45);
|
||||
OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45);
|
||||
}
|
16
apps/openmw/mwgui/cursorreplace.hpp
Normal file
16
apps/openmw/mwgui/cursorreplace.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef GAME_CURSORREPLACE_H
|
||||
#define GAME_CURSORREPLACE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
/// \brief MyGUI does not support rotating cursors, so we have to do it manually
|
||||
class CursorReplace
|
||||
{
|
||||
public:
|
||||
CursorReplace();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -43,9 +43,6 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager,MWWorld::Environm
|
|||
// Centre dialog
|
||||
center();
|
||||
|
||||
//WindowManager *wm = environment.mWindowManager;
|
||||
setText("NpcName", "Name of character");
|
||||
|
||||
//History view
|
||||
getWidget(history, "History");
|
||||
history->setOverflowToTheLeft(true);
|
||||
|
@ -116,7 +113,8 @@ void DialogueWindow::onSelectTopic(MyGUI::ListBox* _sender, size_t _index)
|
|||
|
||||
void DialogueWindow::startDialogue(std::string npcName)
|
||||
{
|
||||
setText("NpcName", npcName);
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(npcName);
|
||||
adjustWindowCaption();
|
||||
}
|
||||
|
||||
void DialogueWindow::setKeywords(std::list<std::string> keyWords)
|
||||
|
|
|
@ -15,6 +15,27 @@ using namespace MWGui;
|
|||
|
||||
HUD::HUD(int width, int height, int fpsLevel)
|
||||
: Layout("openmw_hud_layout.xml")
|
||||
, health(NULL)
|
||||
, magicka(NULL)
|
||||
, stamina(NULL)
|
||||
, weapImage(NULL)
|
||||
, spellImage(NULL)
|
||||
, weapStatus(NULL)
|
||||
, spellStatus(NULL)
|
||||
, effectBox(NULL)
|
||||
, effect1(NULL)
|
||||
, minimap(NULL)
|
||||
, compass(NULL)
|
||||
, crosshair(NULL)
|
||||
, fpsbox(NULL)
|
||||
, fpscounter(NULL)
|
||||
, trianglecounter(NULL)
|
||||
, batchcounter(NULL)
|
||||
, hmsBaseLeft(0)
|
||||
, weapBoxBaseLeft(0)
|
||||
, spellBoxBaseLeft(0)
|
||||
, effectBoxBaseRight(0)
|
||||
, minimapBoxBaseRight(0)
|
||||
{
|
||||
setCoord(0,0, width, height);
|
||||
|
||||
|
@ -22,32 +43,32 @@ HUD::HUD(int width, int height, int fpsLevel)
|
|||
getWidget(health, "Health");
|
||||
getWidget(magicka, "Magicka");
|
||||
getWidget(stamina, "Stamina");
|
||||
hmsBaseLeft = health->getLeft();
|
||||
|
||||
// Item and spell images and status bars
|
||||
getWidget(weapBox, "WeapBox");
|
||||
getWidget(weapImage, "WeapImage");
|
||||
getWidget(weapStatus, "WeapStatus");
|
||||
weapBoxBaseLeft = weapBox->getLeft();
|
||||
|
||||
getWidget(spellBox, "SpellBox");
|
||||
getWidget(spellImage, "SpellImage");
|
||||
getWidget(spellStatus, "SpellStatus");
|
||||
spellBoxBaseLeft = spellBox->getLeft();
|
||||
|
||||
getWidget(effectBox, "EffectBox");
|
||||
getWidget(effect1, "Effect1");
|
||||
effectBoxBaseRight = effectBox->getRight();
|
||||
|
||||
getWidget(minimapBox, "MiniMapBox");
|
||||
minimapBoxBaseRight = minimapBox->getRight();
|
||||
getWidget(minimap, "MiniMap");
|
||||
getWidget(compass, "Compass");
|
||||
|
||||
getWidget(crosshair, "Crosshair");
|
||||
|
||||
if ( fpsLevel == 2 ){
|
||||
getWidget(fpsbox, "FPSBoxAdv");
|
||||
fpsbox->setVisible(true);
|
||||
getWidget(fpscounter, "FPSCounterAdv");
|
||||
}else if ( fpsLevel == 1 ){
|
||||
getWidget(fpsbox, "FPSBox");
|
||||
fpsbox->setVisible(true);
|
||||
getWidget(fpscounter, "FPSCounter");
|
||||
}else{
|
||||
getWidget(fpscounter, "FPSCounter");
|
||||
}
|
||||
setFpsLevel(fpsLevel);
|
||||
|
||||
getWidget(trianglecounter, "TriangleCounter");
|
||||
getWidget(batchcounter, "BatchCounter");
|
||||
|
||||
|
@ -65,6 +86,28 @@ HUD::HUD(int width, int height, int fpsLevel)
|
|||
LocalMapBase::init(minimap, this);
|
||||
}
|
||||
|
||||
void HUD::setFpsLevel(int level)
|
||||
{
|
||||
MyGUI::Widget* fps;
|
||||
getWidget(fps, "FPSBoxAdv");
|
||||
fps->setVisible(false);
|
||||
getWidget(fps, "FPSBox");
|
||||
fps->setVisible(false);
|
||||
|
||||
if (level == 2)
|
||||
{
|
||||
getWidget(fpsbox, "FPSBoxAdv");
|
||||
fpsbox->setVisible(true);
|
||||
getWidget(fpscounter, "FPSCounterAdv");
|
||||
}
|
||||
else if (level == 1)
|
||||
{
|
||||
getWidget(fpsbox, "FPSBox");
|
||||
fpsbox->setVisible(true);
|
||||
getWidget(fpscounter, "FPSCounter");
|
||||
}
|
||||
}
|
||||
|
||||
void HUD::setFPS(float fps)
|
||||
{
|
||||
fpscounter->setCaption(boost::lexical_cast<std::string>((int)fps));
|
||||
|
@ -147,15 +190,21 @@ void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat<int>& v
|
|||
|
||||
void HUD::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
if (!minimapBox->getVisible() || (x == mLastPositionX && y == mLastPositionY)) return;
|
||||
|
||||
MyGUI::ISubWidget* main = compass->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
mLastPositionX = x;
|
||||
mLastPositionY = y;
|
||||
}
|
||||
|
||||
void HUD::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
if (!minimapBox->getVisible() || (x == mLastDirectionX && y == mLastDirectionY)) return;
|
||||
|
||||
MyGUI::IntSize size = minimap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = minimap->getCoord();
|
||||
|
@ -163,96 +212,55 @@ void HUD::setPlayerPos(const float x, const float y)
|
|||
|
||||
minimap->setViewOffset(pos);
|
||||
compass->setPosition(MyGUI::IntPoint(x*512-16, y*512-16));
|
||||
|
||||
mLastDirectionX = x;
|
||||
mLastDirectionY = y;
|
||||
}
|
||||
|
||||
MapWindow::MapWindow()
|
||||
: Layout("openmw_map_window_layout.xml"), mGlobal(false)
|
||||
void HUD::setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellVisible)
|
||||
{
|
||||
setCoord(500,0,320,300);
|
||||
setText("WorldButton", "World");
|
||||
setImage("Compass", "textures\\compass.dds");
|
||||
int weapDx = 0, spellDx = 0;
|
||||
if (!hmsVisible)
|
||||
spellDx = weapDx = weapBoxBaseLeft - hmsBaseLeft;
|
||||
|
||||
// Obviously you should override this later on
|
||||
setCellName("No Cell Loaded");
|
||||
if (!weapVisible)
|
||||
spellDx -= spellBoxBaseLeft - weapBoxBaseLeft;
|
||||
|
||||
getWidget(mLocalMap, "LocalMap");
|
||||
getWidget(mGlobalMap, "GlobalMap");
|
||||
getWidget(mPlayerArrow, "Compass");
|
||||
|
||||
getWidget(mButton, "WorldButton");
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
|
||||
MyGUI::Button* eventbox;
|
||||
getWidget(eventbox, "EventBox");
|
||||
eventbox->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
eventbox->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
|
||||
LocalMapBase::init(mLocalMap, this);
|
||||
health->setVisible(hmsVisible);
|
||||
stamina->setVisible(hmsVisible);
|
||||
magicka->setVisible(hmsVisible);
|
||||
weapBox->setPosition(weapBoxBaseLeft - weapDx, weapBox->getTop());
|
||||
weapBox->setVisible(weapVisible);
|
||||
spellBox->setPosition(spellBoxBaseLeft - spellDx, spellBox->getTop());
|
||||
spellBox->setVisible(spellVisible);
|
||||
}
|
||||
|
||||
void MapWindow::setVisible(bool b)
|
||||
void HUD::setBottomRightVisibility(bool effectBoxVisible, bool minimapBoxVisible)
|
||||
{
|
||||
mMainWidget->setVisible(b);
|
||||
if (b)
|
||||
mVisible = true;
|
||||
else
|
||||
mVisible = false;
|
||||
// effect box can have variable width -> variable left coordinate
|
||||
int effectsDx = 0;
|
||||
if (!minimapBoxVisible)
|
||||
effectsDx = minimapBoxBaseRight - effectBoxBaseRight;
|
||||
|
||||
minimapBox->setVisible(minimapBoxVisible);
|
||||
effectBox->setPosition(effectBoxBaseRight - effectBox->getWidth() + effectsDx, effectBox->getTop());
|
||||
effectBox->setVisible(effectBoxVisible);
|
||||
}
|
||||
|
||||
void MapWindow::setCellName(const std::string& cellName)
|
||||
LocalMapBase::LocalMapBase()
|
||||
: mCurX(0)
|
||||
, mCurY(0)
|
||||
, mInterior(false)
|
||||
, mFogOfWar(true)
|
||||
, mLocalMap(NULL)
|
||||
, mPrefix()
|
||||
, mChanged(true)
|
||||
, mLayout(NULL)
|
||||
, mLastPositionX(0.0f)
|
||||
, mLastPositionY(0.0f)
|
||||
, mLastDirectionX(0.0f)
|
||||
, mLastDirectionY(0.0f)
|
||||
{
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(cellName);
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
if (mGlobal || mVisible) return;
|
||||
MyGUI::IntSize size = mLocalMap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = mLocalMap->getCoord();
|
||||
MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
|
||||
mLocalMap->setViewOffset(pos);
|
||||
|
||||
mPlayerArrow->setPosition(MyGUI::IntPoint(x*512-16, y*512-16));
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
if (!mVisible) return;
|
||||
MyGUI::ISubWidget* main = mPlayerArrow->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
}
|
||||
|
||||
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
if (!mGlobal)
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
|
||||
if (!mGlobal)
|
||||
{
|
||||
MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos;
|
||||
mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff );
|
||||
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
}
|
||||
|
||||
void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mGlobal = !mGlobal;
|
||||
mGlobalMap->setVisible(mGlobal);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
||||
mButton->setCaption( mGlobal ? "Local" : "World" );
|
||||
}
|
||||
|
||||
void LocalMapBase::init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout)
|
||||
|
@ -267,6 +275,32 @@ void LocalMapBase::setCellPrefix(const std::string& prefix)
|
|||
mChanged = true;
|
||||
}
|
||||
|
||||
void LocalMapBase::toggleFogOfWar()
|
||||
{
|
||||
mFogOfWar = !mFogOfWar;
|
||||
applyFogOfWar();
|
||||
}
|
||||
|
||||
void LocalMapBase::applyFogOfWar()
|
||||
{
|
||||
for (int mx=0; mx<3; ++mx)
|
||||
{
|
||||
for (int my=0; my<3; ++my)
|
||||
{
|
||||
std::string name = "Map_" + boost::lexical_cast<std::string>(mx) + "_"
|
||||
+ boost::lexical_cast<std::string>(my);
|
||||
std::string image = mPrefix+"_"+ boost::lexical_cast<std::string>(mCurX + (mx-1)) + "_"
|
||||
+ boost::lexical_cast<std::string>(mCurY + (mInterior ? (my-1) : -1*(my-1)));
|
||||
MyGUI::ImageBox* fog;
|
||||
mLayout->getWidget(fog, name+"_fog");
|
||||
fog->setImageTexture(mFogOfWar ?
|
||||
((MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0) ? image+"_fog"
|
||||
: "black.png" )
|
||||
: "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
|
||||
{
|
||||
if (x==mCurX && y==mCurY && mInterior==interior && !mChanged) return; // don't do anything if we're still in the same cell
|
||||
|
@ -282,23 +316,17 @@ void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
|
|||
|
||||
MyGUI::ImageBox* box;
|
||||
mLayout->getWidget(box, name);
|
||||
MyGUI::ImageBox* fog;
|
||||
mLayout->getWidget(fog, name+"_fog");
|
||||
|
||||
if (MyGUI::RenderManager::getInstance().getTexture(image) != 0)
|
||||
box->setImageTexture(image);
|
||||
else
|
||||
box->setImageTexture("black.png");
|
||||
|
||||
if (MyGUI::RenderManager::getInstance().getTexture(image+"_fog") != 0)
|
||||
fog->setImageTexture(image+"_fog");
|
||||
else
|
||||
fog->setImageTexture("black.png");
|
||||
}
|
||||
}
|
||||
mInterior = interior;
|
||||
mCurX = x;
|
||||
mCurY = y;
|
||||
mChanged = false;
|
||||
applyFogOfWar();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,19 +34,30 @@ namespace MWGui
|
|||
class LocalMapBase
|
||||
{
|
||||
public:
|
||||
LocalMapBase();
|
||||
void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout);
|
||||
|
||||
void setCellPrefix(const std::string& prefix);
|
||||
void setActiveCell(const int x, const int y, bool interior=false);
|
||||
|
||||
void toggleFogOfWar();
|
||||
|
||||
protected:
|
||||
int mCurX, mCurY;
|
||||
bool mInterior;
|
||||
MyGUI::ScrollView* mLocalMap;
|
||||
std::string mPrefix;
|
||||
bool mChanged;
|
||||
bool mFogOfWar;
|
||||
|
||||
void applyFogOfWar();
|
||||
|
||||
OEngine::GUI::Layout* mLayout;
|
||||
|
||||
float mLastPositionX;
|
||||
float mLastPositionY;
|
||||
float mLastDirectionX;
|
||||
float mLastDirectionY;
|
||||
};
|
||||
|
||||
class HUD : public OEngine::GUI::Layout, public LocalMapBase
|
||||
|
@ -65,11 +76,15 @@ namespace MWGui
|
|||
void setBatchCount(size_t count);
|
||||
void setPlayerDir(const float x, const float y);
|
||||
void setPlayerPos(const float x, const float y);
|
||||
void setBottomLeftVisibility(bool hmsVisible, bool weapVisible, bool spellVisible);
|
||||
void setBottomRightVisibility(bool effectBoxVisible, bool minimapVisible);
|
||||
void setFpsLevel(const int level);
|
||||
|
||||
MyGUI::ProgressPtr health, magicka, stamina;
|
||||
MyGUI::Widget *weapBox, *spellBox;
|
||||
MyGUI::ImageBox *weapImage, *spellImage;
|
||||
MyGUI::ProgressPtr weapStatus, spellStatus;
|
||||
MyGUI::WidgetPtr effectBox;
|
||||
MyGUI::Widget *effectBox, *minimapBox;
|
||||
MyGUI::ImageBox* effect1;
|
||||
MyGUI::ScrollView* minimap;
|
||||
MyGUI::ImageBox* compass;
|
||||
|
@ -79,29 +94,12 @@ namespace MWGui
|
|||
MyGUI::TextBox* fpscounter;
|
||||
MyGUI::TextBox* trianglecounter;
|
||||
MyGUI::TextBox* batchcounter;
|
||||
};
|
||||
|
||||
class MapWindow : public OEngine::GUI::Layout, public LocalMapBase
|
||||
{
|
||||
public:
|
||||
MapWindow();
|
||||
|
||||
void setVisible(bool b);
|
||||
void setPlayerPos(const float x, const float y);
|
||||
void setPlayerDir(const float x, const float y);
|
||||
void setCellName(const std::string& cellName);
|
||||
|
||||
private:
|
||||
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onWorldButtonClicked(MyGUI::Widget* _sender);
|
||||
|
||||
MyGUI::ScrollView* mGlobalMap;
|
||||
MyGUI::ImageBox* mPlayerArrow;
|
||||
MyGUI::Button* mButton;
|
||||
MyGUI::IntPoint mLastDragPos;
|
||||
bool mVisible;
|
||||
bool mGlobal;
|
||||
// bottom left elements
|
||||
int hmsBaseLeft, weapBoxBaseLeft, spellBoxBaseLeft;
|
||||
// bottom right elements
|
||||
int minimapBoxBaseRight, effectBoxBaseRight;
|
||||
};
|
||||
|
||||
class MainMenu : public OEngine::GUI::Layout
|
||||
|
|
106
apps/openmw/mwgui/map_window.cpp
Normal file
106
apps/openmw/mwgui/map_window.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "map_window.hpp"
|
||||
#include "window_manager.hpp"
|
||||
/*
|
||||
#include "../mwmechanics/mechanicsmanager.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
*/
|
||||
using namespace MWGui;
|
||||
|
||||
MapWindow::MapWindow(WindowManager& parWindowManager) :
|
||||
MWGui::WindowPinnableBase("openmw_map_window_layout.xml", parWindowManager),
|
||||
mGlobal(false)
|
||||
{
|
||||
setCoord(500,0,320,300);
|
||||
setText("WorldButton", "World");
|
||||
setImage("Compass", "textures\\compass.dds");
|
||||
|
||||
// Obviously you should override this later on
|
||||
setCellName("No Cell Loaded");
|
||||
|
||||
getWidget(mLocalMap, "LocalMap");
|
||||
getWidget(mGlobalMap, "GlobalMap");
|
||||
getWidget(mPlayerArrow, "Compass");
|
||||
|
||||
getWidget(mButton, "WorldButton");
|
||||
mButton->eventMouseButtonClick += MyGUI::newDelegate(this, &MapWindow::onWorldButtonClicked);
|
||||
|
||||
MyGUI::Button* eventbox;
|
||||
getWidget(eventbox, "EventBox");
|
||||
eventbox->eventMouseDrag += MyGUI::newDelegate(this, &MapWindow::onMouseDrag);
|
||||
eventbox->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
|
||||
|
||||
LocalMapBase::init(mLocalMap, this);
|
||||
}
|
||||
|
||||
void MapWindow::setCellName(const std::string& cellName)
|
||||
{
|
||||
static_cast<MyGUI::Window*>(mMainWidget)->setCaption(cellName);
|
||||
adjustWindowCaption();
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerPos(const float x, const float y)
|
||||
{
|
||||
if (mGlobal || !mVisible || (x == mLastPositionX && y == mLastPositionY)) return;
|
||||
MyGUI::IntSize size = mLocalMap->getCanvasSize();
|
||||
MyGUI::IntPoint middle = MyGUI::IntPoint((1/3.f + x/3.f)*size.width,(1/3.f + y/3.f)*size.height);
|
||||
MyGUI::IntCoord viewsize = mLocalMap->getCoord();
|
||||
MyGUI::IntPoint pos(0.5*viewsize.width - middle.left, 0.5*viewsize.height - middle.top);
|
||||
mLocalMap->setViewOffset(pos);
|
||||
|
||||
mPlayerArrow->setPosition(MyGUI::IntPoint(x*512-16, y*512-16));
|
||||
mLastPositionX = x;
|
||||
mLastPositionY = y;
|
||||
}
|
||||
|
||||
void MapWindow::setPlayerDir(const float x, const float y)
|
||||
{
|
||||
if (!mVisible || (x == mLastDirectionX && y == mLastDirectionY)) return;
|
||||
MyGUI::ISubWidget* main = mPlayerArrow->getSubWidgetMain();
|
||||
MyGUI::RotatingSkin* rotatingSubskin = main->castType<MyGUI::RotatingSkin>();
|
||||
rotatingSubskin->setCenter(MyGUI::IntPoint(16,16));
|
||||
float angle = std::atan2(x,y);
|
||||
rotatingSubskin->setAngle(angle);
|
||||
|
||||
mLastDirectionX = x;
|
||||
mLastDirectionY = y;
|
||||
}
|
||||
|
||||
void MapWindow::onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
if (!mGlobal)
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
|
||||
void MapWindow::onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id)
|
||||
{
|
||||
if (_id!=MyGUI::MouseButton::Left) return;
|
||||
|
||||
if (!mGlobal)
|
||||
{
|
||||
MyGUI::IntPoint diff = MyGUI::IntPoint(_left, _top) - mLastDragPos;
|
||||
mLocalMap->setViewOffset( mLocalMap->getViewOffset() + diff );
|
||||
|
||||
mLastDragPos = MyGUI::IntPoint(_left, _top);
|
||||
}
|
||||
}
|
||||
|
||||
void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
mGlobal = !mGlobal;
|
||||
mGlobalMap->setVisible(mGlobal);
|
||||
mLocalMap->setVisible(!mGlobal);
|
||||
|
||||
mButton->setCaption( mGlobal ? "Local" : "World" );
|
||||
}
|
||||
|
||||
void MapWindow::onPinToggled()
|
||||
{
|
||||
mWindowManager.setMinimapVisibility(!mPinned);
|
||||
}
|
34
apps/openmw/mwgui/map_window.hpp
Normal file
34
apps/openmw/mwgui/map_window.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef MWGUI_MAPWINDOW_H
|
||||
#define MWGUI_MAPWINDOW_H
|
||||
|
||||
#include "layouts.hpp"
|
||||
#include "window_pinnable_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class MapWindow : public MWGui::WindowPinnableBase, public LocalMapBase
|
||||
{
|
||||
public:
|
||||
MapWindow(WindowManager& parWindowManager);
|
||||
virtual ~MapWindow(){}
|
||||
|
||||
void setPlayerPos(const float x, const float y);
|
||||
void setPlayerDir(const float x, const float y);
|
||||
void setCellName(const std::string& cellName);
|
||||
|
||||
private:
|
||||
void onDragStart(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onMouseDrag(MyGUI::Widget* _sender, int _left, int _top, MyGUI::MouseButton _id);
|
||||
void onWorldButtonClicked(MyGUI::Widget* _sender);
|
||||
|
||||
MyGUI::ScrollView* mGlobalMap;
|
||||
MyGUI::ImageBox* mPlayerArrow;
|
||||
MyGUI::Button* mButton;
|
||||
MyGUI::IntPoint mLastDragPos;
|
||||
bool mGlobal;
|
||||
|
||||
protected:
|
||||
virtual void onPinToggled();
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -7,6 +7,7 @@
|
|||
#include "window_base.hpp"
|
||||
#include "window_manager.hpp"
|
||||
|
||||
#undef MessageBox
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
|
|
@ -12,10 +12,23 @@ using namespace MWGui;
|
|||
const int StatsWindow::lineHeight = 18;
|
||||
|
||||
StatsWindow::StatsWindow (WindowManager& parWindowManager)
|
||||
: WindowBase("openmw_stats_window_layout.xml", parWindowManager)
|
||||
: WindowPinnableBase("openmw_stats_window_layout.xml", parWindowManager)
|
||||
, skillAreaWidget(NULL)
|
||||
, skillClientWidget(NULL)
|
||||
, skillScrollerWidget(NULL)
|
||||
, lastPos(0)
|
||||
, clientHeight(0)
|
||||
, majorSkills()
|
||||
, minorSkills()
|
||||
, miscSkills()
|
||||
, skillValues()
|
||||
, skillWidgetMap()
|
||||
, factionWidgetMap()
|
||||
, factions()
|
||||
, birthSignId()
|
||||
, reputation(0)
|
||||
, bounty(0)
|
||||
, skillWidgets()
|
||||
{
|
||||
setCoord(0,0,498, 342);
|
||||
|
||||
|
@ -368,3 +381,8 @@ void StatsWindow::updateScroller()
|
|||
skillScrollerWidget->setScrollRange(std::max(clientHeight - skillClientWidget->getHeight(), 0));
|
||||
skillScrollerWidget->setScrollPage(std::max(skillClientWidget->getHeight() - lineHeight, 0));
|
||||
}
|
||||
|
||||
void StatsWindow::onPinToggled()
|
||||
{
|
||||
mWindowManager.setHMSVisibility(!mPinned);
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
#include <utility>
|
||||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
#include "window_base.hpp"
|
||||
#include "window_pinnable_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class WindowManager;
|
||||
|
||||
class StatsWindow : public WindowBase
|
||||
class StatsWindow : public WindowPinnableBase
|
||||
{
|
||||
public:
|
||||
typedef std::pair<std::string, int> Faction;
|
||||
|
@ -74,6 +74,9 @@ namespace MWGui
|
|||
std::string birthSignId;
|
||||
int reputation, bounty;
|
||||
std::vector<MyGUI::WidgetPtr> skillWidgets; //< Skills and other information
|
||||
|
||||
protected:
|
||||
virtual void onPinToggled();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
#include "../mwmechanics/stat.hpp"
|
||||
|
||||
#undef MYGUI_EXPORT
|
||||
#define MYGUI_EXPORT
|
||||
|
||||
/*
|
||||
This file contains various custom widgets used in OpenMW.
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "review.hpp"
|
||||
#include "dialogue.hpp"
|
||||
#include "dialogue_history.hpp"
|
||||
#include "map_window.hpp"
|
||||
#include "stats_window.hpp"
|
||||
#include "messagebox.hpp"
|
||||
#include "container.hpp"
|
||||
|
@ -15,6 +16,8 @@
|
|||
#include "journalwindow.hpp"
|
||||
#include "charactercreation.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
@ -23,15 +26,40 @@ using namespace MWGui;
|
|||
|
||||
WindowManager::WindowManager(MWWorld::Environment& environment,
|
||||
const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string logpath)
|
||||
: environment(environment)
|
||||
: mGuiManager(NULL)
|
||||
, environment(environment)
|
||||
, hud(NULL)
|
||||
, map(NULL)
|
||||
, menu(NULL)
|
||||
, stats(NULL)
|
||||
, mMessageBoxManager(NULL)
|
||||
, console(NULL)
|
||||
, mJournal(NULL)
|
||||
, dialogueWindow(nullptr)
|
||||
, mCharGen(NULL)
|
||||
, playerClass()
|
||||
, playerName()
|
||||
, playerRaceId()
|
||||
, playerBirthSignId()
|
||||
, playerAttributes()
|
||||
, playerMajorSkills()
|
||||
, playerMinorSkills()
|
||||
, playerSkillValues()
|
||||
, playerHealth()
|
||||
, playerMagicka()
|
||||
, playerFatigue()
|
||||
, gui(NULL)
|
||||
, mode(GM_Game)
|
||||
, nextMode(GM_Game)
|
||||
, needModeChange(false)
|
||||
, garbageDialogs()
|
||||
, shown(GW_ALL)
|
||||
, allowed(newGame ? GW_None : GW_ALL)
|
||||
, showFPSLevel(fpsLevel)
|
||||
, mFPS(0.0f)
|
||||
, mTriangleCount(0)
|
||||
, mBatchCount(0)
|
||||
{
|
||||
showFPSLevel = fpsLevel;
|
||||
|
||||
// Set up the GUI system
|
||||
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath);
|
||||
|
@ -47,7 +75,7 @@ WindowManager::WindowManager(MWWorld::Environment& environment,
|
|||
|
||||
hud = new HUD(w,h, showFPSLevel);
|
||||
menu = new MainMenu(w,h);
|
||||
map = new MapWindow();
|
||||
map = new MapWindow(*this);
|
||||
stats = new StatsWindow(*this);
|
||||
console = new Console(w,h, environment, extensions);
|
||||
mJournal = new JournalWindow(*this);
|
||||
|
@ -157,71 +185,58 @@ void WindowManager::updateVisible()
|
|||
// Mouse is visible whenever we're not in game mode
|
||||
MyGUI::PointerManager::getInstance().setVisible(isGuiMode());
|
||||
|
||||
// If in game mode, don't show anything.
|
||||
if(mode == GM_Game) //Use a switch/case structure
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch(mode) {
|
||||
case GM_Game:
|
||||
// If in game mode, don't show anything.
|
||||
break;
|
||||
case GM_MainMenu:
|
||||
menu->setVisible(true);
|
||||
break;
|
||||
case GM_Console:
|
||||
console->enable();
|
||||
break;
|
||||
case GM_Name:
|
||||
case GM_Race:
|
||||
case GM_Class:
|
||||
case GM_ClassPick:
|
||||
case GM_ClassCreate:
|
||||
case GM_Birth:
|
||||
case GM_ClassGenerate:
|
||||
case GM_Review:
|
||||
mCharGen->spawnDialog(mode);
|
||||
break;
|
||||
case GM_Inventory:
|
||||
{
|
||||
// First, compute the effective set of windows to show.
|
||||
// This is controlled both by what windows the
|
||||
// user has opened/closed (the 'shown' variable) and by what
|
||||
// windows we are allowed to show (the 'allowed' var.)
|
||||
int eff = shown & allowed;
|
||||
|
||||
if(mode == GM_MainMenu)
|
||||
{
|
||||
// Enable the main menu
|
||||
menu->setVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode == GM_Console)
|
||||
{
|
||||
console->enable();
|
||||
return;
|
||||
}
|
||||
|
||||
//There must be a more elegant solution
|
||||
if (mode == GM_Name || mode == GM_Race || mode == GM_Class || mode == GM_ClassPick || mode == GM_ClassCreate || mode == GM_Birth || mode == GM_ClassGenerate || mode == GM_Review)
|
||||
{
|
||||
mCharGen->spawnDialog(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode == GM_Inventory)
|
||||
{
|
||||
// Ah, inventory mode. First, compute the effective set of
|
||||
// windows to show. This is controlled both by what windows the
|
||||
// user has opened/closed (the 'shown' variable) and by what
|
||||
// windows we are allowed to show (the 'allowed' var.)
|
||||
int eff = shown & allowed;
|
||||
|
||||
// Show the windows we want
|
||||
map -> setVisible( (eff & GW_Map) != 0 );
|
||||
stats -> setVisible( (eff & GW_Stats) != 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == GM_Dialogue)
|
||||
{
|
||||
dialogueWindow->open();
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode == GM_InterMessageBox)
|
||||
{
|
||||
if(!mMessageBoxManager->isInteractiveMessageBox()) {
|
||||
setGuiMode(GM_Game);
|
||||
// Show the windows we want
|
||||
map -> setVisible( (eff & GW_Map) != 0 );
|
||||
stats -> setVisible( (eff & GW_Stats) != 0 );
|
||||
break;
|
||||
}
|
||||
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)
|
||||
|
@ -348,7 +363,6 @@ void WindowManager::updateSkillArea()
|
|||
|
||||
void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
|
||||
{
|
||||
std::cout << "dialogue a la poubelle";
|
||||
assert(dialog);
|
||||
if (!dialog)
|
||||
return;
|
||||
|
@ -446,3 +460,27 @@ void WindowManager::setPlayerDir(const float x, const float y)
|
|||
map->setPlayerDir(x,y);
|
||||
hud->setPlayerDir(x,y);
|
||||
}
|
||||
|
||||
void WindowManager::setHMSVisibility(bool visible)
|
||||
{
|
||||
hud->setBottomLeftVisibility(visible, hud->weapBox->getVisible(), hud->spellBox->getVisible());
|
||||
}
|
||||
|
||||
void WindowManager::setMinimapVisibility(bool visible)
|
||||
{
|
||||
hud->setBottomRightVisibility(hud->effectBox->getVisible(), visible);
|
||||
}
|
||||
|
||||
void WindowManager::toggleFogOfWar()
|
||||
{
|
||||
map->toggleFogOfWar();
|
||||
hud->toggleFogOfWar();
|
||||
}
|
||||
|
||||
int WindowManager::toggleFps()
|
||||
{
|
||||
showFPSLevel = (showFPSLevel+1)%3;
|
||||
hud->setFpsLevel(showFPSLevel);
|
||||
Settings::Manager::setInt("fps", "HUD", showFPSLevel);
|
||||
return showFPSLevel;
|
||||
}
|
||||
|
|
|
@ -158,10 +158,20 @@ namespace MWGui
|
|||
void changeCell(MWWorld::Ptr::CellStore* cell); ///< change the active cell
|
||||
void setPlayerPos(const float x, const float y); ///< set player position in map space
|
||||
void setPlayerDir(const float x, const float y); ///< set player view direction in map space
|
||||
|
||||
|
||||
void toggleFogOfWar();
|
||||
|
||||
int toggleFps();
|
||||
///< toggle fps display @return resulting fps level
|
||||
|
||||
void setInteriorMapTexture(const int x, const int y);
|
||||
///< set the index of the map texture that should be used (for interiors)
|
||||
|
||||
// sets the visibility of the hud health/magicka/stamina bars
|
||||
void setHMSVisibility(bool visible);
|
||||
// sets the visibility of the hud minimap
|
||||
void setMinimapVisibility(bool visible);
|
||||
|
||||
template<typename T>
|
||||
void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr.
|
||||
void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted.
|
||||
|
|
33
apps/openmw/mwgui/window_pinnable_base.cpp
Normal file
33
apps/openmw/mwgui/window_pinnable_base.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "window_pinnable_base.hpp"
|
||||
#include "window_manager.hpp"
|
||||
|
||||
using namespace MWGui;
|
||||
|
||||
WindowPinnableBase::WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager)
|
||||
: WindowBase(parLayout, parWindowManager), mPinned(false), mVisible(false)
|
||||
{
|
||||
MyGUI::WindowPtr t = static_cast<MyGUI::WindowPtr>(mMainWidget);
|
||||
t->eventWindowButtonPressed += MyGUI::newDelegate(this, &WindowPinnableBase::onWindowButtonPressed);
|
||||
}
|
||||
|
||||
void WindowPinnableBase::setVisible(bool b)
|
||||
{
|
||||
// Pinned windows can not be hidden
|
||||
if (mPinned && !b)
|
||||
return;
|
||||
|
||||
WindowBase::setVisible(b);
|
||||
mVisible = b;
|
||||
}
|
||||
|
||||
void WindowPinnableBase::onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName)
|
||||
{
|
||||
if ("PinToggle" == eventName)
|
||||
{
|
||||
mPinned = !mPinned;
|
||||
onPinToggled();
|
||||
}
|
||||
|
||||
eventDone(this);
|
||||
}
|
||||
|
28
apps/openmw/mwgui/window_pinnable_base.hpp
Normal file
28
apps/openmw/mwgui/window_pinnable_base.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef MWGUI_WINDOW_PINNABLE_BASE_H
|
||||
#define MWGUI_WINDOW_PINNABLE_BASE_H
|
||||
|
||||
#include "window_base.hpp"
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
class WindowManager;
|
||||
|
||||
class WindowPinnableBase: public WindowBase
|
||||
{
|
||||
public:
|
||||
WindowPinnableBase(const std::string& parLayout, WindowManager& parWindowManager);
|
||||
void setVisible(bool b);
|
||||
|
||||
private:
|
||||
void onWindowButtonPressed(MyGUI::Window* sender, const std::string& eventName);
|
||||
|
||||
protected:
|
||||
virtual void onPinToggled() = 0;
|
||||
|
||||
bool mPinned;
|
||||
bool mVisible;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -65,12 +65,16 @@ namespace MWInput
|
|||
A_QuickLoad,
|
||||
A_QuickMenu,
|
||||
A_GameMenu,
|
||||
A_ToggleWeapon,
|
||||
A_ToggleSpell,
|
||||
|
||||
A_ToggleFps, // Toggle FPS display (this is temporary)
|
||||
|
||||
A_LAST // Marker for the last item
|
||||
};
|
||||
|
||||
// Class that handles all input and key bindings for OpenMW
|
||||
class InputImpl : public Ogre::FrameListener
|
||||
class InputImpl
|
||||
{
|
||||
OEngine::Input::DispatcherPtr disp;
|
||||
OEngine::Render::OgreRenderer &ogre;
|
||||
|
@ -86,6 +90,43 @@ namespace MWInput
|
|||
|
||||
/* InputImpl Methods */
|
||||
|
||||
void toggleFps()
|
||||
{
|
||||
windows.toggleFps();
|
||||
}
|
||||
|
||||
void toggleSpell()
|
||||
{
|
||||
DrawState state = player.getDrawState();
|
||||
if(state == DrawState_Weapon || state == DrawState_Nothing)
|
||||
{
|
||||
player.setDrawState(DrawState_Spell);
|
||||
std::cout << "Player has now readied his hands for spellcasting!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
player.setDrawState(DrawState_Nothing);
|
||||
std::cout << "Player does not have any kind of attack ready now.\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void toggleWeapon()
|
||||
{
|
||||
DrawState state = player.getDrawState();
|
||||
if(state == DrawState_Spell || state == DrawState_Nothing)
|
||||
{
|
||||
player.setDrawState(DrawState_Weapon);
|
||||
std::cout << "Player is now drawing his weapon.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
player.setDrawState(DrawState_Nothing);
|
||||
std::cout << "Player does not have any kind of attack ready now.\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void screenshot()
|
||||
{
|
||||
mEngine.screenshot();
|
||||
|
@ -197,11 +238,14 @@ namespace MWInput
|
|||
"Auto Move");
|
||||
disp->funcs.bind(A_ToggleWalk, boost::bind(&InputImpl::toggleWalking, this),
|
||||
"Toggle Walk/Run");
|
||||
|
||||
disp->funcs.bind(A_ToggleWeapon,boost::bind(&InputImpl::toggleWeapon,this),
|
||||
"Draw Weapon");
|
||||
disp->funcs.bind(A_ToggleSpell,boost::bind(&InputImpl::toggleSpell,this),
|
||||
"Ready hands");
|
||||
disp->funcs.bind(A_ToggleFps, boost::bind(&InputImpl::toggleFps, this),
|
||||
"Toggle FPS display");
|
||||
// Add the exit listener
|
||||
ogre.getRoot()->addFrameListener(&exit);
|
||||
// Add ourselves as a frame listener to catch movement keys
|
||||
ogre.getRoot()->addFrameListener(this);
|
||||
|
||||
// Set up the mouse handler and tell it about the player camera
|
||||
mouse = MouseLookEventPtr(new MouseLookEvent(player.getRenderer()->getCamera()));
|
||||
|
@ -244,6 +288,9 @@ namespace MWInput
|
|||
disp->bind(A_AutoMove, KC_Z);
|
||||
disp->bind(A_ToggleSneak, KC_X);
|
||||
disp->bind(A_ToggleWalk, KC_C);
|
||||
disp->bind(A_ToggleWeapon,KC_F);
|
||||
disp->bind(A_ToggleSpell,KC_R);
|
||||
disp->bind(A_ToggleFps, KC_F10);
|
||||
|
||||
// Key bindings for polled keys
|
||||
// NOTE: These keys are constantly being polled. Only add keys that must be checked each frame.
|
||||
|
@ -262,7 +309,7 @@ namespace MWInput
|
|||
}
|
||||
|
||||
//NOTE: Used to check for movement keys
|
||||
bool frameRenderingQueued (const Ogre::FrameEvent &evt)
|
||||
void update ()
|
||||
{
|
||||
// Tell OIS to handle all input events
|
||||
input.capture();
|
||||
|
@ -276,7 +323,7 @@ namespace MWInput
|
|||
windows.update();
|
||||
|
||||
// Disable movement in Gui mode
|
||||
if (windows.isGuiMode()) return true;
|
||||
if (windows.isGuiMode()) return;
|
||||
|
||||
// Configure player movement according to keyboard input. Actual movement will
|
||||
// be done in the physics system.
|
||||
|
@ -305,8 +352,6 @@ namespace MWInput
|
|||
}
|
||||
else
|
||||
player.setForwardBackward (0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Switch between gui modes. Besides controlling the Gui windows
|
||||
|
@ -358,4 +403,9 @@ namespace MWInput
|
|||
{
|
||||
impl->setGuiMode(mode);
|
||||
}
|
||||
|
||||
void MWInputManager::update()
|
||||
{
|
||||
impl->update();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,8 @@ namespace MWInput
|
|||
OMW::Engine& engine);
|
||||
~MWInputManager();
|
||||
|
||||
void update();
|
||||
|
||||
void setGuiMode(MWGui::GuiMode mode);
|
||||
};
|
||||
}
|
||||
|
|
77
apps/openmw/mwmechanics/actors.cpp
Normal file
77
apps/openmw/mwmechanics/actors.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
|
||||
#include "actors.hpp"
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
void Actors::updateActor (const MWWorld::Ptr& ptr, float duration)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused)
|
||||
{
|
||||
if (!paused && ptr.getRefData().getHandle()!="player")
|
||||
MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip (
|
||||
MWWorld::Class::get (ptr).getNpcStats (ptr), mEnvironment);
|
||||
}
|
||||
|
||||
Actors::Actors (MWWorld::Environment& environment) : mEnvironment (environment), mDuration (0) {}
|
||||
|
||||
void Actors::addActor (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mActors.insert (ptr);
|
||||
}
|
||||
|
||||
void Actors::removeActor (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mActors.erase (ptr);
|
||||
}
|
||||
|
||||
void Actors::dropActors (const MWWorld::Ptr::CellStore *cellStore)
|
||||
{
|
||||
std::set<MWWorld::Ptr>::iterator iter = mActors.begin();
|
||||
|
||||
while (iter!=mActors.end())
|
||||
if (iter->getCell()==cellStore)
|
||||
{
|
||||
mActors.erase (iter++);
|
||||
}
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
|
||||
void Actors::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, float duration,
|
||||
bool paused)
|
||||
{
|
||||
mDuration += duration;
|
||||
|
||||
if (mDuration>=0.25)
|
||||
{
|
||||
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter)
|
||||
{
|
||||
updateActor (*iter, mDuration);
|
||||
|
||||
if (iter->getTypeName()==typeid (ESM::NPC).name())
|
||||
updateNpc (*iter, mDuration, paused);
|
||||
}
|
||||
|
||||
mDuration = 0;
|
||||
}
|
||||
|
||||
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end();
|
||||
++iter)
|
||||
{
|
||||
Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter);
|
||||
|
||||
if (vector!=Ogre::Vector3::ZERO)
|
||||
movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector));
|
||||
}
|
||||
}
|
||||
}
|
51
apps/openmw/mwmechanics/actors.hpp
Normal file
51
apps/openmw/mwmechanics/actors.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#ifndef GAME_MWMECHANICS_ACTORS_H
|
||||
#define GAME_MWMECHANICS_ACTORS_H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Vector3;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Environment;
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class Actors
|
||||
{
|
||||
MWWorld::Environment& mEnvironment;
|
||||
std::set<MWWorld::Ptr> mActors;
|
||||
float mDuration;
|
||||
|
||||
void updateActor (const MWWorld::Ptr& ptr, float duration);
|
||||
|
||||
void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused);
|
||||
|
||||
public:
|
||||
|
||||
Actors (MWWorld::Environment& environment);
|
||||
|
||||
void addActor (const MWWorld::Ptr& ptr);
|
||||
///< Register an actor for stats management
|
||||
|
||||
void removeActor (const MWWorld::Ptr& ptr);
|
||||
///< Deregister an actor for stats management
|
||||
|
||||
void dropActors (const MWWorld::Ptr::CellStore *cellStore);
|
||||
///< Deregister all actors in the given cell.
|
||||
|
||||
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
|
||||
float duration, bool paused);
|
||||
///< Update actor stats and store desired velocity vectors in \a movement
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "stat.hpp"
|
||||
#include "magiceffects.hpp"
|
||||
#include "spells.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
@ -14,7 +15,7 @@ namespace MWMechanics
|
|||
Stat<int> mAttributes[8];
|
||||
DynamicStat<int> mDynamic[3]; // health, magicka, fatigue
|
||||
int mLevel;
|
||||
std::set<std::string> mAbilities;
|
||||
Spells mSpells;
|
||||
MagicEffects mMagicEffects;
|
||||
};
|
||||
}
|
||||
|
|
13
apps/openmw/mwmechanics/drawstate.hpp
Normal file
13
apps/openmw/mwmechanics/drawstate.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef GAME_MWMECHANICS_DRAWSTATE_H
|
||||
#define GAME_MWMECHANICS_DRAWSTATE_H
|
||||
|
||||
#undef DrawState
|
||||
|
||||
enum DrawState
|
||||
{
|
||||
DrawState_Weapon = 0,
|
||||
DrawState_Spell = 1,
|
||||
DrawState_Nothing = 2,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -23,7 +23,7 @@ namespace MWMechanics
|
|||
|
||||
// reset
|
||||
creatureStats.mLevel = player->npdt52.level;
|
||||
creatureStats.mAbilities.clear();
|
||||
creatureStats.mSpells.clear();
|
||||
creatureStats.mMagicEffects = MagicEffects();
|
||||
|
||||
for (int i=0; i<27; ++i)
|
||||
|
@ -71,7 +71,7 @@ namespace MWMechanics
|
|||
for (std::vector<std::string>::const_iterator iter (race->powers.list.begin());
|
||||
iter!=race->powers.list.end(); ++iter)
|
||||
{
|
||||
insertSpell (*iter, ptr);
|
||||
creatureStats.mSpells.add (*iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace MWMechanics
|
|||
for (std::vector<std::string>::const_iterator iter (sign->powers.list.begin());
|
||||
iter!=sign->powers.list.end(); ++iter)
|
||||
{
|
||||
insertSpell (*iter, ptr);
|
||||
creatureStats.mSpells.add (*iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,59 +159,14 @@ namespace MWMechanics
|
|||
creatureStats.mDynamic[i].setCurrent (creatureStats.mDynamic[i].getModified());
|
||||
}
|
||||
|
||||
void MechanicsManager::insertSpell (const std::string& id, MWWorld::Ptr& creature)
|
||||
{
|
||||
MWMechanics::CreatureStats& creatureStats =
|
||||
MWWorld::Class::get (creature).getCreatureStats (creature);
|
||||
|
||||
const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (id);
|
||||
|
||||
switch (spell->data.type)
|
||||
{
|
||||
case ESM::Spell::ST_Ability:
|
||||
|
||||
if (creatureStats.mAbilities.find (id)==creatureStats.mAbilities.end())
|
||||
{
|
||||
creatureStats.mAbilities.insert (id);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// TODO ST_SPELL, ST_Blight, ST_Disease, ST_Curse, ST_Power
|
||||
|
||||
default:
|
||||
|
||||
std::cout
|
||||
<< "adding unsupported spell type (" << spell->data.type
|
||||
<< ") to creature: " << id << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void MechanicsManager::adjustMagicEffects (MWWorld::Ptr& creature)
|
||||
{
|
||||
MWMechanics::CreatureStats& creatureStats =
|
||||
MWWorld::Class::get (creature).getCreatureStats (creature);
|
||||
|
||||
MagicEffects now;
|
||||
MagicEffects now = creatureStats.mSpells.getMagicEffects (mEnvironment);
|
||||
|
||||
for (std::set<std::string>::const_iterator iter (creatureStats.mAbilities.begin());
|
||||
iter!=creatureStats.mAbilities.end(); ++iter)
|
||||
{
|
||||
const ESM::Spell *spell = mEnvironment.mWorld->getStore().spells.find (*iter);
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->effects.list.begin();
|
||||
iter!=spell->effects.list.end(); ++iter)
|
||||
{
|
||||
if (iter->range==0) // self
|
||||
{
|
||||
EffectParam param;
|
||||
param.mMagnitude = iter->magnMax; // TODO calculate magnitude
|
||||
now.add (EffectKey (*iter), param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add effects from other spell types, active spells and equipment
|
||||
/// \todo add effects from active spells and equipment
|
||||
|
||||
MagicEffects diff = MagicEffects::diff (creatureStats.mMagicEffects, now);
|
||||
|
||||
|
@ -222,14 +177,14 @@ namespace MWMechanics
|
|||
|
||||
MechanicsManager::MechanicsManager (MWWorld::Environment& environment)
|
||||
: mEnvironment (environment), mUpdatePlayer (true), mClassSelected (false),
|
||||
mRaceSelected (false)
|
||||
mRaceSelected (false), mActors (environment)
|
||||
{
|
||||
buildPlayer();
|
||||
}
|
||||
|
||||
void MechanicsManager::addActor (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mActors.insert (ptr);
|
||||
mActors.addActor (ptr);
|
||||
}
|
||||
|
||||
void MechanicsManager::removeActor (const MWWorld::Ptr& ptr)
|
||||
|
@ -237,7 +192,7 @@ namespace MWMechanics
|
|||
if (ptr==mWatched)
|
||||
mWatched = MWWorld::Ptr();
|
||||
|
||||
mActors.erase (ptr);
|
||||
mActors.removeActor (ptr);
|
||||
}
|
||||
|
||||
void MechanicsManager::dropActors (const MWWorld::Ptr::CellStore *cellStore)
|
||||
|
@ -245,16 +200,7 @@ namespace MWMechanics
|
|||
if (!mWatched.isEmpty() && mWatched.getCell()==cellStore)
|
||||
mWatched = MWWorld::Ptr();
|
||||
|
||||
std::set<MWWorld::Ptr>::iterator iter = mActors.begin();
|
||||
|
||||
while (iter!=mActors.end())
|
||||
if (iter->getCell()==cellStore)
|
||||
{
|
||||
//std::cout << "Erasing an actor";
|
||||
mActors.erase (iter++);
|
||||
}
|
||||
else
|
||||
++iter;
|
||||
mActors.dropActors (cellStore);
|
||||
}
|
||||
|
||||
void MechanicsManager::watchActor (const MWWorld::Ptr& ptr)
|
||||
|
@ -262,7 +208,8 @@ namespace MWMechanics
|
|||
mWatched = ptr;
|
||||
}
|
||||
|
||||
void MechanicsManager::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement)
|
||||
void MechanicsManager::update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement,
|
||||
float duration, bool paused)
|
||||
{
|
||||
if (!mWatched.isEmpty())
|
||||
{
|
||||
|
@ -345,14 +292,7 @@ namespace MWMechanics
|
|||
mEnvironment.mWindowManager->configureSkills (majorSkills, minorSkills);
|
||||
}
|
||||
|
||||
for (std::set<MWWorld::Ptr>::iterator iter (mActors.begin()); iter!=mActors.end();
|
||||
++iter)
|
||||
{
|
||||
Ogre::Vector3 vector = MWWorld::Class::get (*iter).getMovementVector (*iter);
|
||||
|
||||
if (vector!=Ogre::Vector3::ZERO)
|
||||
movement.push_back (std::make_pair (iter->getRefData().getHandle(), vector));
|
||||
}
|
||||
mActors.update (movement, duration, paused);
|
||||
}
|
||||
|
||||
void MechanicsManager::setPlayerName (const std::string& name)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef GAME_MWMECHANICS_MECHANICSMANAGER_H
|
||||
#define GAME_MWMECHANICS_MECHANICSMANAGER_H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
@ -9,6 +8,7 @@
|
|||
|
||||
#include "creaturestats.hpp"
|
||||
#include "npcstats.hpp"
|
||||
#include "actors.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
|
@ -25,20 +25,18 @@ namespace MWMechanics
|
|||
class MechanicsManager
|
||||
{
|
||||
MWWorld::Environment& mEnvironment;
|
||||
std::set<MWWorld::Ptr> mActors;
|
||||
MWWorld::Ptr mWatched;
|
||||
CreatureStats mWatchedCreature;
|
||||
NpcStats mWatchedNpc;
|
||||
bool mUpdatePlayer;
|
||||
bool mClassSelected;
|
||||
bool mRaceSelected;
|
||||
Actors mActors;
|
||||
|
||||
void buildPlayer();
|
||||
///< build player according to stored class/race/birthsign information. Will
|
||||
/// default to the values of the ESM::NPC object, if no explicit information is given.
|
||||
|
||||
void insertSpell (const std::string& id, MWWorld::Ptr& creature);
|
||||
|
||||
void adjustMagicEffects (MWWorld::Ptr& creature);
|
||||
|
||||
public:
|
||||
|
@ -60,8 +58,12 @@ namespace MWMechanics
|
|||
///< On each update look for changes in a previously registered actor and update the
|
||||
/// GUI accordingly.
|
||||
|
||||
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement);
|
||||
void update (std::vector<std::pair<std::string, Ogre::Vector3> >& movement, float duration,
|
||||
bool paused);
|
||||
///< Update actor stats and store desired velocity vectors in \a movement
|
||||
///
|
||||
/// \param paused In game type does not currently advance (this usually means some GUI
|
||||
/// component is up).
|
||||
|
||||
void setPlayerName (const std::string& name);
|
||||
///< Set player name.
|
||||
|
|
|
@ -2,14 +2,19 @@
|
|||
#define GAME_MWMECHANICS_NPCSTATS_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "stat.hpp"
|
||||
#include "drawstate.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief Additional stats for NPCs
|
||||
///
|
||||
/// For non-NPC-specific stats, see the CreatureStats struct.
|
||||
///
|
||||
/// \note For technical reasons the spell list and the currently selected spell is also handled by
|
||||
/// CreatureStats, even though they are actually NPC stats.
|
||||
|
||||
struct NpcStats
|
||||
{
|
||||
|
@ -24,9 +29,10 @@ namespace MWMechanics
|
|||
bool mRun;
|
||||
bool mSneak;
|
||||
bool mCombat;
|
||||
DrawState mDrawState;
|
||||
|
||||
NpcStats() : mForceRun (false), mForceSneak (false), mRun (false), mSneak (false),
|
||||
mCombat (false) {}
|
||||
mCombat (false) , mDrawState(DrawState_Nothing) {}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
81
apps/openmw/mwmechanics/spells.cpp
Normal file
81
apps/openmw/mwmechanics/spells.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
|
||||
#include "spells.hpp"
|
||||
|
||||
#include <components/esm/loadspel.hpp>
|
||||
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
#include "magiceffects.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
void Spells::addSpell (const ESM::Spell *spell, MagicEffects& effects) const
|
||||
{
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter = spell->effects.list.begin();
|
||||
iter!=spell->effects.list.end(); ++iter)
|
||||
{
|
||||
EffectParam param;
|
||||
param.mMagnitude = iter->magnMax; /// \todo calculate magnitude
|
||||
effects.add (EffectKey (*iter), param);
|
||||
}
|
||||
}
|
||||
|
||||
Spells::TIterator Spells::begin() const
|
||||
{
|
||||
return mSpells.begin();
|
||||
}
|
||||
|
||||
Spells::TIterator Spells::end() const
|
||||
{
|
||||
return mSpells.end();
|
||||
}
|
||||
|
||||
void Spells::add (const std::string& spellId)
|
||||
{
|
||||
if (std::find (mSpells.begin(), mSpells.end(), spellId)!=mSpells.end())
|
||||
mSpells.push_back (spellId);
|
||||
}
|
||||
|
||||
void Spells::remove (const std::string& spellId)
|
||||
{
|
||||
TContainer::iterator iter = std::find (mSpells.begin(), mSpells.end(), spellId);
|
||||
|
||||
if (iter!=mSpells.end())
|
||||
mSpells.erase (iter);
|
||||
|
||||
if (spellId==mSelectedSpell)
|
||||
mSelectedSpell.clear();
|
||||
}
|
||||
|
||||
MagicEffects Spells::getMagicEffects (const MWWorld::Environment& environment) const
|
||||
{
|
||||
MagicEffects effects;
|
||||
|
||||
for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter)
|
||||
{
|
||||
const ESM::Spell *spell = environment.mWorld->getStore().spells.find (*iter);
|
||||
|
||||
if (spell->data.type==ESM::Spell::ST_Ability || spell->data.type==ESM::Spell::ST_Blight ||
|
||||
spell->data.type==ESM::Spell::ST_Disease || spell->data.type==ESM::Spell::ST_Curse)
|
||||
addSpell (spell, effects);
|
||||
}
|
||||
|
||||
return effects;
|
||||
}
|
||||
|
||||
void Spells::clear()
|
||||
{
|
||||
mSpells.clear();
|
||||
}
|
||||
|
||||
void Spells::setSelectedSpell (const std::string& spellId)
|
||||
{
|
||||
mSelectedSpell = spellId;
|
||||
}
|
||||
|
||||
const std::string Spells::getSelectedSpell() const
|
||||
{
|
||||
return mSelectedSpell;
|
||||
}
|
||||
}
|
66
apps/openmw/mwmechanics/spells.hpp
Normal file
66
apps/openmw/mwmechanics/spells.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#ifndef GAME_MWMECHANICS_SPELLS_H
|
||||
#define GAME_MWMECHANICS_SPELLS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Spell;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
struct Environment;
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class MagicEffects;
|
||||
|
||||
/// \brief Spell list
|
||||
///
|
||||
/// This class manages known spells as well as abilities, powers and permanent negative effects like
|
||||
/// diseaes.
|
||||
class Spells
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<std::string> TContainer;
|
||||
typedef TContainer::const_iterator TIterator;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<std::string> mSpells;
|
||||
std::string mSelectedSpell;
|
||||
|
||||
void addSpell (const ESM::Spell *, MagicEffects& effects) const;
|
||||
|
||||
public:
|
||||
|
||||
TIterator begin() const;
|
||||
|
||||
TIterator end() const;
|
||||
|
||||
void add (const std::string& spell);
|
||||
///< Adding a spell that is already listed in *this is a no-op.
|
||||
|
||||
void remove (const std::string& spell);
|
||||
///< If the spell to be removed is the selected spell, the selected spell will be changed to
|
||||
/// no spell (empty string).
|
||||
|
||||
MagicEffects getMagicEffects (const MWWorld::Environment& environment) const;
|
||||
///< Return sum of magic effects resulting from abilities, blights, deseases and curses.
|
||||
|
||||
void clear();
|
||||
///< Remove all spells of al types.
|
||||
|
||||
void setSelectedSpell (const std::string& spellId);
|
||||
///< This function does not verify, if the spell is available.
|
||||
|
||||
const std::string getSelectedSpell() const;
|
||||
///< May return an empty string.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -8,6 +8,15 @@ using namespace Ogre;
|
|||
using namespace MWRender;
|
||||
using namespace NifOgre;
|
||||
|
||||
Actors::~Actors(){
|
||||
|
||||
std::map<MWWorld::Ptr, Animation*>::iterator it = mAllActors.begin();
|
||||
for (; it != mAllActors.end(); ++it) {
|
||||
delete it->second;
|
||||
it->second = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Actors::setMwRoot(Ogre::SceneNode* root){
|
||||
mMwRoot = root;
|
||||
}
|
||||
|
@ -61,6 +70,7 @@ void Actors::insertCreature (const MWWorld::Ptr& ptr){
|
|||
insertBegin(ptr, true, true);
|
||||
CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr, mEnvironment, mRend);
|
||||
//mAllActors.insert(std::pair<MWWorld::Ptr, Animation*>(ptr,anim));
|
||||
delete mAllActors[ptr];
|
||||
mAllActors[ptr] = anim;
|
||||
//mAllActors.push_back(&anim);*/
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace MWRender{
|
|||
|
||||
public:
|
||||
Actors(OEngine::Render::OgreRenderer& _rend, MWWorld::Environment& _env): mRend(_rend), mEnvironment(_env){}
|
||||
~Actors(){}
|
||||
~Actors();
|
||||
void setMwRoot(Ogre::SceneNode* root);
|
||||
void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_);
|
||||
void insertCreature (const MWWorld::Ptr& ptr);
|
||||
|
|
|
@ -4,7 +4,30 @@
|
|||
namespace MWRender{
|
||||
std::map<std::string, int> Animation::mUniqueIDs;
|
||||
|
||||
Animation::~Animation(){
|
||||
Animation::Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend)
|
||||
: insert(NULL)
|
||||
, mRend(_rend)
|
||||
, mEnvironment(_env)
|
||||
, vecRotPos()
|
||||
, shapeparts()
|
||||
, time(0.0f)
|
||||
, startTime(0.0f)
|
||||
, stopTime(0.0f)
|
||||
, animate(0)
|
||||
, rindexI()
|
||||
, tindexI()
|
||||
, shapeNumber(0)
|
||||
, shapeIndexI()
|
||||
, shapes(NULL)
|
||||
, entityparts()
|
||||
, transformations(NULL)
|
||||
, textmappings(NULL)
|
||||
, base(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
Animation::~Animation()
|
||||
{
|
||||
}
|
||||
|
||||
std::string Animation::getUniqueID(std::string mesh){
|
||||
|
@ -103,6 +126,11 @@ namespace MWRender{
|
|||
void Animation::handleShapes(std::vector<Nif::NiTriShapeCopy>* allshapes, Ogre::Entity* creaturemodel, Ogre::SkeletonInstance *skel){
|
||||
shapeNumber = 0;
|
||||
|
||||
if (allshapes == NULL || creaturemodel == NULL || skel == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Nif::NiTriShapeCopy>::iterator allshapesiter;
|
||||
for(allshapesiter = allshapes->begin(); allshapesiter != allshapes->end(); allshapesiter++)
|
||||
|
||||
|
|
|
@ -60,14 +60,14 @@ class Animation{
|
|||
std::string getUniqueID(std::string mesh);
|
||||
|
||||
public:
|
||||
Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend): mRend(_rend), mEnvironment(_env), animate(0){};
|
||||
virtual void runAnimation(float timepassed) = 0;
|
||||
void startScript(std::string groupname, int mode, int loops);
|
||||
void stopScript();
|
||||
|
||||
|
||||
virtual ~Animation();
|
||||
Animation(MWWorld::Environment& _env, OEngine::Render::OgreRenderer& _rend);
|
||||
virtual void runAnimation(float timepassed) = 0;
|
||||
void startScript(std::string groupname, int mode, int loops);
|
||||
void stopScript();
|
||||
|
||||
|
||||
virtual ~Animation();
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "creatureanimation.hpp"
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
|
@ -20,6 +21,28 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, MWWorld::Environme
|
|||
std::string meshNumbered = mesh + getUniqueID(mesh) + ">|";
|
||||
NifOgre::NIFLoader::load(meshNumbered);
|
||||
base = mRend.getScene()->createEntity(meshNumbered);
|
||||
base->setVisibilityFlags(RV_Actors);
|
||||
|
||||
bool transparent = false;
|
||||
for (unsigned int i=0; i<base->getNumSubEntities(); ++i)
|
||||
{
|
||||
Ogre::MaterialPtr mat = base->getSubEntity(i)->getMaterial();
|
||||
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
|
||||
while (techIt.hasMoreElements())
|
||||
{
|
||||
Ogre::Technique* tech = techIt.getNext();
|
||||
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
|
||||
while (passIt.hasMoreElements())
|
||||
{
|
||||
Ogre::Pass* pass = passIt.getNext();
|
||||
|
||||
if (pass->getDepthWriteEnabled() == false)
|
||||
transparent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
|
||||
|
||||
std::string meshZero = mesh + "0000>|";
|
||||
|
||||
if((transformations = (NIFLoader::getSingletonPtr())->getAnim(meshZero))){
|
||||
|
|
|
@ -2,37 +2,283 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
#include "OgreRoot.h"
|
||||
#include "OgreRenderWindow.h"
|
||||
#include "OgreSceneManager.h"
|
||||
#include "OgreViewport.h"
|
||||
#include "OgreCamera.h"
|
||||
#include "OgreTextureManager.h"
|
||||
#include <OgreNode.h>
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreMaterial.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
|
||||
#include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include <components/esm/loadstat.hpp>
|
||||
#include <components/esm/loadpgrd.hpp>
|
||||
|
||||
#include "player.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
||||
Debugging::Debugging(OEngine::Physic::PhysicEngine* engine){
|
||||
eng = engine;
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
static const std::string PATHGRID_POINT_MATERIAL = "pathgridPointMaterial";
|
||||
static const std::string PATHGRID_LINE_MATERIAL = "pathgridLineMaterial";
|
||||
static const std::string DEBUGGING_GROUP = "debugging";
|
||||
static const int POINT_MESH_BASE = 35;
|
||||
|
||||
void Debugging::createGridMaterials()
|
||||
{
|
||||
if (mGridMatsCreated) return;
|
||||
|
||||
if (MaterialManager::getSingleton().getByName(PATHGRID_LINE_MATERIAL, DEBUGGING_GROUP).isNull())
|
||||
{
|
||||
MaterialPtr lineMatPtr = MaterialManager::getSingleton().create(PATHGRID_LINE_MATERIAL, DEBUGGING_GROUP);
|
||||
lineMatPtr->setReceiveShadows(false);
|
||||
lineMatPtr->getTechnique(0)->setLightingEnabled(true);
|
||||
lineMatPtr->getTechnique(0)->getPass(0)->setDiffuse(1,1,0,0);
|
||||
lineMatPtr->getTechnique(0)->getPass(0)->setAmbient(1,1,0);
|
||||
lineMatPtr->getTechnique(0)->getPass(0)->setSelfIllumination(1,1,0);
|
||||
}
|
||||
|
||||
if (MaterialManager::getSingleton().getByName(PATHGRID_POINT_MATERIAL, DEBUGGING_GROUP).isNull())
|
||||
{
|
||||
MaterialPtr pointMatPtr = MaterialManager::getSingleton().create(PATHGRID_POINT_MATERIAL, DEBUGGING_GROUP);
|
||||
pointMatPtr->setReceiveShadows(false);
|
||||
pointMatPtr->getTechnique(0)->setLightingEnabled(true);
|
||||
pointMatPtr->getTechnique(0)->getPass(0)->setDiffuse(1,0,0,0);
|
||||
pointMatPtr->getTechnique(0)->getPass(0)->setAmbient(1,0,0);
|
||||
pointMatPtr->getTechnique(0)->getPass(0)->setSelfIllumination(1,0,0);
|
||||
}
|
||||
mGridMatsCreated = true;
|
||||
}
|
||||
|
||||
void Debugging::destroyGridMaterials()
|
||||
{
|
||||
if (mGridMatsCreated)
|
||||
{
|
||||
MaterialManager::getSingleton().remove(PATHGRID_POINT_MATERIAL);
|
||||
MaterialManager::getSingleton().remove(PATHGRID_LINE_MATERIAL);
|
||||
mGridMatsCreated = false;
|
||||
}
|
||||
}
|
||||
|
||||
ManualObject *Debugging::createPathgridLines(const ESM::Pathgrid *pathgrid)
|
||||
{
|
||||
ManualObject *result = mSceneMgr->createManualObject();
|
||||
|
||||
result->begin(PATHGRID_LINE_MATERIAL, RenderOperation::OT_LINE_LIST);
|
||||
for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid->edges.begin();
|
||||
it != pathgrid->edges.end();
|
||||
it++)
|
||||
{
|
||||
const ESM::Pathgrid::Edge &edge = *it;
|
||||
const ESM::Pathgrid::Point &p1 = pathgrid->points[edge.v0], &p2 = pathgrid->points[edge.v1];
|
||||
Vector3 direction = (Vector3(p2.x, p2.y, p2.z) - Vector3(p1.x, p1.y, p1.z));
|
||||
Vector3 lineDisplacement = direction.crossProduct(Vector3::UNIT_Z).normalisedCopy();
|
||||
lineDisplacement = lineDisplacement * POINT_MESH_BASE +
|
||||
Vector3(0, 0, 10); // move lines up a little, so they will be less covered by meshes/landscape
|
||||
result->position(Vector3(p1.x, p1.y, p1.z) + lineDisplacement);
|
||||
result->position(Vector3(p2.x, p2.y, p2.z) + lineDisplacement);
|
||||
}
|
||||
result->end();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ManualObject *Debugging::createPathgridPoints(const ESM::Pathgrid *pathgrid)
|
||||
{
|
||||
ManualObject *result = mSceneMgr->createManualObject();
|
||||
const float height = POINT_MESH_BASE * sqrtf(2);
|
||||
|
||||
result->begin(PATHGRID_POINT_MATERIAL, RenderOperation::OT_TRIANGLE_STRIP);
|
||||
|
||||
bool first = true;
|
||||
uint32 startIndex = 0;
|
||||
for(ESM::Pathgrid::PointList::const_iterator it = pathgrid->points.begin();
|
||||
it != pathgrid->points.end();
|
||||
it++, startIndex += 6)
|
||||
{
|
||||
Vector3 pointPos(it->x, it->y, it->z);
|
||||
|
||||
if (!first)
|
||||
{
|
||||
// degenerate triangle from previous octahedron
|
||||
result->index(startIndex - 4); // 2nd point of previous octahedron
|
||||
result->index(startIndex); // start point of current octahedron
|
||||
}
|
||||
|
||||
result->position(pointPos + Vector3(0, 0, height)); // 0
|
||||
result->position(pointPos + Vector3(-POINT_MESH_BASE, -POINT_MESH_BASE, 0)); // 1
|
||||
result->position(pointPos + Vector3(POINT_MESH_BASE, -POINT_MESH_BASE, 0)); // 2
|
||||
result->position(pointPos + Vector3(POINT_MESH_BASE, POINT_MESH_BASE, 0)); // 3
|
||||
result->position(pointPos + Vector3(-POINT_MESH_BASE, POINT_MESH_BASE, 0)); // 4
|
||||
result->position(pointPos + Vector3(0, 0, -height)); // 5
|
||||
|
||||
result->index(startIndex + 0);
|
||||
result->index(startIndex + 1);
|
||||
result->index(startIndex + 2);
|
||||
result->index(startIndex + 5);
|
||||
result->index(startIndex + 3);
|
||||
result->index(startIndex + 4);
|
||||
// degenerates
|
||||
result->index(startIndex + 4);
|
||||
result->index(startIndex + 5);
|
||||
result->index(startIndex + 5);
|
||||
// end degenerates
|
||||
result->index(startIndex + 1);
|
||||
result->index(startIndex + 4);
|
||||
result->index(startIndex + 0);
|
||||
result->index(startIndex + 3);
|
||||
result->index(startIndex + 2);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
result->end();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Debugging::Debugging(SceneNode *mwRoot, MWWorld::Environment &env, OEngine::Physic::PhysicEngine *engine) :
|
||||
mMwRoot(mwRoot), mEnvironment(env), mEngine(engine),
|
||||
mSceneMgr(mwRoot->getCreator()),
|
||||
mPathgridEnabled(false),
|
||||
mInteriorPathgridNode(NULL), mPathGridRoot(NULL),
|
||||
mGridMatsCreated(false)
|
||||
{
|
||||
ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP);
|
||||
}
|
||||
|
||||
Debugging::~Debugging()
|
||||
{
|
||||
if (mPathgridEnabled)
|
||||
{
|
||||
togglePathgrid();
|
||||
}
|
||||
|
||||
ResourceGroupManager::getSingleton().destroyResourceGroup(DEBUGGING_GROUP);
|
||||
}
|
||||
|
||||
|
||||
bool Debugging::toggleRenderMode (int mode){
|
||||
switch (mode)
|
||||
switch (mode)
|
||||
{
|
||||
case MWWorld::World::Render_CollisionDebug:
|
||||
|
||||
// TODO use a proper function instead of accessing the member variable
|
||||
// directly.
|
||||
eng->setDebugRenderingMode (!eng->isDebugCreated);
|
||||
return eng->isDebugCreated;
|
||||
return mEngine->toggleDebugRendering();
|
||||
|
||||
case MWWorld::World::Render_Pathgrid:
|
||||
togglePathgrid();
|
||||
return mPathgridEnabled;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Debugging::cellAdded(MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
mActiveCells.push_back(store);
|
||||
if (mPathgridEnabled)
|
||||
enableCellPathgrid(store);
|
||||
}
|
||||
|
||||
void Debugging::cellRemoved(MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
mActiveCells.erase(std::remove(mActiveCells.begin(), mActiveCells.end(), store), mActiveCells.end());
|
||||
if (mPathgridEnabled)
|
||||
disableCellPathgrid(store);
|
||||
}
|
||||
|
||||
void Debugging::togglePathgrid()
|
||||
{
|
||||
mPathgridEnabled = !mPathgridEnabled;
|
||||
if (mPathgridEnabled)
|
||||
{
|
||||
createGridMaterials();
|
||||
|
||||
// add path grid meshes to already loaded cells
|
||||
mPathGridRoot = mMwRoot->createChildSceneNode();
|
||||
for(CellList::iterator it = mActiveCells.begin(); it != mActiveCells.end(); it++)
|
||||
{
|
||||
enableCellPathgrid(*it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove path grid meshes from already loaded cells
|
||||
for(CellList::iterator it = mActiveCells.begin(); it != mActiveCells.end(); it++)
|
||||
{
|
||||
disableCellPathgrid(*it);
|
||||
}
|
||||
mPathGridRoot->removeAndDestroyAllChildren();
|
||||
mSceneMgr->destroySceneNode(mPathGridRoot);
|
||||
mPathGridRoot = NULL;
|
||||
destroyGridMaterials();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugging::enableCellPathgrid(MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
ESM::Pathgrid *pathgrid = mEnvironment.mWorld->getStore().pathgrids.search(*store->cell);
|
||||
if (!pathgrid) return;
|
||||
|
||||
Vector3 cellPathGridPos(0, 0, 0);
|
||||
if (store->cell->isExterior())
|
||||
{
|
||||
cellPathGridPos.x = store->cell->data.gridX * ESM::Land::REAL_SIZE;
|
||||
cellPathGridPos.y = store->cell->data.gridY * ESM::Land::REAL_SIZE;
|
||||
}
|
||||
SceneNode *cellPathGrid = mPathGridRoot->createChildSceneNode(cellPathGridPos);
|
||||
cellPathGrid->attachObject(createPathgridLines(pathgrid));
|
||||
cellPathGrid->attachObject(createPathgridPoints(pathgrid));
|
||||
|
||||
if (store->cell->isExterior())
|
||||
{
|
||||
mExteriorPathgridNodes[std::make_pair(store->cell->data.gridX, store->cell->data.gridY)] = cellPathGrid;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(mInteriorPathgridNode == NULL);
|
||||
mInteriorPathgridNode = cellPathGrid;
|
||||
}
|
||||
}
|
||||
|
||||
void Debugging::disableCellPathgrid(MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
if (store->cell->isExterior())
|
||||
{
|
||||
ExteriorPathgridNodes::iterator it =
|
||||
mExteriorPathgridNodes.find(std::make_pair(store->cell->data.gridX, store->cell->data.gridY));
|
||||
if (it != mExteriorPathgridNodes.end())
|
||||
{
|
||||
destroyCellPathgridNode(it->second);
|
||||
mExteriorPathgridNodes.erase(it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mInteriorPathgridNode)
|
||||
{
|
||||
destroyCellPathgridNode(mInteriorPathgridNode);
|
||||
mInteriorPathgridNode = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Debugging::destroyCellPathgridNode(SceneNode *node)
|
||||
{
|
||||
mPathGridRoot->removeChild(node);
|
||||
destroyAttachedObjects(node);
|
||||
mSceneMgr->destroySceneNode(node);
|
||||
}
|
||||
|
||||
void Debugging::destroyAttachedObjects(SceneNode *node)
|
||||
{
|
||||
SceneNode::ObjectIterator objIt = node->getAttachedObjectIterator();
|
||||
while (objIt.hasMoreElements())
|
||||
{
|
||||
MovableObject *mesh = static_cast<MovableObject *>(objIt.getNext());
|
||||
mSceneMgr->destroyMovableObject(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <utility>
|
||||
#include <openengine/ogre/renderer.hpp>
|
||||
#include <openengine/bullet/physic.hpp>
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
@ -22,20 +23,58 @@ namespace Ogre
|
|||
namespace MWWorld
|
||||
{
|
||||
class World;
|
||||
class Environment;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Player;
|
||||
|
||||
class Debugging{
|
||||
OEngine::Physic::PhysicEngine* eng;
|
||||
class Debugging
|
||||
{
|
||||
OEngine::Physic::PhysicEngine* mEngine;
|
||||
Ogre::SceneManager *mSceneMgr;
|
||||
MWWorld::Environment& mEnvironment;
|
||||
|
||||
// Path grid stuff
|
||||
bool mPathgridEnabled;
|
||||
|
||||
public:
|
||||
Debugging(OEngine::Physic::PhysicEngine* engine);
|
||||
bool toggleRenderMode (int mode);
|
||||
};
|
||||
void togglePathgrid();
|
||||
|
||||
typedef std::vector<MWWorld::Ptr::CellStore *> CellList;
|
||||
CellList mActiveCells;
|
||||
|
||||
Ogre::SceneNode *mMwRoot;
|
||||
|
||||
Ogre::SceneNode *mPathGridRoot;
|
||||
|
||||
typedef std::map<std::pair<int,int>, Ogre::SceneNode *> ExteriorPathgridNodes;
|
||||
ExteriorPathgridNodes mExteriorPathgridNodes;
|
||||
Ogre::SceneNode *mInteriorPathgridNode;
|
||||
|
||||
void enableCellPathgrid(MWWorld::Ptr::CellStore *store);
|
||||
void disableCellPathgrid(MWWorld::Ptr::CellStore *store);
|
||||
|
||||
// utility
|
||||
void destroyCellPathgridNode(Ogre::SceneNode *node);
|
||||
void destroyAttachedObjects(Ogre::SceneNode *node);
|
||||
|
||||
// materials
|
||||
bool mGridMatsCreated;
|
||||
void createGridMaterials();
|
||||
void destroyGridMaterials();
|
||||
|
||||
// path grid meshes
|
||||
Ogre::ManualObject *createPathgridLines(const ESM::Pathgrid *pathgrid);
|
||||
Ogre::ManualObject *createPathgridPoints(const ESM::Pathgrid *pathgrid);
|
||||
public:
|
||||
Debugging(Ogre::SceneNode* mwRoot, MWWorld::Environment &env, OEngine::Physic::PhysicEngine *engine);
|
||||
~Debugging();
|
||||
bool toggleRenderMode (int mode);
|
||||
|
||||
void cellAdded(MWWorld::Ptr::CellStore* store);
|
||||
void cellRemoved(MWWorld::Ptr::CellStore* store);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
#include "renderingmanager.hpp"
|
||||
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "../mwgui/window_manager.hpp"
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include <OgreOverlayManager.h>
|
||||
#include <OgreMaterialManager.h>
|
||||
|
@ -10,16 +12,24 @@
|
|||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
||||
LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWWorld::Environment* env)
|
||||
LocalMap::LocalMap(OEngine::Render::OgreRenderer* rend, MWRender::RenderingManager* rendering, MWWorld::Environment* env) :
|
||||
mInterior(false), mCellX(0), mCellY(0)
|
||||
{
|
||||
mRendering = rend;
|
||||
mRenderingManager = rendering;
|
||||
mEnvironment = env;
|
||||
|
||||
|
||||
mCameraPosNode = mRendering->getScene()->getRootSceneNode()->createChildSceneNode();
|
||||
mCameraRotNode = mCameraPosNode->createChildSceneNode();
|
||||
mCameraNode = mCameraRotNode->createChildSceneNode();
|
||||
|
||||
mCellCamera = mRendering->getScene()->createCamera("CellCamera");
|
||||
mCellCamera->setProjectionType(PT_ORTHOGRAPHIC);
|
||||
// look down -y
|
||||
const float sqrt0pt5 = 0.707106781;
|
||||
mCellCamera->setOrientation(Quaternion(sqrt0pt5, -sqrt0pt5, 0, 0));
|
||||
|
||||
mCameraNode->attachObject(mCellCamera);
|
||||
}
|
||||
|
||||
LocalMap::~LocalMap()
|
||||
|
@ -27,13 +37,14 @@ LocalMap::~LocalMap()
|
|||
deleteBuffers();
|
||||
}
|
||||
|
||||
const Ogre::Vector2 LocalMap::rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle)
|
||||
{
|
||||
return Vector2( Math::Cos(angle) * (p.x - c.x) - Math::Sin(angle) * (p.y - c.y) + c.x,
|
||||
Math::Sin(angle) * (p.x - c.x) + Math::Cos(angle) * (p.y - c.y) + c.y);
|
||||
}
|
||||
|
||||
void LocalMap::deleteBuffers()
|
||||
{
|
||||
for (std::map<std::string, uint32*>::iterator it=mBuffers.begin();
|
||||
it != mBuffers.end(); ++it)
|
||||
{
|
||||
delete it->second;
|
||||
}
|
||||
mBuffers.clear();
|
||||
}
|
||||
|
||||
|
@ -70,9 +81,6 @@ void LocalMap::saveFogOfWar(MWWorld::Ptr::CellStore* cell)
|
|||
{
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z);
|
||||
/// \todo why is this workaround needed?
|
||||
min *= 1.3;
|
||||
max *= 1.3;
|
||||
Vector2 length = max-min;
|
||||
|
||||
// divide into segments
|
||||
|
@ -95,11 +103,15 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell)
|
|||
{
|
||||
mInterior = false;
|
||||
|
||||
mCameraRotNode->setOrientation(Quaternion::IDENTITY);
|
||||
|
||||
std::string name = "Cell_"+coordStr(cell->cell->data.gridX, cell->cell->data.gridY);
|
||||
|
||||
int x = cell->cell->data.gridX;
|
||||
int y = cell->cell->data.gridY;
|
||||
|
||||
mCameraPosNode->setPosition(Vector3(0,0,0));
|
||||
|
||||
render((x+0.5)*sSize, (-y-0.5)*sSize, -10000, 10000, sSize, sSize, name);
|
||||
}
|
||||
|
||||
|
@ -108,17 +120,41 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell,
|
|||
{
|
||||
mInterior = true;
|
||||
mBounds = bounds;
|
||||
|
||||
Vector2 z(bounds.getMaximum().y, bounds.getMinimum().y);
|
||||
Vector2 min(bounds.getMinimum().x, bounds.getMinimum().z);
|
||||
Vector2 max(bounds.getMaximum().x, bounds.getMaximum().z);
|
||||
|
||||
/// \todo why is this workaround needed?
|
||||
min *= 1.3;
|
||||
max *= 1.3;
|
||||
Vector2 z(mBounds.getMaximum().y, mBounds.getMinimum().y);
|
||||
|
||||
const Vector2& north = mEnvironment->mWorld->getNorthVector(cell);
|
||||
Radian angle(std::atan2(-north.x, -north.y));
|
||||
mAngle = angle.valueRadians();
|
||||
mCameraRotNode->setOrientation(Quaternion(Math::Cos(angle/2.f), 0, Math::Sin(angle/2.f), 0));
|
||||
|
||||
// rotate the cell and merge the rotated corners to the bounding box
|
||||
Vector2 _center(bounds.getCenter().x, bounds.getCenter().z);
|
||||
Vector3 _c1 = bounds.getCorner(AxisAlignedBox::NEAR_LEFT_BOTTOM);
|
||||
Vector3 _c2 = bounds.getCorner(AxisAlignedBox::FAR_LEFT_BOTTOM);
|
||||
Vector3 _c3 = bounds.getCorner(AxisAlignedBox::NEAR_RIGHT_BOTTOM);
|
||||
Vector3 _c4 = bounds.getCorner(AxisAlignedBox::FAR_RIGHT_BOTTOM);
|
||||
Vector2 c1(_c1.x, _c1.z);
|
||||
Vector2 c2(_c2.x, _c2.z);
|
||||
Vector2 c3(_c3.x, _c3.z);
|
||||
Vector2 c4(_c4.x, _c4.z);
|
||||
c1 = rotatePoint(c1, _center, mAngle);
|
||||
c2 = rotatePoint(c2, _center, mAngle);
|
||||
c3 = rotatePoint(c3, _center, mAngle);
|
||||
c4 = rotatePoint(c4, _center, mAngle);
|
||||
mBounds.merge(Vector3(c1.x, 0, c1.y));
|
||||
mBounds.merge(Vector3(c2.x, 0, c2.y));
|
||||
mBounds.merge(Vector3(c3.x, 0, c3.y));
|
||||
mBounds.merge(Vector3(c4.x, 0, c4.y));
|
||||
|
||||
Vector2 center(mBounds.getCenter().x, mBounds.getCenter().z);
|
||||
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
Vector2 max(mBounds.getMaximum().x, mBounds.getMaximum().z);
|
||||
|
||||
Vector2 length = max-min;
|
||||
Vector2 center(bounds.getCenter().x, bounds.getCenter().z);
|
||||
|
||||
mCameraPosNode->setPosition(Vector3(center.x, 0, center.y));
|
||||
|
||||
// divide into segments
|
||||
const int segsX = std::ceil( length.x / sSize );
|
||||
|
@ -133,7 +169,7 @@ void LocalMap::requestMap(MWWorld::Ptr::CellStore* cell,
|
|||
Vector2 start = min + Vector2(sSize*x,sSize*y);
|
||||
Vector2 newcenter = start + 4096;
|
||||
|
||||
render(newcenter.x, newcenter.y, z.y, z.x, sSize, sSize,
|
||||
render(newcenter.x - center.x, newcenter.y - center.y, z.y, z.x, sSize, sSize,
|
||||
cell->cell->name + "_" + coordStr(x,y));
|
||||
}
|
||||
}
|
||||
|
@ -152,8 +188,9 @@ void LocalMap::render(const float x, const float y,
|
|||
|
||||
// make everything visible
|
||||
mRendering->getScene()->setAmbientLight(ColourValue(1,1,1));
|
||||
mRenderingManager->disableLights();
|
||||
|
||||
mCellCamera->setPosition(Vector3(x, zhigh+100000, y));
|
||||
mCameraNode->setPosition(Vector3(x, zhigh+100000, y));
|
||||
//mCellCamera->setFarClipDistance( (zhigh-zlow) * 1.1 );
|
||||
mCellCamera->setFarClipDistance(0); // infinite
|
||||
|
||||
|
@ -187,7 +224,10 @@ void LocalMap::render(const float x, const float y,
|
|||
vp->setOverlaysEnabled(false);
|
||||
vp->setShadowsEnabled(false);
|
||||
vp->setBackgroundColour(ColourValue(0, 0, 0));
|
||||
//vp->setVisibilityMask( ... );
|
||||
vp->setVisibilityMask(RV_Map);
|
||||
|
||||
// use fallback techniques without shadows and without mrt
|
||||
vp->setMaterialScheme("Fallback");
|
||||
|
||||
rtt->update();
|
||||
|
||||
|
@ -202,16 +242,16 @@ void LocalMap::render(const float x, const float y,
|
|||
TU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
|
||||
|
||||
// create a buffer to use for dynamic operations
|
||||
uint32* buffer = new uint32[sFogOfWarResolution*sFogOfWarResolution];
|
||||
std::vector<uint32> buffer;
|
||||
buffer.resize(sFogOfWarResolution*sFogOfWarResolution);
|
||||
|
||||
// initialize to (0, 0, 0, 1)
|
||||
uint32* pointer = buffer;
|
||||
for (int p=0; p<sFogOfWarResolution*sFogOfWarResolution; ++p)
|
||||
{
|
||||
*(pointer+p) = (255 << 24);
|
||||
buffer[p] = (255 << 24);
|
||||
}
|
||||
|
||||
memcpy(tex2->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), buffer, sFogOfWarResolution*sFogOfWarResolution*4);
|
||||
memcpy(tex2->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &buffer[0], sFogOfWarResolution*sFogOfWarResolution*4);
|
||||
tex2->getBuffer()->unlock();
|
||||
|
||||
mBuffers[texture] = buffer;
|
||||
|
@ -220,13 +260,14 @@ void LocalMap::render(const float x, const float y,
|
|||
//rtt->writeContentsToFile("./" + texture + ".jpg");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mRenderingManager->enableLights();
|
||||
|
||||
// re-enable fog
|
||||
mRendering->getScene()->setFog(FOG_LINEAR, clr, 0, fStart, fEnd);
|
||||
}
|
||||
|
||||
void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction)
|
||||
void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation)
|
||||
{
|
||||
if (sFogOfWarSkip != 0)
|
||||
{
|
||||
|
@ -237,7 +278,19 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3&
|
|||
|
||||
// retrieve the x,y grid coordinates the player is in
|
||||
int x,y;
|
||||
Vector2 pos(position.x, position.z);
|
||||
Vector3 _pos(position.x, 0, position.z);
|
||||
Vector2 pos(_pos.x, _pos.z);
|
||||
|
||||
if (mInterior)
|
||||
{
|
||||
pos = rotatePoint(pos, Vector2(mBounds.getCenter().x, mBounds.getCenter().z), mAngle);
|
||||
}
|
||||
|
||||
|
||||
Vector3 playerdirection = -mCameraRotNode->convertWorldToLocalOrientation(orientation).zAxis();
|
||||
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
|
||||
if (!mInterior)
|
||||
{
|
||||
x = std::ceil(pos.x / sSize)-1;
|
||||
|
@ -247,9 +300,6 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3&
|
|||
}
|
||||
else
|
||||
{
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
min *= 1.3;
|
||||
|
||||
x = std::ceil((pos.x - min.x)/sSize)-1;
|
||||
y = std::ceil((pos.y - min.y)/sSize)-1;
|
||||
|
||||
|
@ -264,20 +314,17 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3&
|
|||
u = std::abs((pos.x - (sSize*x))/sSize);
|
||||
v = 1-std::abs((pos.y + (sSize*y))/sSize);
|
||||
texName = "Cell_"+coordStr(x,y);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 min(mBounds.getMinimum().x, mBounds.getMinimum().z);
|
||||
min *= 1.3;
|
||||
|
||||
u = (pos.x - min.x - sSize*x)/sSize;
|
||||
v = (pos.y - min.y - sSize*y)/sSize;
|
||||
|
||||
texName = mInteriorName + "_" + coordStr(x,y);
|
||||
}
|
||||
|
||||
mEnvironment->mWindowManager->setPlayerPos(u, v);
|
||||
mEnvironment->mWindowManager->setPlayerDir(direction.x, -direction.z);
|
||||
mEnvironment->mWindowManager->setPlayerDir(playerdirection.x, -playerdirection.z);
|
||||
|
||||
// explore radius (squared)
|
||||
const float sqrExploreRadius = 0.01 * sFogOfWarResolution*sFogOfWarResolution;
|
||||
|
@ -288,25 +335,23 @@ void LocalMap::updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3&
|
|||
{
|
||||
// get its buffer
|
||||
if (mBuffers.find(texName) == mBuffers.end()) return;
|
||||
uint32* buffer = mBuffers[texName];
|
||||
uint32* pointer = buffer;
|
||||
int i=0;
|
||||
for (int texV = 0; texV<sFogOfWarResolution; ++texV)
|
||||
{
|
||||
for (int texU = 0; texU<sFogOfWarResolution; ++texU)
|
||||
{
|
||||
float sqrDist = Math::Sqr(texU - u*sFogOfWarResolution) + Math::Sqr(texV - v*sFogOfWarResolution);
|
||||
uint32 clr = *pointer;
|
||||
uint32 clr = mBuffers[texName][i];
|
||||
uint8 alpha = (clr >> 24);
|
||||
alpha = std::min( alpha, (uint8) (std::max(0.f, std::min(1.f, (sqrDist/sqrExploreRadius)))*255) );
|
||||
*((uint32*)pointer) = (alpha << 24);
|
||||
mBuffers[texName][i] = (uint32) (alpha << 24);
|
||||
|
||||
// move to next texel
|
||||
++pointer;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// copy to the texture
|
||||
memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), buffer, sFogOfWarResolution*sFogOfWarResolution*4);
|
||||
memcpy(tex->getBuffer()->lock(HardwareBuffer::HBL_DISCARD), &mBuffers[texName][0], sFogOfWarResolution*sFogOfWarResolution*4);
|
||||
tex->getBuffer()->unlock();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,15 @@ namespace MWWorld
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
class RenderingManager;
|
||||
|
||||
///
|
||||
/// \brief Local map rendering
|
||||
///
|
||||
class LocalMap
|
||||
{
|
||||
public:
|
||||
LocalMap(OEngine::Render::OgreRenderer*, MWWorld::Environment* env);
|
||||
LocalMap(OEngine::Render::OgreRenderer*, MWRender::RenderingManager* rendering, MWWorld::Environment* env);
|
||||
~LocalMap();
|
||||
|
||||
/**
|
||||
|
@ -44,9 +46,9 @@ namespace MWRender
|
|||
* @remarks This is used to draw a "fog of war" effect
|
||||
* to hide areas on the map the player has not discovered yet.
|
||||
* @param position (OGRE coordinates)
|
||||
* @param view direction (OGRE coordinates)
|
||||
* @param camera orientation (OGRE coordinates)
|
||||
*/
|
||||
void updatePlayer (const Ogre::Vector3& position, const Ogre::Vector3& direction);
|
||||
void updatePlayer (const Ogre::Vector3& position, const Ogre::Quaternion& orientation);
|
||||
|
||||
/**
|
||||
* Save the fog of war for the current cell to disk.
|
||||
|
@ -58,6 +60,7 @@ namespace MWRender
|
|||
|
||||
private:
|
||||
OEngine::Render::OgreRenderer* mRendering;
|
||||
MWRender::RenderingManager* mRenderingManager;
|
||||
MWWorld::Environment* mEnvironment;
|
||||
|
||||
// 1024*1024 pixels for a cell
|
||||
|
@ -73,6 +76,12 @@ namespace MWRender
|
|||
static const int sSize = 8192;
|
||||
|
||||
Ogre::Camera* mCellCamera;
|
||||
Ogre::SceneNode* mCameraNode;
|
||||
Ogre::SceneNode* mCameraPosNode;
|
||||
Ogre::SceneNode* mCameraRotNode;
|
||||
|
||||
float mAngle;
|
||||
const Ogre::Vector2 rotatePoint(const Ogre::Vector2& p, const Ogre::Vector2& c, const float angle);
|
||||
|
||||
void render(const float x, const float y,
|
||||
const float zlow, const float zhigh,
|
||||
|
@ -86,7 +95,7 @@ namespace MWRender
|
|||
// a buffer for the "fog of war" texture of the current cell.
|
||||
// interior cells could be divided into multiple textures,
|
||||
// so we store in a map.
|
||||
std::map <std::string, Ogre::uint32*> mBuffers;
|
||||
std::map <std::string, std::vector<Ogre::uint32> > mBuffers;
|
||||
|
||||
void deleteBuffers();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "npcanimation.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "renderconst.hpp"
|
||||
|
||||
|
||||
using namespace Ogre;
|
||||
|
@ -65,6 +66,27 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, MWWorld::Environment& _env,O
|
|||
NifOgre::NIFLoader::load(smodel);
|
||||
|
||||
base = mRend.getScene()->createEntity(smodel);
|
||||
base->setVisibilityFlags(RV_Actors);
|
||||
bool transparent = false;
|
||||
for (unsigned int i=0; i<base->getNumSubEntities(); ++i)
|
||||
{
|
||||
Ogre::MaterialPtr mat = base->getSubEntity(i)->getMaterial();
|
||||
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
|
||||
while (techIt.hasMoreElements())
|
||||
{
|
||||
Ogre::Technique* tech = techIt.getNext();
|
||||
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
|
||||
while (passIt.hasMoreElements())
|
||||
{
|
||||
Ogre::Pass* pass = passIt.getNext();
|
||||
|
||||
if (pass->getDepthWriteEnabled() == false)
|
||||
transparent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
base->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
|
||||
|
||||
base->setSkipAnimationStateUpdate(true); //Magical line of code, this makes the bones
|
||||
//stay in the same place when we skipanim, or open a gui window
|
||||
|
||||
|
@ -218,6 +240,7 @@ Ogre::Entity* NpcAnimation::insertBoundedPart(const std::string &mesh, std::stri
|
|||
|
||||
NIFLoader::load(mesh);
|
||||
Entity* ent = mRend.getScene()->createEntity(mesh);
|
||||
ent->setVisibilityFlags(RV_Actors);
|
||||
|
||||
base->attachObjectToBone(bonename, ent);
|
||||
return ent;
|
||||
|
@ -227,9 +250,7 @@ void NpcAnimation::insertFreePart(const std::string &mesh, const std::string suf
|
|||
NIFLoader::load(meshNumbered);
|
||||
|
||||
Ogre::Entity* ent = mRend.getScene()->createEntity(meshNumbered);
|
||||
|
||||
|
||||
|
||||
ent->setVisibilityFlags(RV_Actors);
|
||||
|
||||
insert->attachObject(ent);
|
||||
entityparts.push_back(ent);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <OgreSceneNode.h>
|
||||
|
||||
#include <components/nifogre/ogre_nif_loader.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include "renderconst.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
|
||||
|
@ -88,38 +90,112 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
|
|||
NifOgre::NIFLoader::load(mesh);
|
||||
Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh);
|
||||
|
||||
if(!mIsStatic)
|
||||
|
||||
Ogre::Vector3 extents = ent->getBoundingBox().getSize();
|
||||
extents *= insert->getScale();
|
||||
float size = std::max(std::max(extents.x, extents.y), extents.z);
|
||||
|
||||
bool small = (size < Settings::Manager::getInt("small object size", "Viewing distance")) && Settings::Manager::getBool("limit small object distance", "Objects");
|
||||
|
||||
// do not fade out doors. that will cause holes and look stupid
|
||||
if (ptr.getTypeName().find("Door") != std::string::npos)
|
||||
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);
|
||||
|
||||
bool transparent = false;
|
||||
for (unsigned int i=0; i<ent->getNumSubEntities(); ++i)
|
||||
{
|
||||
Ogre::MaterialPtr mat = ent->getSubEntity(i)->getMaterial();
|
||||
Ogre::Material::TechniqueIterator techIt = mat->getTechniqueIterator();
|
||||
while (techIt.hasMoreElements())
|
||||
{
|
||||
Ogre::Technique* tech = techIt.getNext();
|
||||
Ogre::Technique::PassIterator passIt = tech->getPassIterator();
|
||||
while (passIt.hasMoreElements())
|
||||
{
|
||||
Ogre::Pass* pass = passIt.getNext();
|
||||
|
||||
if (pass->getDepthWriteEnabled() == false)
|
||||
transparent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!mIsStatic || !Settings::Manager::getBool("use static geometry", "Objects"))
|
||||
{
|
||||
insert->attachObject(ent);
|
||||
|
||||
ent->setRenderingDistance(small ? Settings::Manager::getInt("small object distance", "Viewing distance") : 0);
|
||||
ent->setVisibilityFlags(mIsStatic ? (small ? RV_StaticsSmall : RV_Statics) : RV_Misc);
|
||||
ent->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
|
||||
}
|
||||
else
|
||||
{
|
||||
Ogre::StaticGeometry* sg = 0;
|
||||
if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
|
||||
|
||||
/* if (transparent)
|
||||
{
|
||||
uniqueID = uniqueID +1;
|
||||
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
|
||||
//Create the scenenode and put it in the map
|
||||
mStaticGeometry[ptr.getCell()] = sg;
|
||||
if( mStaticGeometryAlpha.find(ptr.getCell()) == mStaticGeometryAlpha.end())
|
||||
{
|
||||
uniqueID = uniqueID +1;
|
||||
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
|
||||
mStaticGeometryAlpha[ptr.getCell()] = sg;
|
||||
}
|
||||
else
|
||||
sg = mStaticGeometryAlpha[ptr.getCell()];
|
||||
}
|
||||
else*/ if (small)
|
||||
{
|
||||
if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end())
|
||||
{
|
||||
uniqueID = uniqueID +1;
|
||||
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
|
||||
mStaticGeometrySmall[ptr.getCell()] = sg;
|
||||
|
||||
// This specifies the size of a single batch region.
|
||||
// If it is set too high:
|
||||
// - there will be problems choosing the correct lights
|
||||
// - the culling will be more inefficient
|
||||
// If it is set too low:
|
||||
// - there will be too many batches.
|
||||
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
|
||||
|
||||
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
|
||||
mBounds[ptr.getCell()].merge(ent->getBoundingBox());
|
||||
sg->setRenderingDistance(Settings::Manager::getInt("small object distance", "Viewing distance"));
|
||||
}
|
||||
else
|
||||
sg = mStaticGeometrySmall[ptr.getCell()];
|
||||
}
|
||||
else
|
||||
{
|
||||
sg = mStaticGeometry[ptr.getCell()];
|
||||
if( mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
|
||||
{
|
||||
|
||||
uniqueID = uniqueID +1;
|
||||
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
|
||||
mStaticGeometry[ptr.getCell()] = sg;
|
||||
}
|
||||
else
|
||||
sg = mStaticGeometry[ptr.getCell()];
|
||||
}
|
||||
|
||||
// This specifies the size of a single batch region.
|
||||
// If it is set too high:
|
||||
// - there will be problems choosing the correct lights
|
||||
// - the culling will be more inefficient
|
||||
// If it is set too low:
|
||||
// - there will be too many batches.
|
||||
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
|
||||
|
||||
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
|
||||
mBounds[ptr.getCell()].merge(insert->_getDerivedPosition());
|
||||
|
||||
sg->setVisibilityFlags(small ? RV_StaticsSmall : RV_Statics);
|
||||
|
||||
sg->setCastShadows(true);
|
||||
|
||||
sg->setRenderQueueGroup(transparent ? RQG_Alpha : RQG_Main);
|
||||
|
||||
mRenderer.getScene()->destroyEntity(ent);
|
||||
}
|
||||
|
@ -131,6 +207,7 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f
|
|||
assert(insert);
|
||||
Ogre::Light *light = mRenderer.getScene()->createLight();
|
||||
light->setDiffuseColour (r, g, b);
|
||||
mLights.push_back(light->getName());
|
||||
|
||||
float cval=0.0f, lval=0.0f, qval=0.0f;
|
||||
|
||||
|
@ -206,7 +283,21 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store)
|
|||
mRenderer.getScene()->destroyStaticGeometry (sg);
|
||||
sg = 0;
|
||||
}
|
||||
|
||||
if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end())
|
||||
{
|
||||
Ogre::StaticGeometry* sg = mStaticGeometrySmall[store];
|
||||
mStaticGeometrySmall.erase(store);
|
||||
mRenderer.getScene()->destroyStaticGeometry (sg);
|
||||
sg = 0;
|
||||
}
|
||||
/*if(mStaticGeometryAlpha.find(store) != mStaticGeometryAlpha.end())
|
||||
{
|
||||
Ogre::StaticGeometry* sg = mStaticGeometryAlpha[store];
|
||||
mStaticGeometryAlpha.erase(store);
|
||||
mRenderer.getScene()->destroyStaticGeometry (sg);
|
||||
sg = 0;
|
||||
}*/
|
||||
|
||||
if(mBounds.find(store) != mBounds.end())
|
||||
mBounds.erase(store);
|
||||
}
|
||||
|
@ -218,9 +309,50 @@ void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
|
|||
Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
|
||||
sg->build();
|
||||
}
|
||||
if(mStaticGeometrySmall.find(&cell) != mStaticGeometrySmall.end())
|
||||
{
|
||||
Ogre::StaticGeometry* sg = mStaticGeometrySmall[&cell];
|
||||
sg->build();
|
||||
}
|
||||
/*if(mStaticGeometryAlpha.find(&cell) != mStaticGeometryAlpha.end())
|
||||
{
|
||||
Ogre::StaticGeometry* sg = mStaticGeometryAlpha[&cell];
|
||||
sg->build();
|
||||
}*/
|
||||
}
|
||||
|
||||
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)
|
||||
{
|
||||
return mBounds[cell];
|
||||
}
|
||||
|
||||
void Objects::enableLights()
|
||||
{
|
||||
std::vector<std::string>::iterator it = mLights.begin();
|
||||
while (it != mLights.end())
|
||||
{
|
||||
if (mMwRoot->getCreator()->hasLight(*it))
|
||||
{
|
||||
mMwRoot->getCreator()->getLight(*it)->setVisible(true);
|
||||
++it;
|
||||
}
|
||||
else
|
||||
it = mLights.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void Objects::disableLights()
|
||||
{
|
||||
std::vector<std::string>::iterator it = mLights.begin();
|
||||
while (it != mLights.end())
|
||||
{
|
||||
if (mMwRoot->getCreator()->hasLight(*it))
|
||||
{
|
||||
mMwRoot->getCreator()->getLight(*it)->setVisible(false);
|
||||
++it;
|
||||
}
|
||||
else
|
||||
it = mLights.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,10 @@ class Objects{
|
|||
OEngine::Render::OgreRenderer &mRenderer;
|
||||
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
|
||||
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometry;
|
||||
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometrySmall;
|
||||
//std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometryAlpha;
|
||||
std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds;
|
||||
std::vector<std::string> mLights;
|
||||
Ogre::SceneNode* mMwRoot;
|
||||
bool mIsStatic;
|
||||
static int uniqueID;
|
||||
|
@ -43,6 +46,9 @@ public:
|
|||
void insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh);
|
||||
void insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, float radius);
|
||||
|
||||
void enableLights();
|
||||
void disableLights();
|
||||
|
||||
Ogre::AxisAlignedBox getDimensions(MWWorld::Ptr::CellStore*);
|
||||
///< get a bounding box that encloses all objects in the specified cell
|
||||
|
||||
|
|
304
apps/openmw/mwrender/occlusionquery.cpp
Normal file
304
apps/openmw/mwrender/occlusionquery.cpp
Normal file
|
@ -0,0 +1,304 @@
|
|||
#include "occlusionquery.hpp"
|
||||
#include "renderconst.hpp"
|
||||
|
||||
#include <OgreRenderSystem.h>
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreBillboardSet.h>
|
||||
#include <OgreHardwareOcclusionQuery.h>
|
||||
#include <OgreEntity.h>
|
||||
#include <OgreSubEntity.h>
|
||||
#include <OgreMaterialManager.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;
|
||||
}
|
||||
|
||||
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->setCastShadows(false);
|
||||
mBBQueryTotal->setDefaultDimensions(150, 150);
|
||||
mBBQueryTotal->createBillboard(Vector3::ZERO);
|
||||
mBBQueryTotal->setMaterialName("QueryTotalPixels");
|
||||
mBBQueryTotal->setRenderQueueGroup(RQG_OcclusionQuery+1);
|
||||
mBBNodeReal->attachObject(mBBQueryTotal);
|
||||
|
||||
mBBQueryVisible = mRendering->getScene()->createBillboardSet(1);
|
||||
mBBQueryVisible->setCastShadows(false);
|
||||
mBBQueryVisible->setDefaultDimensions(150, 150);
|
||||
mBBQueryVisible->createBillboard(Vector3::ZERO);
|
||||
mBBQueryVisible->setMaterialName("QueryVisiblePixels");
|
||||
mBBQueryVisible->setRenderQueueGroup(RQG_OcclusionQuery+1);
|
||||
mBBNodeReal->attachObject(mBBQueryVisible);
|
||||
|
||||
mBBQuerySingleObject = mRendering->getScene()->createBillboardSet(1);
|
||||
/// \todo ideally this should occupy exactly 1 pixel on the screen
|
||||
mBBQuerySingleObject->setCastShadows(false);
|
||||
mBBQuerySingleObject->setDefaultDimensions(0.003, 0.003);
|
||||
mBBQuerySingleObject->createBillboard(Vector3::ZERO);
|
||||
mBBQuerySingleObject->setMaterialName("QueryVisiblePixels");
|
||||
mBBQuerySingleObject->setRenderQueueGroup(RQG_OcclusionQuery);
|
||||
mObjectNode->attachObject(mBBQuerySingleObject);
|
||||
|
||||
mRendering->getScene()->addRenderObjectListener(this);
|
||||
mRendering->getScene()->addRenderQueueListener(this);
|
||||
mDoQuery = 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 == RQG_SkiesLate)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool OcclusionQuery::isPotentialOccluder(Ogre::SceneNode* node)
|
||||
{
|
||||
bool result = false;
|
||||
for (unsigned int i=0; i < node->numAttachedObjects(); ++i)
|
||||
{
|
||||
MovableObject* ob = node->getAttachedObject(i);
|
||||
std::string type = ob->getMovableType();
|
||||
if (type == "Entity")
|
||||
{
|
||||
Entity* ent = static_cast<Entity*>(ob);
|
||||
for (unsigned int j=0; j < ent->getNumSubEntities(); ++j)
|
||||
{
|
||||
// if any sub entity has a material with depth write off,
|
||||
// consider the object as not an occluder
|
||||
MaterialPtr mat = ent->getSubEntity(j)->getMaterial();
|
||||
|
||||
Material::TechniqueIterator techIt = mat->getTechniqueIterator();
|
||||
while (techIt.hasMoreElements())
|
||||
{
|
||||
Technique* tech = techIt.getNext();
|
||||
Technique::PassIterator passIt = tech->getPassIterator();
|
||||
while (passIt.hasMoreElements())
|
||||
{
|
||||
Pass* pass = passIt.getNext();
|
||||
|
||||
if (pass->getDepthWriteEnabled() == false)
|
||||
return false;
|
||||
else
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
105
apps/openmw/mwrender/occlusionquery.hpp
Normal file
105
apps/openmw/mwrender/occlusionquery.hpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
#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();
|
||||
|
||||
/**
|
||||
* Checks if the objects held by this scenenode
|
||||
* can be considered as potential occluders
|
||||
* (which might not be the case when transparency is involved)
|
||||
* @param Scene node
|
||||
*/
|
||||
bool isPotentialOccluder(Ogre::SceneNode* node);
|
||||
|
||||
/**
|
||||
* @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
|
62
apps/openmw/mwrender/renderconst.hpp
Normal file
62
apps/openmw/mwrender/renderconst.hpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#ifndef GAME_RENDER_CONST_H
|
||||
#define GAME_RENDER_CONST_H
|
||||
|
||||
#include <OgreRenderQueue.h>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
// Render queue groups
|
||||
enum RenderQueueGroups
|
||||
{
|
||||
// Sky early (atmosphere, clouds, moons)
|
||||
RQG_SkiesEarly = Ogre::RENDER_QUEUE_SKIES_EARLY,
|
||||
|
||||
RQG_Main = Ogre::RENDER_QUEUE_MAIN,
|
||||
|
||||
RQG_Water = Ogre::RENDER_QUEUE_7+1,
|
||||
|
||||
RQG_Alpha = Ogre::RENDER_QUEUE_MAIN,
|
||||
|
||||
RQG_UnderWater = Ogre::RENDER_QUEUE_7+1,
|
||||
|
||||
RQG_OcclusionQuery = Ogre::RENDER_QUEUE_8,
|
||||
|
||||
// Sky late (sun & sun flare)
|
||||
RQG_SkiesLate = Ogre::RENDER_QUEUE_SKIES_LATE
|
||||
};
|
||||
|
||||
// Visibility flags
|
||||
enum VisibilityFlags
|
||||
{
|
||||
// Terrain
|
||||
RV_Terrain = 1,
|
||||
|
||||
// Statics (e.g. trees, houses)
|
||||
RV_Statics = 2,
|
||||
|
||||
// Small statics
|
||||
RV_StaticsSmall = 4,
|
||||
|
||||
// Water
|
||||
RV_Water = 8,
|
||||
|
||||
// Actors (player, npcs, creatures)
|
||||
RV_Actors = 16,
|
||||
|
||||
// Misc objects (containers, dynamic objects)
|
||||
RV_Misc = 32,
|
||||
|
||||
RV_Sky = 64,
|
||||
|
||||
// Sun glare (not visible in reflection)
|
||||
RV_Glare = 128,
|
||||
|
||||
RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water,
|
||||
|
||||
/// \todo markers (normally hidden)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -12,7 +12,12 @@
|
|||
#include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include <components/esm/loadstat.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "shadows.hpp"
|
||||
#include "shaderhelper.hpp"
|
||||
#include "localmap.hpp"
|
||||
#include "water.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
@ -20,16 +25,54 @@ using namespace Ogre;
|
|||
namespace MWRender {
|
||||
|
||||
RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment)
|
||||
:mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mDebugging(engine)
|
||||
:mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mSunEnabled(0)
|
||||
{
|
||||
mRendering.createScene("PlayerCam", 55, 5);
|
||||
mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5);
|
||||
|
||||
mWater = 0;
|
||||
|
||||
//The fog type must be set before any terrain objects are created as if the
|
||||
//fog type is set to FOG_NONE then the initially created terrain won't have any fog
|
||||
configureFog(1, ColourValue(1,1,1));
|
||||
|
||||
// Set default mipmap level (NB some APIs ignore this)
|
||||
TextureManager::getSingleton().setDefaultNumMipmaps(5);
|
||||
TextureManager::getSingleton().setDefaultNumMipmaps(Settings::Manager::getInt("num mipmaps", "General"));
|
||||
|
||||
// Set default texture filtering options
|
||||
TextureFilterOptions tfo;
|
||||
std::string filter = Settings::Manager::getString("texture filtering", "General");
|
||||
if (filter == "anisotropic") tfo = TFO_ANISOTROPIC;
|
||||
else if (filter == "trilinear") tfo = TFO_TRILINEAR;
|
||||
else if (filter == "bilinear") tfo = TFO_BILINEAR;
|
||||
else if (filter == "none") tfo = TFO_NONE;
|
||||
|
||||
MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
|
||||
MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 );
|
||||
|
||||
// Load resources
|
||||
ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
|
||||
|
||||
// disable unsupported effects
|
||||
const RenderSystemCapabilities* caps = Root::getSingleton().getRenderSystem()->getCapabilities();
|
||||
if (caps->getNumMultiRenderTargets() < 2)
|
||||
Settings::Manager::setBool("shader", "Water", false);
|
||||
if (!caps->isShaderProfileSupported("fp40") && !caps->isShaderProfileSupported("ps_4_0"))
|
||||
Settings::Manager::setBool("enabled", "Shadows", false);
|
||||
|
||||
// note that the order is important here
|
||||
if (useMRT())
|
||||
{
|
||||
CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "gbuffer");
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbuffer", true);
|
||||
CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "Underwater");
|
||||
CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "gbufferFinalizer");
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbufferFinalizer", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
CompositorManager::getSingleton().addCompositor(mRendering.getViewport(), "UnderwaterNoMRT");
|
||||
}
|
||||
|
||||
// Turn the entire scene (represented by the 'root' node) -90
|
||||
// degrees around the x axis. This makes Z go upwards, and Y go into
|
||||
// the screen (when x is to the right.) This is the orientation that
|
||||
|
@ -40,31 +83,39 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
|
|||
mMwRoot->pitch(Degree(-90));
|
||||
mObjects.setMwRoot(mMwRoot);
|
||||
mActors.setMwRoot(mMwRoot);
|
||||
|
||||
//used to obtain ingame information of ogre objects (which are faced or selected)
|
||||
mRaySceneQuery = mRendering.getScene()->createRayQuery(Ray());
|
||||
|
||||
Ogre::SceneNode *playerNode = mMwRoot->createChildSceneNode ("player");
|
||||
playerNode->pitch(Degree(90));
|
||||
Ogre::SceneNode *cameraYawNode = playerNode->createChildSceneNode();
|
||||
Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode();
|
||||
cameraPitchNode->attachObject(mRendering.getCamera());
|
||||
|
||||
|
||||
mShadows = new Shadows(&mRendering);
|
||||
mShaderHelper = new ShaderHelper(this);
|
||||
|
||||
mTerrainManager = new TerrainManager(mRendering.getScene(), this,
|
||||
environment);
|
||||
|
||||
//mSkyManager = 0;
|
||||
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
|
||||
|
||||
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
|
||||
|
||||
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
|
||||
mSun = 0;
|
||||
|
||||
mLocalMap = new MWRender::LocalMap(&mRendering, &environment);
|
||||
mDebugging = new Debugging(mMwRoot, environment, engine);
|
||||
mLocalMap = new MWRender::LocalMap(&mRendering, this, &environment);
|
||||
}
|
||||
|
||||
RenderingManager::~RenderingManager ()
|
||||
{
|
||||
//TODO: destroy mSun?
|
||||
delete mPlayer;
|
||||
delete mSkyManager;
|
||||
delete mDebugging;
|
||||
delete mTerrainManager;
|
||||
delete mLocalMap;
|
||||
delete mOcclusionQuery;
|
||||
}
|
||||
|
||||
MWRender::SkyManager* RenderingManager::getSkyManager()
|
||||
|
@ -88,14 +139,35 @@ OEngine::Render::Fader* RenderingManager::getFader()
|
|||
return mRendering.getFader();
|
||||
}
|
||||
|
||||
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){
|
||||
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
mObjects.removeCell(store);
|
||||
mActors.removeCell(store);
|
||||
mDebugging->cellRemoved(store);
|
||||
if (store->cell->isExterior())
|
||||
mTerrainManager->cellRemoved(store);
|
||||
}
|
||||
|
||||
void RenderingManager::removeWater ()
|
||||
{
|
||||
if(mWater){
|
||||
mWater->setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderingManager::toggleWater()
|
||||
{
|
||||
if (mWater)
|
||||
mWater->toggle();
|
||||
}
|
||||
|
||||
void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
|
||||
{
|
||||
mObjects.buildStaticGeometry (*store);
|
||||
mDebugging->cellAdded(store);
|
||||
if (store->cell->isExterior())
|
||||
mTerrainManager->cellAdded(store);
|
||||
waterAdded(store);
|
||||
}
|
||||
|
||||
void RenderingManager::addObject (const MWWorld::Ptr& ptr){
|
||||
|
@ -136,18 +208,44 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve
|
|||
void RenderingManager::update (float duration){
|
||||
|
||||
mActors.update (duration);
|
||||
|
||||
|
||||
mOcclusionQuery->update(duration);
|
||||
|
||||
mSkyManager->update(duration);
|
||||
|
||||
|
||||
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
|
||||
|
||||
mRendering.update(duration);
|
||||
|
||||
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );
|
||||
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealOrientation() );
|
||||
|
||||
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(), mSkyManager, store->cell);
|
||||
else
|
||||
mWater->changeCell(store->cell);
|
||||
mWater->setActive(true);
|
||||
}
|
||||
else
|
||||
removeWater();
|
||||
|
||||
}
|
||||
|
||||
void RenderingManager::setWaterHeight(const float height)
|
||||
{
|
||||
if (mWater)
|
||||
mWater->setHeight(height);
|
||||
}
|
||||
|
||||
void RenderingManager::skyEnable ()
|
||||
{
|
||||
if(mSkyManager)
|
||||
mSkyManager->enable();
|
||||
|
||||
mOcclusionQuery->setSunNode(mSkyManager->getSunNode());
|
||||
}
|
||||
|
||||
void RenderingManager::skyDisable ()
|
||||
|
@ -171,7 +269,7 @@ void RenderingManager::skySetDate (int day, int month)
|
|||
|
||||
int RenderingManager::skyGetMasserPhase() const
|
||||
{
|
||||
|
||||
|
||||
return mSkyManager->getMasserPhase();
|
||||
}
|
||||
|
||||
|
@ -187,17 +285,31 @@ void RenderingManager::skySetMoonColour (bool red){
|
|||
|
||||
bool RenderingManager::toggleRenderMode(int mode)
|
||||
{
|
||||
if (mode == MWWorld::World::Render_CollisionDebug)
|
||||
return mDebugging.toggleRenderMode(mode);
|
||||
if (mode != MWWorld::World::Render_Wireframe)
|
||||
return mDebugging->toggleRenderMode(mode);
|
||||
else // if (mode == MWWorld::World::Render_Wireframe)
|
||||
{
|
||||
if (mRendering.getCamera()->getPolygonMode() == PM_SOLID)
|
||||
{
|
||||
// disable compositors
|
||||
if (useMRT())
|
||||
{
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbuffer", false);
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbufferFinalizer", false);
|
||||
}
|
||||
|
||||
mRendering.getCamera()->setPolygonMode(PM_WIREFRAME);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// re-enable compositors
|
||||
if (useMRT())
|
||||
{
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbuffer", true);
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mRendering.getViewport(), "gbufferFinalizer", true);
|
||||
}
|
||||
|
||||
mRendering.getCamera()->setPolygonMode(PM_SOLID);
|
||||
return false;
|
||||
}
|
||||
|
@ -206,27 +318,29 @@ bool RenderingManager::toggleRenderMode(int mode)
|
|||
|
||||
void RenderingManager::configureFog(ESMS::CellStore<MWWorld::RefData> &mCell)
|
||||
{
|
||||
Ogre::ColourValue color;
|
||||
color.setAsABGR (mCell.cell->ambi.fog);
|
||||
Ogre::ColourValue color;
|
||||
color.setAsABGR (mCell.cell->ambi.fog);
|
||||
|
||||
configureFog(mCell.cell->ambi.fogDensity, color);
|
||||
configureFog(mCell.cell->ambi.fogDensity, color);
|
||||
}
|
||||
|
||||
void RenderingManager::configureFog(const float density, const Ogre::ColourValue& colour)
|
||||
{
|
||||
/// \todo make the viewing distance and fog start/end configurable
|
||||
{
|
||||
float max = Settings::Manager::getFloat("max viewing distance", "Viewing distance");
|
||||
|
||||
// right now we load 3x3 cells, so the maximum viewing distance we
|
||||
// can allow (to prevent objects suddenly popping up) equals:
|
||||
// 8192 * 0.69
|
||||
// ^ cell size ^ minimum density value used (clear weather)
|
||||
float low = 5652.48 / density / 2.f;
|
||||
float high = 5652.48 / density;
|
||||
float low = max / (density) * Settings::Manager::getFloat("fog start factor", "Viewing distance");
|
||||
float high = max / (density) * Settings::Manager::getFloat("fog end factor", "Viewing distance");
|
||||
|
||||
mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high);
|
||||
|
||||
mRendering.getCamera()->setFarClipDistance ( high );
|
||||
mRendering.getViewport()->setBackgroundColour (colour);
|
||||
mRendering.getScene()->setFog (FOG_LINEAR, colour, 0, low, high);
|
||||
|
||||
mRendering.getCamera()->setFarClipDistance ( max / density );
|
||||
mRendering.getViewport()->setBackgroundColour (colour);
|
||||
|
||||
CompositorInstance* inst = CompositorManager::getSingleton().getCompositorChain(mRendering.getViewport())->getCompositor("gbuffer");
|
||||
if (inst != 0)
|
||||
inst->getCompositor()->getTechnique(0)->getTargetPass(0)->getPass(0)->setClearColour(colour);
|
||||
if (mWater)
|
||||
mWater->setViewportBackground(colour);
|
||||
}
|
||||
|
||||
|
||||
|
@ -236,55 +350,62 @@ void RenderingManager::setAmbientMode()
|
|||
{
|
||||
case 0:
|
||||
|
||||
mRendering.getScene()->setAmbientLight(mAmbientColor);
|
||||
setAmbientColour(mAmbientColor);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
||||
mRendering.getScene()->setAmbientLight(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1));
|
||||
setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
mRendering.getScene()->setAmbientLight(ColourValue(1,1,1));
|
||||
setAmbientColour(ColourValue(1,1,1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderingManager::configureAmbient(ESMS::CellStore<MWWorld::RefData> &mCell)
|
||||
{
|
||||
mAmbientColor.setAsABGR (mCell.cell->ambi.ambient);
|
||||
setAmbientMode();
|
||||
mAmbientColor.setAsABGR (mCell.cell->ambi.ambient);
|
||||
setAmbientMode();
|
||||
|
||||
// Create a "sun" that shines light downwards. It doesn't look
|
||||
// completely right, but leave it for now.
|
||||
if(!mSun)
|
||||
{
|
||||
mSun = mRendering.getScene()->createLight();
|
||||
}
|
||||
Ogre::ColourValue colour;
|
||||
colour.setAsABGR (mCell.cell->ambi.sunlight);
|
||||
mSun->setDiffuseColour (colour);
|
||||
mSun->setType(Ogre::Light::LT_DIRECTIONAL);
|
||||
mSun->setDirection(0,-1,0);
|
||||
// Create a "sun" that shines light downwards. It doesn't look
|
||||
// completely right, but leave it for now.
|
||||
if(!mSun)
|
||||
{
|
||||
mSun = mRendering.getScene()->createLight();
|
||||
}
|
||||
Ogre::ColourValue colour;
|
||||
colour.setAsABGR (mCell.cell->ambi.sunlight);
|
||||
mSun->setDiffuseColour (colour);
|
||||
mSun->setType(Ogre::Light::LT_DIRECTIONAL);
|
||||
mSun->setDirection(0,-1,0);
|
||||
}
|
||||
// Switch through lighting modes.
|
||||
|
||||
void RenderingManager::toggleLight()
|
||||
{
|
||||
if (mAmbientMode==2)
|
||||
mAmbientMode = 0;
|
||||
else
|
||||
++mAmbientMode;
|
||||
if (mAmbientMode==2)
|
||||
mAmbientMode = 0;
|
||||
else
|
||||
++mAmbientMode;
|
||||
|
||||
switch (mAmbientMode)
|
||||
{
|
||||
case 0: std::cout << "Setting lights to normal\n"; break;
|
||||
case 1: std::cout << "Turning the lights up\n"; break;
|
||||
case 2: std::cout << "Turning the lights to full\n"; break;
|
||||
}
|
||||
switch (mAmbientMode)
|
||||
{
|
||||
case 0: std::cout << "Setting lights to normal\n"; break;
|
||||
case 1: std::cout << "Turning the lights up\n"; break;
|
||||
case 2: std::cout << "Turning the lights to full\n"; break;
|
||||
}
|
||||
|
||||
setAmbientMode();
|
||||
setAmbientMode();
|
||||
}
|
||||
void RenderingManager::checkUnderwater()
|
||||
{
|
||||
if(mWater)
|
||||
{
|
||||
mWater->checkUnderwater( mRendering.getCamera()->getRealPosition().y );
|
||||
}
|
||||
}
|
||||
|
||||
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
|
||||
|
@ -300,30 +421,43 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr)
|
|||
|
||||
void RenderingManager::setSunColour(const Ogre::ColourValue& colour)
|
||||
{
|
||||
if (!mSunEnabled) return;
|
||||
mSun->setDiffuseColour(colour);
|
||||
mSun->setSpecularColour(colour);
|
||||
mTerrainManager->setDiffuse(colour);
|
||||
}
|
||||
|
||||
void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour)
|
||||
{
|
||||
mRendering.getScene()->setAmbientLight(colour);
|
||||
mTerrainManager->setAmbient(colour);
|
||||
}
|
||||
|
||||
void RenderingManager::sunEnable()
|
||||
{
|
||||
if (mSun) mSun->setVisible(true);
|
||||
// Don't disable the light, as the shaders assume the first light to be directional.
|
||||
//if (mSun) mSun->setVisible(true);
|
||||
mSunEnabled = true;
|
||||
}
|
||||
|
||||
void RenderingManager::sunDisable()
|
||||
{
|
||||
if (mSun) mSun->setVisible(false);
|
||||
// Don't disable the light, as the shaders assume the first light to be directional.
|
||||
//if (mSun) mSun->setVisible(false);
|
||||
mSunEnabled = false;
|
||||
if (mSun)
|
||||
{
|
||||
mSun->setDiffuseColour(ColourValue(0,0,0));
|
||||
mSun->setSpecularColour(ColourValue(0,0,0));
|
||||
}
|
||||
}
|
||||
|
||||
void RenderingManager::setSunDirection(const Ogre::Vector3& direction)
|
||||
{
|
||||
// direction * -1 (because 'direction' is camera to sun vector and not sun to camera),
|
||||
// direction * -1 (because 'direction' is camera to sun vector and not sun to camera),
|
||||
// then convert from MW to ogre coordinates (swap y,z and make y negative)
|
||||
if (mSun) mSun->setDirection(Vector3(-direction.x, -direction.z, direction.y));
|
||||
|
||||
|
||||
mSkyManager->setSunDirection(direction);
|
||||
}
|
||||
|
||||
|
@ -345,4 +479,26 @@ void RenderingManager::preCellChange(MWWorld::Ptr::CellStore* cell)
|
|||
mLocalMap->saveFogOfWar(cell);
|
||||
}
|
||||
|
||||
void RenderingManager::disableLights()
|
||||
{
|
||||
mObjects.disableLights();
|
||||
sunDisable();
|
||||
}
|
||||
|
||||
void RenderingManager::enableLights()
|
||||
{
|
||||
mObjects.enableLights();
|
||||
sunEnable();
|
||||
}
|
||||
|
||||
const bool RenderingManager::useMRT()
|
||||
{
|
||||
return Settings::Manager::getBool("shader", "Water");
|
||||
}
|
||||
|
||||
Shadows* RenderingManager::getShadows()
|
||||
{
|
||||
return mShadows;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
|
||||
#include "sky.hpp"
|
||||
#include "terrain.hpp"
|
||||
#include "debugging.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -24,15 +25,12 @@
|
|||
#include "objects.hpp"
|
||||
#include "actors.hpp"
|
||||
#include "player.hpp"
|
||||
#include "localmap.hpp"
|
||||
#include "occlusionquery.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Camera;
|
||||
class Viewport;
|
||||
class SceneManager;
|
||||
class SceneNode;
|
||||
class RaySceneQuery;
|
||||
class Quaternion;
|
||||
class Vector3;
|
||||
}
|
||||
|
@ -45,7 +43,10 @@ namespace MWWorld
|
|||
namespace MWRender
|
||||
{
|
||||
|
||||
|
||||
class Shadows;
|
||||
class ShaderHelper;
|
||||
class LocalMap;
|
||||
class Water;
|
||||
|
||||
class RenderingManager: private RenderingInterface {
|
||||
|
||||
|
@ -59,6 +60,8 @@ class RenderingManager: private RenderingInterface {
|
|||
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment);
|
||||
virtual ~RenderingManager();
|
||||
|
||||
|
||||
|
||||
virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as
|
||||
/// MWWorld::Player has been rewritten to not need access
|
||||
/// to internal details of the rendering system anymore
|
||||
|
@ -67,7 +70,7 @@ class RenderingManager: private RenderingInterface {
|
|||
|
||||
void toggleLight();
|
||||
bool toggleRenderMode(int mode);
|
||||
|
||||
|
||||
OEngine::Render::Fader* getFader();
|
||||
|
||||
void removeCell (MWWorld::Ptr::CellStore *store);
|
||||
|
@ -75,6 +78,11 @@ class RenderingManager: private RenderingInterface {
|
|||
/// \todo this function should be removed later. Instead the rendering subsystems should track
|
||||
/// when rebatching is needed and update automatically at the end of each frame.
|
||||
void cellAdded (MWWorld::Ptr::CellStore *store);
|
||||
void waterAdded(MWWorld::Ptr::CellStore *store);
|
||||
|
||||
void removeWater();
|
||||
|
||||
static const bool useMRT();
|
||||
|
||||
void preCellChange (MWWorld::Ptr::CellStore* store);
|
||||
///< this event is fired immediately before changing cell
|
||||
|
@ -86,17 +94,29 @@ class RenderingManager: private RenderingInterface {
|
|||
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
|
||||
void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation);
|
||||
|
||||
void checkUnderwater();
|
||||
void setWaterHeight(const float height);
|
||||
void toggleWater();
|
||||
|
||||
/// \param store Cell the object was in previously (\a ptr has already been updated to the new cell).
|
||||
void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store);
|
||||
|
||||
void update (float duration);
|
||||
|
||||
|
||||
void setAmbientColour(const Ogre::ColourValue& colour);
|
||||
void setSunColour(const Ogre::ColourValue& colour);
|
||||
void setSunDirection(const Ogre::Vector3& direction);
|
||||
void sunEnable();
|
||||
void sunDisable();
|
||||
|
||||
|
||||
void disableLights();
|
||||
void enableLights();
|
||||
|
||||
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
|
||||
OcclusionQuery* getOcclusionQuery() { return mOcclusionQuery; };
|
||||
|
||||
Shadows* getShadows();
|
||||
|
||||
void setGlare(bool glare);
|
||||
void skyEnable ();
|
||||
void skyDisable ();
|
||||
|
@ -109,13 +129,13 @@ class RenderingManager: private RenderingInterface {
|
|||
|
||||
void requestMap (MWWorld::Ptr::CellStore* cell);
|
||||
///< request the local map for a cell
|
||||
|
||||
|
||||
/// configure fog according to cell
|
||||
void configureFog(ESMS::CellStore<MWWorld::RefData> &mCell);
|
||||
|
||||
|
||||
/// configure fog manually
|
||||
void configureFog(const float density, const Ogre::ColourValue& colour);
|
||||
|
||||
|
||||
void playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode,
|
||||
int number = 1);
|
||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||
|
@ -131,9 +151,17 @@ class RenderingManager: private RenderingInterface {
|
|||
private:
|
||||
|
||||
void setAmbientMode();
|
||||
|
||||
|
||||
bool mSunEnabled;
|
||||
|
||||
SkyManager* mSkyManager;
|
||||
|
||||
|
||||
OcclusionQuery* mOcclusionQuery;
|
||||
|
||||
TerrainManager* mTerrainManager;
|
||||
|
||||
MWRender::Water *mWater;
|
||||
|
||||
OEngine::Render::OgreRenderer &mRendering;
|
||||
|
||||
MWRender::Objects mObjects;
|
||||
|
@ -149,14 +177,18 @@ class RenderingManager: private RenderingInterface {
|
|||
/// that the OGRE coordinate system matches that used internally in
|
||||
/// Morrowind.
|
||||
Ogre::SceneNode *mMwRoot;
|
||||
Ogre::RaySceneQuery *mRaySceneQuery;
|
||||
|
||||
OEngine::Physic::PhysicEngine* mPhysicsEngine;
|
||||
|
||||
MWRender::Player *mPlayer;
|
||||
MWRender::Debugging mDebugging;
|
||||
|
||||
MWRender::Debugging *mDebugging;
|
||||
|
||||
MWRender::LocalMap* mLocalMap;
|
||||
|
||||
MWRender::Shadows* mShadows;
|
||||
|
||||
MWRender::ShaderHelper* mShaderHelper;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
308
apps/openmw/mwrender/shaderhelper.cpp
Normal file
308
apps/openmw/mwrender/shaderhelper.cpp
Normal file
|
@ -0,0 +1,308 @@
|
|||
#include "shaderhelper.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
#include "shadows.hpp"
|
||||
|
||||
#include <OgreHighLevelGpuProgramManager.h>
|
||||
#include <OgreHighLevelGpuProgram.h>
|
||||
#include <OgreGpuProgramParams.h>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
using namespace Ogre;
|
||||
using namespace MWRender;
|
||||
|
||||
ShaderHelper::ShaderHelper(RenderingManager* rend)
|
||||
{
|
||||
mRendering = rend;
|
||||
applyShaders();
|
||||
}
|
||||
|
||||
void ShaderHelper::applyShaders()
|
||||
{
|
||||
if (!Settings::Manager::getBool("shaders", "Objects")) return;
|
||||
|
||||
bool mrt = RenderingManager::useMRT();
|
||||
bool shadows = Settings::Manager::getBool("enabled", "Shadows");
|
||||
bool split = Settings::Manager::getBool("split", "Shadows");
|
||||
|
||||
// shader for normal rendering
|
||||
createShader(mrt, shadows, split, "main");
|
||||
|
||||
// fallback shader without mrt and without shadows
|
||||
// (useful for reflection and for minimap)
|
||||
createShader(false, false, false, "main_fallback");
|
||||
}
|
||||
|
||||
void ShaderHelper::createShader(const bool mrt, const bool shadows, const bool split, const std::string& name)
|
||||
{
|
||||
HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
|
||||
|
||||
const int numsplits = 3;
|
||||
|
||||
// the number of lights to support.
|
||||
// when rendering an object, OGRE automatically picks the lights that are
|
||||
// closest to the object being rendered. unfortunately this mechanism does
|
||||
// not work perfectly for objects batched together (they will all use the same
|
||||
// lights). to work around this, we are simply pushing the maximum number
|
||||
// of lights here in order to minimize disappearing lights.
|
||||
int num_lights = Settings::Manager::getInt("num lights", "Objects");
|
||||
|
||||
{
|
||||
// vertex
|
||||
HighLevelGpuProgramPtr vertex;
|
||||
if (!mgr.getByName(name+"_vp").isNull())
|
||||
mgr.remove(name+"_vp");
|
||||
|
||||
vertex = mgr.createProgram(name+"_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"cg", GPT_VERTEX_PROGRAM);
|
||||
vertex->setParameter("profiles", "vs_4_0 vs_2_x vp40 arbvp1");
|
||||
vertex->setParameter("entry_point", "main_vp");
|
||||
StringUtil::StrStreamType outStream;
|
||||
outStream <<
|
||||
"void main_vp( \n"
|
||||
" float4 position : POSITION, \n"
|
||||
" float4 normal : NORMAL, \n"
|
||||
" float4 colour : COLOR, \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float2 oUV : TEXCOORD0, \n"
|
||||
" out float4 oPosition : POSITION, \n"
|
||||
" out float4 oPositionObjSpace : TEXCOORD1, \n"
|
||||
" out float4 oNormal : TEXCOORD2, \n"
|
||||
" out float oDepth : TEXCOORD3, \n"
|
||||
" out float4 oVertexColour : TEXCOORD4, \n";
|
||||
if (shadows && !split) outStream <<
|
||||
" out float4 oLightSpacePos0 : TEXCOORD5, \n"
|
||||
" uniform float4x4 worldMatrix, \n"
|
||||
" uniform float4x4 texViewProjMatrix0, \n";
|
||||
else
|
||||
{
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" out float4 oLightSpacePos"<<i<<" : TEXCOORD"<<i+5<<", \n"
|
||||
" uniform float4x4 texViewProjMatrix"<<i<<", \n";
|
||||
}
|
||||
outStream <<
|
||||
" uniform float4x4 worldMatrix, \n";
|
||||
}
|
||||
outStream <<
|
||||
" uniform float4x4 worldViewProj \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" oVertexColour = colour; \n"
|
||||
" oUV = uv; \n"
|
||||
" oNormal = normal; \n"
|
||||
" oPosition = mul( worldViewProj, position ); \n"
|
||||
" oDepth = oPosition.z; \n"
|
||||
" oPositionObjSpace = position; \n";
|
||||
if (shadows && !split) outStream <<
|
||||
" oLightSpacePos0 = mul(texViewProjMatrix0, mul(worldMatrix, position)); \n";
|
||||
else
|
||||
{
|
||||
outStream <<
|
||||
" float4 wPos = mul(worldMatrix, position); \n";
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" oLightSpacePos"<<i<<" = mul(texViewProjMatrix"<<i<<", wPos); \n";
|
||||
}
|
||||
}
|
||||
outStream <<
|
||||
"}";
|
||||
vertex->setSource(outStream.str());
|
||||
vertex->load();
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
|
||||
if (shadows)
|
||||
{
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX);
|
||||
if (!split)
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix0", GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, 0);
|
||||
else
|
||||
{
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix"+StringConverter::toString(i), GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// fragment
|
||||
HighLevelGpuProgramPtr fragment;
|
||||
if (!mgr.getByName(name+"_fp").isNull())
|
||||
mgr.remove(name+"_fp");
|
||||
|
||||
fragment = mgr.createProgram(name+"_fp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"cg", GPT_FRAGMENT_PROGRAM);
|
||||
fragment->setParameter("profiles", "ps_4_0 ps_2_x fp40 arbfp1");
|
||||
fragment->setParameter("entry_point", "main_fp");
|
||||
StringUtil::StrStreamType outStream;
|
||||
|
||||
if (shadows) outStream <<
|
||||
"float depthShadow(sampler2D shadowMap, float4 shadowMapPos, float2 offset) \n"
|
||||
"{ \n"
|
||||
" shadowMapPos /= shadowMapPos.w; \n"
|
||||
" float3 o = float3(offset.xy, -offset.x) * 0.3f; \n"
|
||||
" float c = (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left \n"
|
||||
" c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right \n"
|
||||
" c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left \n"
|
||||
" c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right \n"
|
||||
" return c / 4; \n"
|
||||
"} \n";
|
||||
|
||||
outStream <<
|
||||
"void main_fp( \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n"
|
||||
" uniform sampler2D texture : register(s0), \n"
|
||||
" float4 positionObjSpace : TEXCOORD1, \n"
|
||||
" float4 normal : TEXCOORD2, \n"
|
||||
" float iDepth : TEXCOORD3, \n"
|
||||
" float4 vertexColour : TEXCOORD4, \n"
|
||||
" uniform float4 fogColour, \n"
|
||||
" uniform float4 fogParams, \n";
|
||||
|
||||
if (shadows) outStream <<
|
||||
" uniform float4 shadowFar_fadeStart, \n";
|
||||
|
||||
if (shadows && !split) outStream <<
|
||||
" uniform sampler2D shadowMap : register(s1), \n"
|
||||
" float4 lightSpacePos0 : TEXCOORD5, \n"
|
||||
" uniform float4 invShadowmapSize0, \n";
|
||||
else
|
||||
{
|
||||
outStream <<
|
||||
" uniform float4 pssmSplitPoints, \n";
|
||||
for (int i=0; i<numsplits; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" uniform sampler2D shadowMap"<<i<<" : register(s"<<i+1<<"), \n"
|
||||
" float4 lightSpacePos"<<i<<" : TEXCOORD"<<i+5<<", \n"
|
||||
" uniform float4 invShadowmapSize"<<i<<", \n";
|
||||
}
|
||||
}
|
||||
|
||||
if (mrt) outStream <<
|
||||
" out float4 oColor1 : COLOR1, \n"
|
||||
" uniform float far, \n";
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" uniform float4 lightDiffuse"<<i<<", \n"
|
||||
" uniform float4 lightPositionObjSpace"<<i<<", \n"
|
||||
" uniform float4 lightAttenuation"<<i<<", \n";
|
||||
}
|
||||
outStream <<
|
||||
" uniform float4 lightAmbient, \n"
|
||||
" uniform float4 ambient, \n"
|
||||
" uniform float4 diffuse, \n"
|
||||
" uniform float4 emissive \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" float4 tex = tex2D(texture, uv); \n"
|
||||
" float d; \n"
|
||||
" float attn; \n"
|
||||
" float3 lightDir; \n"
|
||||
" float3 lightColour = float3(0, 0, 0); \n";
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
outStream <<
|
||||
" lightDir = lightPositionObjSpace"<<i<<".xyz - (positionObjSpace.xyz * lightPositionObjSpace"<<i<<".w); \n"
|
||||
|
||||
// pre-multiply light color with attenuation factor
|
||||
" d = length( lightDir ); \n"
|
||||
" attn = ( 1.0 / (( lightAttenuation"<<i<<".y ) + ( lightAttenuation"<<i<<".z * d ) + ( lightAttenuation"<<i<<".w * d * d ))); \n"
|
||||
" lightDiffuse"<<i<<" *= attn; \n";
|
||||
|
||||
if (i == 0 && shadows)
|
||||
{
|
||||
outStream <<
|
||||
" float shadow; \n";
|
||||
if (!split) outStream <<
|
||||
" shadow = depthShadow(shadowMap, lightSpacePos0, invShadowmapSize0.xy); \n";
|
||||
else
|
||||
{
|
||||
for (int j=0; j<numsplits; ++j)
|
||||
{
|
||||
std::string channel;
|
||||
if (j==0) channel = "x";
|
||||
else if (j==1) channel = "y";
|
||||
else if (j==2) channel = "z";
|
||||
|
||||
if (j==0)
|
||||
outStream << " if (iDepth <= pssmSplitPoints." << channel << ") \n";
|
||||
else if (j < numsplits - 1)
|
||||
outStream << " else if (iDepth <= pssmSplitPoints." << channel << ") \n";
|
||||
else
|
||||
outStream << " else \n";
|
||||
|
||||
outStream <<
|
||||
" { \n"
|
||||
" shadow = depthShadow(shadowMap" << j << ", lightSpacePos" << j << ", invShadowmapSize" << j << ".xy); \n"
|
||||
" } \n";
|
||||
}
|
||||
}
|
||||
outStream <<
|
||||
" float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; \n"
|
||||
" float fade = 1-((iDepth - shadowFar_fadeStart.y) / fadeRange); \n"
|
||||
" shadow = (iDepth > shadowFar_fadeStart.x) ? 1 : ((iDepth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); \n"
|
||||
" lightColour.xyz += shadow * lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<<i<<".xyz;\n";
|
||||
}
|
||||
else outStream <<
|
||||
" lightColour.xyz += lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<<i<<".xyz;\n";
|
||||
}
|
||||
|
||||
outStream <<
|
||||
" float3 lightingFinal = lightColour.xyz * diffuse.xyz * vertexColour.xyz + ambient.xyz * lightAmbient.xyz + emissive.xyz; \n"
|
||||
" float fogValue = saturate((iDepth - fogParams.y) * fogParams.w); \n"
|
||||
" oColor.xyz = lerp(lightingFinal * tex.xyz, fogColour.xyz, fogValue); \n"
|
||||
" oColor.a = tex.a * diffuse.a * vertexColour.a; \n";
|
||||
|
||||
if (mrt) outStream <<
|
||||
" oColor1 = float4(iDepth / far, 0, 0, (oColor.a == 1)); \n"; // only write to MRT if alpha is 1
|
||||
|
||||
outStream <<
|
||||
"}";
|
||||
fragment->setSource(outStream.str());
|
||||
fragment->load();
|
||||
|
||||
for (int i=0; i<num_lights; ++i)
|
||||
{
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightPositionObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightDiffuse"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i);
|
||||
}
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_SURFACE_AMBIENT_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("lightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR);
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS);
|
||||
|
||||
if (shadows)
|
||||
{
|
||||
fragment->getDefaultParameters()->setNamedConstant("shadowFar_fadeStart", Vector4(mRendering->getShadows()->getShadowFar(), mRendering->getShadows()->getFadeStart()*mRendering->getShadows()->getShadowFar(), 0, 0));
|
||||
for (int i=0; i < (split ? numsplits : 1); ++i)
|
||||
{
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("invShadowmapSize" + StringConverter::toString(i), GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i+1);
|
||||
}
|
||||
if (split)
|
||||
{
|
||||
Vector4 splitPoints;
|
||||
const PSSMShadowCameraSetup::SplitPointList& splitPointList = mRendering->getShadows()->getPSSMSetup()->getSplitPoints();
|
||||
// Populate from split point 1, not 0, since split 0 isn't useful (usually 0)
|
||||
for (int i = 1; i < numsplits; ++i)
|
||||
{
|
||||
splitPoints[i-1] = splitPointList[i];
|
||||
}
|
||||
fragment->getDefaultParameters()->setNamedConstant("pssmSplitPoints", splitPoints);
|
||||
}
|
||||
}
|
||||
|
||||
if (mrt)
|
||||
fragment->getDefaultParameters()->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE);
|
||||
}
|
||||
}
|
29
apps/openmw/mwrender/shaderhelper.hpp
Normal file
29
apps/openmw/mwrender/shaderhelper.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef GAME_SHADERHELPER_H
|
||||
#define GAME_SHADERHELPER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class RenderingManager;
|
||||
|
||||
///
|
||||
/// \brief manages the main shader
|
||||
///
|
||||
class ShaderHelper
|
||||
{
|
||||
public:
|
||||
ShaderHelper(RenderingManager* rend);
|
||||
|
||||
void applyShaders();
|
||||
///< apply new settings
|
||||
|
||||
private:
|
||||
RenderingManager* mRendering;
|
||||
|
||||
void createShader(const bool mrt, const bool shadows, const bool split, const std::string& name);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
173
apps/openmw/mwrender/shadows.cpp
Normal file
173
apps/openmw/mwrender/shadows.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
#include "shadows.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <openengine/ogre/renderer.hpp>
|
||||
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreColourValue.h>
|
||||
#include <OgreShadowCameraSetupLiSPSM.h>
|
||||
#include <OgreShadowCameraSetupPSSM.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
|
||||
#include <OgreOverlayContainer.h>
|
||||
#include <OgreOverlayManager.h>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
using namespace MWRender;
|
||||
|
||||
Shadows::Shadows(OEngine::Render::OgreRenderer* rend) :
|
||||
mShadowFar(1000), mFadeStart(0.9)
|
||||
{
|
||||
mRendering = rend;
|
||||
mSceneMgr = mRendering->getScene();
|
||||
recreate();
|
||||
}
|
||||
|
||||
void Shadows::recreate()
|
||||
{
|
||||
bool enabled = Settings::Manager::getBool("enabled", "Shadows");
|
||||
|
||||
// Split shadow maps are currently disabled because the terrain cannot cope with them
|
||||
// (Too many texture units) Solution would be a multi-pass terrain material
|
||||
bool split = Settings::Manager::getBool("split", "Shadows");
|
||||
//const bool split = false;
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
int texsize = Settings::Manager::getInt("texture size", "Shadows");
|
||||
mSceneMgr->setShadowTextureSize(texsize);
|
||||
|
||||
mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE_INTEGRATED);
|
||||
|
||||
// no point light shadows, i'm afraid. might revisit this with Deferred Shading
|
||||
mSceneMgr->setShadowTextureCountPerLightType(Light::LT_POINT, 0);
|
||||
|
||||
mSceneMgr->setShadowTextureCountPerLightType(Light::LT_DIRECTIONAL, split ? 3 : 1);
|
||||
mSceneMgr->setShadowTextureCount(split ? 3 : 1);
|
||||
|
||||
mSceneMgr->setShadowTextureSelfShadow(true);
|
||||
mSceneMgr->setShadowCasterRenderBackFaces(true);
|
||||
mSceneMgr->setShadowTextureCasterMaterial("depth_shadow_caster");
|
||||
mSceneMgr->setShadowTexturePixelFormat(PF_FLOAT32_R);
|
||||
mSceneMgr->setShadowDirectionalLightExtrusionDistance(1000000);
|
||||
|
||||
mShadowFar = split ? Settings::Manager::getInt("split shadow distance", "Shadows") : Settings::Manager::getInt("shadow distance", "Shadows");
|
||||
mSceneMgr->setShadowFarDistance(mShadowFar);
|
||||
|
||||
mFadeStart = Settings::Manager::getFloat("fade start", "Shadows");
|
||||
|
||||
ShadowCameraSetupPtr shadowCameraSetup;
|
||||
if (split)
|
||||
{
|
||||
mPSSMSetup = new PSSMShadowCameraSetup();
|
||||
mPSSMSetup->setSplitPadding(5);
|
||||
mPSSMSetup->calculateSplitPoints(3, mRendering->getCamera()->getNearClipDistance(), mShadowFar);
|
||||
|
||||
const Real adjustFactors[3] = {64, 64, 64};
|
||||
for (int i=0; i < 3; ++i)
|
||||
{
|
||||
mPSSMSetup->setOptimalAdjustFactor(i, adjustFactors[i]);
|
||||
/*if (i==0)
|
||||
mSceneMgr->setShadowTextureConfig(i, texsize, texsize, Ogre::PF_FLOAT32_R);
|
||||
else if (i ==1)
|
||||
mSceneMgr->setShadowTextureConfig(i, texsize/2, texsize/2, Ogre::PF_FLOAT32_R);
|
||||
else if (i ==2)
|
||||
mSceneMgr->setShadowTextureConfig(i, texsize/4, texsize/4, Ogre::PF_FLOAT32_R);*/
|
||||
}
|
||||
|
||||
shadowCameraSetup = ShadowCameraSetupPtr(mPSSMSetup);
|
||||
}
|
||||
else
|
||||
{
|
||||
LiSPSMShadowCameraSetup* lispsmSetup = new LiSPSMShadowCameraSetup();
|
||||
lispsmSetup->setOptimalAdjustFactor(2);
|
||||
//lispsmSetup->setCameraLightDirectionThreshold(Degree(0));
|
||||
//lispsmSetup->setUseAggressiveFocusRegion(false);
|
||||
shadowCameraSetup = ShadowCameraSetupPtr(lispsmSetup);
|
||||
}
|
||||
mSceneMgr->setShadowCameraSetup(shadowCameraSetup);
|
||||
|
||||
// Set visibility mask for the shadow render textures
|
||||
int visibilityMask = RV_Actors * Settings::Manager::getBool("actor shadows", "Shadows")
|
||||
+ (RV_Statics + RV_StaticsSmall) * Settings::Manager::getBool("statics shadows", "Shadows")
|
||||
+ RV_Misc * Settings::Manager::getBool("misc shadows", "Shadows");
|
||||
|
||||
for (int i = 0; i < (split ? 3 : 1); ++i)
|
||||
{
|
||||
TexturePtr shadowTexture = mSceneMgr->getShadowTexture(i);
|
||||
Viewport* vp = shadowTexture->getBuffer()->getRenderTarget()->getViewport(0);
|
||||
vp->setVisibilityMask(visibilityMask);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// --------------------------- Debug overlays to display the content of shadow maps -----------------------------------
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
/*
|
||||
OverlayManager& mgr = OverlayManager::getSingleton();
|
||||
Overlay* overlay;
|
||||
|
||||
// destroy if already exists
|
||||
if (overlay = mgr.getByName("DebugOverlay"))
|
||||
mgr.destroy(overlay);
|
||||
|
||||
overlay = mgr.create("DebugOverlay");
|
||||
for (size_t i = 0; i < (split ? 3 : 1); ++i) {
|
||||
TexturePtr tex = mRendering->getScene()->getShadowTexture(i);
|
||||
|
||||
// Set up a debug panel to display the shadow
|
||||
|
||||
if (MaterialManager::getSingleton().resourceExists("Ogre/DebugTexture" + StringConverter::toString(i)))
|
||||
MaterialManager::getSingleton().remove("Ogre/DebugTexture" + StringConverter::toString(i));
|
||||
MaterialPtr debugMat = MaterialManager::getSingleton().create(
|
||||
"Ogre/DebugTexture" + StringConverter::toString(i),
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName());
|
||||
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
|
||||
|
||||
OverlayContainer* debugPanel;
|
||||
|
||||
// destroy container if exists
|
||||
try
|
||||
{
|
||||
if (debugPanel =
|
||||
static_cast<OverlayContainer*>(
|
||||
mgr.getOverlayElement("Ogre/DebugTexPanel" + StringConverter::toString(i)
|
||||
)))
|
||||
mgr.destroyOverlayElement(debugPanel);
|
||||
}
|
||||
catch (Ogre::Exception&) {}
|
||||
|
||||
debugPanel = (OverlayContainer*)
|
||||
(OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/DebugTexPanel" + StringConverter::toString(i)));
|
||||
debugPanel->_setPosition(0.8, i*0.25);
|
||||
debugPanel->_setDimensions(0.2, 0.24);
|
||||
debugPanel->setMaterialName(debugMat->getName());
|
||||
debugPanel->show();
|
||||
overlay->add2D(debugPanel);
|
||||
overlay->show();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
PSSMShadowCameraSetup* Shadows::getPSSMSetup()
|
||||
{
|
||||
return mPSSMSetup;
|
||||
}
|
||||
|
||||
float Shadows::getShadowFar() const
|
||||
{
|
||||
return mShadowFar;
|
||||
}
|
||||
|
||||
float Shadows::getFadeStart() const
|
||||
{
|
||||
return mFadeStart;
|
||||
}
|
39
apps/openmw/mwrender/shadows.hpp
Normal file
39
apps/openmw/mwrender/shadows.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef GAME_SHADOWS_H
|
||||
#define GAME_SHADOWS_H
|
||||
|
||||
// forward declares
|
||||
namespace Ogre
|
||||
{
|
||||
class SceneManager;
|
||||
class PSSMShadowCameraSetup;
|
||||
}
|
||||
namespace OEngine{
|
||||
namespace Render{
|
||||
class OgreRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Shadows
|
||||
{
|
||||
public:
|
||||
Shadows(OEngine::Render::OgreRenderer* rend);
|
||||
|
||||
void recreate();
|
||||
|
||||
Ogre::PSSMShadowCameraSetup* getPSSMSetup();
|
||||
float getShadowFar() const;
|
||||
float getFadeStart() const;
|
||||
|
||||
protected:
|
||||
OEngine::Render::OgreRenderer* mRendering;
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
|
||||
Ogre::PSSMShadowCameraSetup* mPSSMSetup;
|
||||
float mShadowFar;
|
||||
float mFadeStart;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "../mwworld/environment.hpp"
|
||||
#include "../mwworld/world.hpp"
|
||||
#include "renderconst.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
@ -30,7 +32,7 @@ BillboardObject::BillboardObject()
|
|||
|
||||
void BillboardObject::setVisible(const bool visible)
|
||||
{
|
||||
mNode->setVisible(visible);
|
||||
mBBSet->setVisible(visible);
|
||||
}
|
||||
|
||||
void BillboardObject::setSize(const float size)
|
||||
|
@ -59,6 +61,11 @@ Vector3 BillboardObject::getPosition() const
|
|||
return Vector3(p.x, -p.z, p.y);
|
||||
}
|
||||
|
||||
void BillboardObject::setVisibilityFlags(int flags)
|
||||
{
|
||||
mBBSet->setVisibilityFlags(flags);
|
||||
}
|
||||
|
||||
void BillboardObject::setColour(const ColourValue& pColour)
|
||||
{
|
||||
mMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(pColour);
|
||||
|
@ -88,13 +95,14 @@ void BillboardObject::init(const String& textureName,
|
|||
/// \todo These billboards are not 100% correct, might want to revisit them later
|
||||
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
|
||||
mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize);
|
||||
mBBSet->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+2);
|
||||
mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON);
|
||||
mBBSet->setCommonDirection( -position.normalisedCopy() );
|
||||
mBBSet->setVisibilityFlags(RV_Sky);
|
||||
mNode = rootNode->createChildSceneNode();
|
||||
mNode->setPosition(finalPosition);
|
||||
mNode->attachObject(mBBSet);
|
||||
mBBSet->createBillboard(0,0,0);
|
||||
mBBSet->setCastShadows(false);
|
||||
|
||||
mMaterial = MaterialManager::getSingleton().create("BillboardMaterial"+StringConverter::toString(bodyCount), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
mMaterial->removeAllTechniques();
|
||||
|
@ -108,6 +116,65 @@ void BillboardObject::init(const String& textureName,
|
|||
p->createTextureUnitState(textureName);
|
||||
mBBSet->setMaterialName("BillboardMaterial"+StringConverter::toString(bodyCount));
|
||||
|
||||
HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
|
||||
HighLevelGpuProgramPtr vshader;
|
||||
if (mgr.resourceExists("BBO_VP"))
|
||||
vshader = mgr.getByName("BBO_VP");
|
||||
else
|
||||
vshader = mgr.createProgram("BBO_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM);
|
||||
vshader->setParameter("profiles", "vs_2_x arbvp1");
|
||||
vshader->setParameter("entry_point", "main_vp");
|
||||
StringUtil::StrStreamType outStream;
|
||||
outStream <<
|
||||
"void main_vp( \n"
|
||||
" float4 position : POSITION, \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float2 oUV : TEXCOORD0, \n"
|
||||
" out float4 oPosition : POSITION, \n"
|
||||
" uniform float4x4 worldViewProj \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" oUV = uv; \n"
|
||||
" oPosition = mul( worldViewProj, position ); \n"
|
||||
"}";
|
||||
vshader->setSource(outStream.str());
|
||||
vshader->load();
|
||||
vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
|
||||
mMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName());
|
||||
|
||||
HighLevelGpuProgramPtr fshader;
|
||||
if (mgr.resourceExists("BBO_FP"))
|
||||
fshader = mgr.getByName("BBO_FP");
|
||||
else
|
||||
fshader = mgr.createProgram("BBO_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM);
|
||||
|
||||
fshader->setParameter("profiles", "ps_2_x arbfp1");
|
||||
fshader->setParameter("entry_point", "main_fp");
|
||||
StringUtil::StrStreamType outStream2;
|
||||
outStream2 <<
|
||||
"void main_fp( \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n";
|
||||
if (RenderingManager::useMRT()) outStream2 <<
|
||||
" out float4 oColor1 : COLOR1, \n";
|
||||
outStream2 <<
|
||||
" uniform sampler2D texture : TEXUNIT0, \n"
|
||||
" uniform float4 diffuse, \n"
|
||||
" uniform float4 emissive \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" float4 tex = tex2D(texture, uv); \n"
|
||||
" oColor = float4(emissive.xyz,1) * tex * float4(1,1,1,diffuse.a); \n";
|
||||
if (RenderingManager::useMRT()) outStream2 <<
|
||||
" oColor1 = float4(1, 0, 0, 1); \n";
|
||||
outStream2 <<
|
||||
"}";
|
||||
fshader->setSource(outStream2.str());
|
||||
fshader->load();
|
||||
fshader->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR);
|
||||
fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
|
||||
mMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName());
|
||||
|
||||
bodyCount++;
|
||||
}
|
||||
|
||||
|
@ -156,7 +223,10 @@ Moon::Moon( const String& textureName,
|
|||
outStream2 <<
|
||||
"void main_fp( \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n"
|
||||
" out float4 oColor : COLOR, \n";
|
||||
if (RenderingManager::useMRT()) outStream2 <<
|
||||
" out float4 oColor1 : COLOR1, \n";
|
||||
outStream2 <<
|
||||
" uniform sampler2D texture : TEXUNIT0, \n"
|
||||
" uniform float4 skyColour, \n"
|
||||
" uniform float4 diffuse, \n"
|
||||
|
@ -164,7 +234,10 @@ Moon::Moon( const String& textureName,
|
|||
") \n"
|
||||
"{ \n"
|
||||
" float4 tex = tex2D(texture, uv); \n"
|
||||
" oColor = float4(emissive.xyz,1) * tex; \n"
|
||||
" oColor = float4(emissive.xyz,1) * tex; \n";
|
||||
if (RenderingManager::useMRT()) outStream2 <<
|
||||
" oColor1 = float4(1, 0, 0, 1); \n";
|
||||
outStream2 <<
|
||||
// use a circle for the alpha (compute UV distance to center)
|
||||
// looks a bit bad because its not filtered on the edges,
|
||||
// but it's cheaper than a seperate alpha texture.
|
||||
|
@ -254,7 +327,7 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType)
|
|||
// Get a pointer to the vertex colour
|
||||
ves_diffuse->baseVertexPointerToElement( pData, ¤tVertex );
|
||||
|
||||
unsigned char alpha;
|
||||
unsigned char alpha=0;
|
||||
if (meshType == 0) alpha = i%2 ? 0 : 255; // this is a cylinder, so every second vertex belongs to the bottom-most row
|
||||
else if (meshType == 1)
|
||||
{
|
||||
|
@ -292,16 +365,49 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType)
|
|||
ent->getMesh()->getSubMesh(0)->vertexData->vertexBufferBinding->getBuffer(ves_diffuse->getSource())->unlock();
|
||||
}
|
||||
|
||||
SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env) :
|
||||
mGlareFade(0), mGlareEnabled(false)
|
||||
SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env)
|
||||
: mEnvironment(env)
|
||||
, mHour(0.0f)
|
||||
, mDay(0)
|
||||
, mMonth(0)
|
||||
, mSun(NULL)
|
||||
, mSunGlare(NULL)
|
||||
, mMasser(NULL)
|
||||
, mSecunda(NULL)
|
||||
, mViewport(NULL)
|
||||
, mRootNode(NULL)
|
||||
, mSceneMgr(NULL)
|
||||
, mAtmosphereDay(NULL)
|
||||
, mAtmosphereNight(NULL)
|
||||
, mCloudMaterial()
|
||||
, mAtmosphereMaterial()
|
||||
, mCloudFragmentShader()
|
||||
, mClouds()
|
||||
, mNextClouds()
|
||||
, mCloudBlendFactor(0.0f)
|
||||
, mCloudOpacity(0.0f)
|
||||
, mCloudSpeed(0.0f)
|
||||
, mStarsOpacity(0.0f)
|
||||
, mThunderOverlay(NULL)
|
||||
, mThunderTextureUnit(NULL)
|
||||
, mRemainingTransitionTime(0.0f)
|
||||
, mGlareFade(0.0f)
|
||||
, mGlare(0.0f)
|
||||
, mEnabled(true)
|
||||
, mSunEnabled(true)
|
||||
, mMasserEnabled(true)
|
||||
, mSecundaEnabled(true)
|
||||
, mCreated(false)
|
||||
{
|
||||
mEnvironment = env;
|
||||
mViewport = pCamera->getViewport();
|
||||
mSceneMgr = pMwRoot->getCreator();
|
||||
mRootNode = pCamera->getParentSceneNode()->createChildSceneNode();
|
||||
mRootNode->pitch(Degree(-90)); // convert MW to ogre coordinates
|
||||
mRootNode->setInheritOrientation(false);
|
||||
}
|
||||
|
||||
void SkyManager::create()
|
||||
{
|
||||
/// \todo preload all the textures and meshes that are used for sky rendering
|
||||
|
||||
// Create overlay used for thunderstorm
|
||||
|
@ -324,15 +430,17 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
|
||||
mSecunda = new Moon("textures\\tx_secunda_full.dds", 0.5, Vector3(-0.4, 0.4, 0.5), mRootNode);
|
||||
mSecunda->setType(Moon::Type_Secunda);
|
||||
mSecunda->setRenderQueue(RENDER_QUEUE_SKIES_EARLY+4);
|
||||
mSecunda->setRenderQueue(RQG_SkiesEarly+4);
|
||||
|
||||
mMasser = new Moon("textures\\tx_masser_full.dds", 0.75, Vector3(-0.4, 0.4, 0.5), mRootNode);
|
||||
mMasser->setRenderQueue(RENDER_QUEUE_SKIES_EARLY+3);
|
||||
mMasser->setRenderQueue(RQG_SkiesEarly+3);
|
||||
mMasser->setType(Moon::Type_Masser);
|
||||
|
||||
mSun = new BillboardObject("textures\\tx_sun_05.dds", 1, Vector3(0.4, 0.4, 0.4), mRootNode);
|
||||
mSun->setRenderQueue(RQG_SkiesEarly+4);
|
||||
mSunGlare = new BillboardObject("textures\\tx_sun_flash_grey_05.dds", 3, Vector3(0.4, 0.4, 0.4), mRootNode);
|
||||
mSunGlare->setRenderQueue(RENDER_QUEUE_SKIES_LATE);
|
||||
mSunGlare->setRenderQueue(RQG_SkiesLate);
|
||||
mSunGlare->setVisibilityFlags(RV_Glare);
|
||||
|
||||
|
||||
HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
|
||||
|
@ -341,7 +449,9 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
/// \todo sky_night_02.nif (available in Bloodmoon)
|
||||
MeshPtr mesh = NifOgre::NIFLoader::load("meshes\\sky_night_01.nif");
|
||||
Entity* night1_ent = mSceneMgr->createEntity("meshes\\sky_night_01.nif");
|
||||
night1_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+1);
|
||||
night1_ent->setRenderQueueGroup(RQG_SkiesEarly+1);
|
||||
night1_ent->setVisibilityFlags(RV_Sky);
|
||||
night1_ent->setCastShadows(false);
|
||||
|
||||
mAtmosphereNight = mRootNode->createChildSceneNode();
|
||||
mAtmosphereNight->attachObject(night1_ent);
|
||||
|
@ -379,7 +489,10 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
outStream5 <<
|
||||
"void main_fp( \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n"
|
||||
" out float4 oColor : COLOR, \n";
|
||||
if (RenderingManager::useMRT()) outStream5 <<
|
||||
" out float4 oColor1 : COLOR1, \n";
|
||||
outStream5 <<
|
||||
" in float fade : TEXCOORD1, \n"
|
||||
" uniform sampler2D texture : TEXUNIT0, \n"
|
||||
" uniform float opacity, \n"
|
||||
|
@ -387,7 +500,10 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
" uniform float4 emissive \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" oColor = tex2D(texture, uv) * float4(emissive.xyz, 1) * float4(1,1,1,fade*diffuse.a); \n"
|
||||
" oColor = tex2D(texture, uv) * float4(emissive.xyz, 1) * float4(1,1,1,fade*diffuse.a); \n";
|
||||
if (RenderingManager::useMRT()) outStream5 <<
|
||||
" oColor1 = float4(1, 0, 0, 1); \n";
|
||||
outStream5 <<
|
||||
"}";
|
||||
stars_fp->setSource(outStream5.str());
|
||||
stars_fp->load();
|
||||
|
@ -411,10 +527,12 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
// Atmosphere (day)
|
||||
mesh = NifOgre::NIFLoader::load("meshes\\sky_atmosphere.nif");
|
||||
Entity* atmosphere_ent = mSceneMgr->createEntity("meshes\\sky_atmosphere.nif");
|
||||
atmosphere_ent->setCastShadows(false);
|
||||
|
||||
ModVertexAlpha(atmosphere_ent, 0);
|
||||
|
||||
atmosphere_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY);
|
||||
atmosphere_ent->setRenderQueueGroup(RQG_SkiesEarly);
|
||||
atmosphere_ent->setVisibilityFlags(RV_Sky);
|
||||
mAtmosphereDay = mRootNode->createChildSceneNode();
|
||||
mAtmosphereDay->attachObject(atmosphere_ent);
|
||||
mAtmosphereMaterial = atmosphere_ent->getSubEntity(0)->getMaterial();
|
||||
|
@ -432,29 +550,56 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
" float4 position : POSITION, \n"
|
||||
" in float4 color : COLOR, \n"
|
||||
" out float4 oPosition : POSITION, \n"
|
||||
" out float4 oColor : COLOR, \n"
|
||||
" uniform float4 emissive, \n"
|
||||
" out float4 oVertexColor : TEXCOORD0, \n"
|
||||
" uniform float4x4 worldViewProj \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" oPosition = mul( worldViewProj, position ); \n"
|
||||
" oColor = color * emissive; \n"
|
||||
" oVertexColor = color; \n"
|
||||
"}";
|
||||
vshader->setSource(outStream.str());
|
||||
vshader->load();
|
||||
|
||||
vshader->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
|
||||
vshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
|
||||
mAtmosphereMaterial->getTechnique(0)->getPass(0)->setVertexProgram(vshader->getName());
|
||||
mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram("");
|
||||
|
||||
HighLevelGpuProgramPtr fshader = mgr.createProgram("Atmosphere_FP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
"cg", GPT_FRAGMENT_PROGRAM);
|
||||
|
||||
fshader->setParameter("profiles", "ps_2_x arbfp1");
|
||||
fshader->setParameter("entry_point", "main_fp");
|
||||
|
||||
StringUtil::StrStreamType _outStream;
|
||||
_outStream <<
|
||||
"void main_fp( \n"
|
||||
" in float4 iVertexColor : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n";
|
||||
if (RenderingManager::useMRT()) _outStream <<
|
||||
" out float4 oColor1 : COLOR1, \n";
|
||||
_outStream <<
|
||||
" uniform float4 emissive \n"
|
||||
") \n"
|
||||
"{ \n"
|
||||
" oColor = iVertexColor * emissive; \n";
|
||||
if (RenderingManager::useMRT()) _outStream <<
|
||||
" oColor1 = float4(1, 0, 0, 1); \n";
|
||||
_outStream <<
|
||||
"}";
|
||||
fshader->setSource(_outStream.str());
|
||||
fshader->load();
|
||||
|
||||
fshader->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR);
|
||||
mAtmosphereMaterial->getTechnique(0)->getPass(0)->setFragmentProgram(fshader->getName());
|
||||
|
||||
// Clouds
|
||||
NifOgre::NIFLoader::load("meshes\\sky_clouds_01.nif");
|
||||
Entity* clouds_ent = mSceneMgr->createEntity("meshes\\sky_clouds_01.nif");
|
||||
clouds_ent->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+5);
|
||||
clouds_ent->setVisibilityFlags(RV_Sky);
|
||||
clouds_ent->setRenderQueueGroup(RQG_SkiesEarly+5);
|
||||
SceneNode* clouds_node = mRootNode->createChildSceneNode();
|
||||
clouds_node->attachObject(clouds_ent);
|
||||
mCloudMaterial = clouds_ent->getSubEntity(0)->getMaterial();
|
||||
clouds_ent->setCastShadows(false);
|
||||
|
||||
// Clouds vertex shader
|
||||
HighLevelGpuProgramPtr vshader2 = mgr.createProgram("Clouds_VP", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
|
@ -491,8 +636,11 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
outStream2 <<
|
||||
"void main_fp( \n"
|
||||
" in float2 uv : TEXCOORD0, \n"
|
||||
" out float4 oColor : COLOR, \n"
|
||||
" in float4 color : TEXCOORD1, \n"
|
||||
" out float4 oColor : COLOR, \n";
|
||||
if (RenderingManager::useMRT()) outStream2 <<
|
||||
" out float4 oColor1 : COLOR1, \n";
|
||||
outStream2 <<
|
||||
" uniform sampler2D texture : TEXUNIT0, \n"
|
||||
" uniform sampler2D secondTexture : TEXUNIT1, \n"
|
||||
" uniform float transitionFactor, \n"
|
||||
|
@ -502,9 +650,12 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
" uniform float4 emissive \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"
|
||||
" 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";
|
||||
if (RenderingManager::useMRT()) outStream2 <<
|
||||
" oColor1 = float4(1, 0, 0, 1); \n";
|
||||
outStream2 <<
|
||||
"}";
|
||||
mCloudFragmentShader->setSource(outStream2.str());
|
||||
mCloudFragmentShader->load();
|
||||
|
@ -530,7 +681,11 @@ SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environmen
|
|||
mAtmosphereMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
|
||||
mCloudMaterial->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA);
|
||||
|
||||
mCloudMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates();
|
||||
mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("textures\\tx_sky_cloudy.dds");
|
||||
mCloudMaterial->getTechnique(0)->getPass(0)->createTextureUnitState("");
|
||||
|
||||
mCreated = true;
|
||||
}
|
||||
|
||||
SkyManager::~SkyManager()
|
||||
|
@ -543,11 +698,13 @@ SkyManager::~SkyManager()
|
|||
|
||||
int SkyManager::getMasserPhase() const
|
||||
{
|
||||
if (!mCreated) return 0;
|
||||
return mMasser->getPhaseInt();
|
||||
}
|
||||
|
||||
int SkyManager::getSecundaPhase() const
|
||||
{
|
||||
if (!mCreated) return 0;
|
||||
return mSecunda->getPhaseInt();
|
||||
}
|
||||
|
||||
|
@ -562,10 +719,23 @@ void SkyManager::update(float duration)
|
|||
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
|
||||
mSecunda->setPhase ( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
|
||||
|
||||
// increase the strength of the sun glare effect depending
|
||||
// on how directly the player is looking at the sun
|
||||
|
||||
if (mSunEnabled)
|
||||
{
|
||||
// take 1/5 sec for fading the glare effect from invisible to full
|
||||
if (mGlareFade > mGlare)
|
||||
{
|
||||
mGlareFade -= duration*5;
|
||||
if (mGlareFade < mGlare) mGlareFade = mGlare;
|
||||
}
|
||||
else if (mGlareFade < mGlare)
|
||||
{
|
||||
mGlareFade += duration*5;
|
||||
if (mGlareFade > mGlare) mGlareFade = mGlare;
|
||||
}
|
||||
|
||||
// increase the strength of the sun glare effect depending
|
||||
// on how directly the player is looking at the sun
|
||||
Vector3 sun = mSunGlare->getPosition();
|
||||
sun = Vector3(sun.x, sun.z, -sun.y);
|
||||
Vector3 cam = mViewport->getCamera()->getRealDirection();
|
||||
|
@ -573,21 +743,10 @@ void SkyManager::update(float duration)
|
|||
float val = 1- (angle.valueDegrees() / 180.f);
|
||||
val = (val*val*val*val)*2;
|
||||
|
||||
if (mGlareEnabled)
|
||||
{
|
||||
mGlareFade += duration*3;
|
||||
if (mGlareFade > 1) mGlareFade = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mGlareFade -= duration*3;
|
||||
if (mGlareFade < 0.3) mGlareFade = 0;
|
||||
}
|
||||
|
||||
mSunGlare->setSize(val * (mGlareFade));
|
||||
mSunGlare->setSize(val * mGlareFade);
|
||||
}
|
||||
|
||||
mSunGlare->setVisible(mGlareFade>0 && mSunEnabled);
|
||||
mSunGlare->setVisible(mSunEnabled);
|
||||
mSun->setVisible(mSunEnabled);
|
||||
mMasser->setVisible(mMasserEnabled);
|
||||
mSecunda->setVisible(mSecundaEnabled);
|
||||
|
@ -598,6 +757,9 @@ void SkyManager::update(float duration)
|
|||
|
||||
void SkyManager::enable()
|
||||
{
|
||||
if (!mCreated)
|
||||
create();
|
||||
|
||||
mRootNode->setVisible(true);
|
||||
mEnabled = true;
|
||||
}
|
||||
|
@ -610,17 +772,20 @@ void SkyManager::disable()
|
|||
|
||||
void SkyManager::setMoonColour (bool red)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
mSecunda->setColour( red ? ColourValue(1.0, 0.0784, 0.0784)
|
||||
: ColourValue(1.0, 1.0, 1.0));
|
||||
}
|
||||
|
||||
void SkyManager::setCloudsOpacity(float opacity)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
mCloudMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("opacity", Real(opacity));
|
||||
}
|
||||
|
||||
void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
if (mClouds != weather.mCloudTexture)
|
||||
{
|
||||
mCloudMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("textures\\"+weather.mCloudTexture);
|
||||
|
@ -689,19 +854,21 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
|
|||
else
|
||||
strength = 1.f;
|
||||
|
||||
mSunGlare->setVisibility(weather.mGlareView * strength);
|
||||
mSun->setVisibility(strength);
|
||||
mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
|
||||
|
||||
mSun->setVisibility(weather.mGlareView * strength);
|
||||
|
||||
mAtmosphereNight->setVisible(weather.mNight && mEnabled);
|
||||
}
|
||||
|
||||
void SkyManager::setGlare(bool glare)
|
||||
void SkyManager::setGlare(const float glare)
|
||||
{
|
||||
mGlareEnabled = glare;
|
||||
mGlare = glare;
|
||||
}
|
||||
|
||||
Vector3 SkyManager::getRealSunPos()
|
||||
{
|
||||
if (!mCreated) return Vector3(0,0,0);
|
||||
return mSun->getNode()->_getDerivedPosition();
|
||||
}
|
||||
|
||||
|
@ -717,17 +884,20 @@ void SkyManager::sunDisable()
|
|||
|
||||
void SkyManager::setSunDirection(const Vector3& direction)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
mSun->setPosition(direction);
|
||||
mSunGlare->setPosition(direction);
|
||||
}
|
||||
|
||||
void SkyManager::setMasserDirection(const Vector3& direction)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
mMasser->setPosition(direction);
|
||||
}
|
||||
|
||||
void SkyManager::setSecundaDirection(const Vector3& direction)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
mSecunda->setPosition(direction);
|
||||
}
|
||||
|
||||
|
@ -753,6 +923,7 @@ void SkyManager::secundaDisable()
|
|||
|
||||
void SkyManager::setThunder(const float factor)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
if (factor > 0.f)
|
||||
{
|
||||
mThunderOverlay->show();
|
||||
|
@ -764,11 +935,13 @@ void SkyManager::setThunder(const float factor)
|
|||
|
||||
void SkyManager::setMasserFade(const float fade)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
mMasser->setVisibility(fade);
|
||||
}
|
||||
|
||||
void SkyManager::setSecundaFade(const float fade)
|
||||
{
|
||||
if (!mCreated) return;
|
||||
mSecunda->setVisibility(fade);
|
||||
}
|
||||
|
||||
|
@ -782,3 +955,24 @@ void SkyManager::setDate(int day, int month)
|
|||
mDay = day;
|
||||
mMonth = month;
|
||||
}
|
||||
|
||||
Ogre::SceneNode* SkyManager::getSunNode()
|
||||
{
|
||||
if (!mCreated) return 0;
|
||||
return mSun->getNode();
|
||||
}
|
||||
|
||||
void SkyManager::setSkyPosition(const Ogre::Vector3& position)
|
||||
{
|
||||
mRootNode->_setDerivedPosition(position);
|
||||
}
|
||||
|
||||
void SkyManager::resetSkyPosition()
|
||||
{
|
||||
mRootNode->setPosition(0,0,0);
|
||||
}
|
||||
|
||||
void SkyManager::scaleSky(float scale)
|
||||
{
|
||||
mRootNode->setScale(scale, scale, scale);
|
||||
}
|
||||
|
|
|
@ -36,24 +36,25 @@ namespace MWRender
|
|||
BillboardObject();
|
||||
|
||||
virtual ~BillboardObject() {}
|
||||
|
||||
|
||||
void setColour(const Ogre::ColourValue& pColour);
|
||||
void setPosition(const Ogre::Vector3& pPosition);
|
||||
void setVisible(const bool visible);
|
||||
void setRenderQueue(unsigned int id);
|
||||
void setVisibilityFlags(int flags);
|
||||
void setSize(const float size);
|
||||
Ogre::Vector3 getPosition() const;
|
||||
|
||||
|
||||
void setVisibility(const float visibility);
|
||||
|
||||
|
||||
Ogre::SceneNode* getNode();
|
||||
|
||||
|
||||
protected:
|
||||
virtual void init(const Ogre::String& textureName,
|
||||
const float size,
|
||||
const Ogre::Vector3& position,
|
||||
Ogre::SceneNode* rootNode);
|
||||
|
||||
|
||||
Ogre::SceneNode* mNode;
|
||||
Ogre::MaterialPtr mMaterial;
|
||||
Ogre::BillboardSet* mBBSet;
|
||||
|
@ -71,9 +72,9 @@ namespace MWRender
|
|||
const Ogre::Vector3& position,
|
||||
Ogre::SceneNode* rootNode
|
||||
);
|
||||
|
||||
|
||||
virtual ~Moon() {}
|
||||
|
||||
|
||||
enum Phase
|
||||
{
|
||||
Phase_New = 0,
|
||||
|
@ -85,20 +86,20 @@ namespace MWRender
|
|||
Phase_WaningHalf,
|
||||
Phase_WaningCrescent
|
||||
};
|
||||
|
||||
|
||||
enum Type
|
||||
{
|
||||
Type_Masser = 0,
|
||||
Type_Secunda
|
||||
};
|
||||
|
||||
|
||||
void setPhase(const Phase& phase);
|
||||
void setType(const Type& type);
|
||||
void setSkyColour(const Ogre::ColourValue& colour);
|
||||
|
||||
|
||||
Phase getPhase() const;
|
||||
unsigned int getPhaseInt() const;
|
||||
|
||||
|
||||
private:
|
||||
Type mType;
|
||||
Phase mPhase;
|
||||
|
@ -109,61 +110,72 @@ namespace MWRender
|
|||
public:
|
||||
SkyManager(Ogre::SceneNode* pMwRoot, Ogre::Camera* pCamera, MWWorld::Environment* env);
|
||||
~SkyManager();
|
||||
|
||||
|
||||
void update(float duration);
|
||||
|
||||
|
||||
void create();
|
||||
///< no need to call this, automatically done on first enable()
|
||||
|
||||
void enable();
|
||||
|
||||
|
||||
void disable();
|
||||
|
||||
|
||||
void setHour (double hour);
|
||||
///< will be called even when sky is disabled.
|
||||
|
||||
|
||||
void setDate (int day, int month);
|
||||
///< will be called even when sky is disabled.
|
||||
|
||||
|
||||
int getMasserPhase() const;
|
||||
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
|
||||
/// 3 waxing or waning gibbous, 4 full moon
|
||||
|
||||
|
||||
int getSecundaPhase() const;
|
||||
///< 0 new moon, 1 waxing or waning cresecent, 2 waxing or waning half,
|
||||
/// 3 waxing or waning gibbous, 4 full moon
|
||||
|
||||
|
||||
void setMoonColour (bool red);
|
||||
///< change Secunda colour to red
|
||||
|
||||
|
||||
void setCloudsOpacity(float opacity);
|
||||
///< change opacity of the clouds
|
||||
|
||||
|
||||
void setWeather(const MWWorld::WeatherResult& weather);
|
||||
|
||||
|
||||
Ogre::SceneNode* getSunNode();
|
||||
|
||||
void sunEnable();
|
||||
|
||||
|
||||
void sunDisable();
|
||||
|
||||
|
||||
void setSunDirection(const Ogre::Vector3& direction);
|
||||
|
||||
|
||||
void setMasserDirection(const Ogre::Vector3& direction);
|
||||
|
||||
|
||||
void setSecundaDirection(const Ogre::Vector3& direction);
|
||||
|
||||
|
||||
void setMasserFade(const float fade);
|
||||
|
||||
|
||||
void setSecundaFade(const float fade);
|
||||
|
||||
|
||||
void masserEnable();
|
||||
void masserDisable();
|
||||
|
||||
void secundaEnable();
|
||||
void secundaDisable();
|
||||
|
||||
|
||||
void setThunder(const float factor);
|
||||
|
||||
void setGlare(bool glare);
|
||||
|
||||
void setGlare(const float glare);
|
||||
Ogre::Vector3 getRealSunPos();
|
||||
|
||||
|
||||
void setSkyPosition(const Ogre::Vector3& position);
|
||||
void resetSkyPosition();
|
||||
void scaleSky(float scale);
|
||||
|
||||
private:
|
||||
bool mCreated;
|
||||
|
||||
MWWorld::Environment* mEnvironment;
|
||||
float mHour;
|
||||
int mDay;
|
||||
|
@ -173,21 +185,21 @@ namespace MWRender
|
|||
BillboardObject* mSunGlare;
|
||||
Moon* mMasser;
|
||||
Moon* mSecunda;
|
||||
|
||||
|
||||
Ogre::Viewport* mViewport;
|
||||
Ogre::SceneNode* mRootNode;
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
|
||||
|
||||
Ogre::SceneNode* mAtmosphereDay;
|
||||
Ogre::SceneNode* mAtmosphereNight;
|
||||
|
||||
|
||||
Ogre::MaterialPtr mCloudMaterial;
|
||||
Ogre::MaterialPtr mAtmosphereMaterial;
|
||||
|
||||
|
||||
Ogre::MaterialPtr mStarsMaterials[7];
|
||||
|
||||
|
||||
Ogre::HighLevelGpuProgramPtr mCloudFragmentShader;
|
||||
|
||||
|
||||
// remember some settings so we don't have to apply them again if they didnt change
|
||||
Ogre::String mClouds;
|
||||
Ogre::String mNextClouds;
|
||||
|
@ -197,18 +209,18 @@ namespace MWRender
|
|||
float mStarsOpacity;
|
||||
Ogre::ColourValue mCloudColour;
|
||||
Ogre::ColourValue mSkyColour;
|
||||
|
||||
|
||||
Ogre::Overlay* mThunderOverlay;
|
||||
Ogre::TextureUnitState* mThunderTextureUnit;
|
||||
|
||||
|
||||
float mRemainingTransitionTime;
|
||||
|
||||
float mGlareFade;
|
||||
|
||||
|
||||
float mGlare; // target
|
||||
float mGlareFade; // actual
|
||||
|
||||
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
|
||||
|
||||
|
||||
bool mEnabled;
|
||||
bool mGlareEnabled;
|
||||
bool mSunEnabled;
|
||||
bool mMasserEnabled;
|
||||
bool mSecundaEnabled;
|
||||
|
|
531
apps/openmw/mwrender/terrain.cpp
Normal file
531
apps/openmw/mwrender/terrain.cpp
Normal file
|
@ -0,0 +1,531 @@
|
|||
#include <OgreTerrain.h>
|
||||
#include <OgreTerrainGroup.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwworld/world.hpp"
|
||||
|
||||
#include "terrainmaterial.hpp"
|
||||
#include "terrain.hpp"
|
||||
#include "renderconst.hpp"
|
||||
#include "shadows.hpp"
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
TerrainManager::TerrainManager(Ogre::SceneManager* mgr, RenderingManager* rend, const MWWorld::Environment& evn) :
|
||||
mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)), mRendering(rend)
|
||||
{
|
||||
|
||||
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);
|
||||
|
||||
bool shadows = Settings::Manager::getBool("enabled", "Shadows");
|
||||
mActiveProfile->setReceiveDynamicShadowsEnabled(shadows);
|
||||
mActiveProfile->setReceiveDynamicShadowsDepth(shadows);
|
||||
if (Settings::Manager::getBool("split", "Shadows"))
|
||||
mActiveProfile->setReceiveDynamicShadowsPSSM(mRendering->getShadows()->getPSSMSetup());
|
||||
else
|
||||
mActiveProfile->setReceiveDynamicShadowsPSSM(0);
|
||||
|
||||
mActiveProfile->setShadowFar(mRendering->getShadows()->getShadowFar());
|
||||
mActiveProfile->setShadowFadeStart(mRendering->getShadows()->getFadeStart());
|
||||
|
||||
//composite maps lead to a drastic increase 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 )
|
||||
{
|
||||
if (!land->dataLoaded)
|
||||
{
|
||||
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);
|
||||
terrain->setVisibilityFlags(RV_Terrain);
|
||||
terrain->setRenderQueueGroup(RQG_Main);
|
||||
|
||||
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 )
|
||||
{
|
||||
if (!land->dataLoaded)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
118
apps/openmw/mwrender/terrain.hpp
Normal file
118
apps/openmw/mwrender/terrain.hpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#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, RenderingManager* rend, 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;
|
||||
RenderingManager* mRendering;
|
||||
|
||||
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
|
1746
apps/openmw/mwrender/terrainmaterial.cpp
Normal file
1746
apps/openmw/mwrender/terrainmaterial.cpp
Normal file
File diff suppressed because it is too large
Load diff
271
apps/openmw/mwrender/terrainmaterial.hpp
Normal file
271
apps/openmw/mwrender/terrainmaterial.hpp
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
-----------------------------------------------------------------------------
|
||||
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);
|
||||
|
||||
void setShadowFar(float far);
|
||||
void setShadowFadeStart(float fadestart);
|
||||
|
||||
/** 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;
|
||||
float mShadowFar;
|
||||
float mShadowFadeStart;
|
||||
|
||||
bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
293
apps/openmw/mwrender/water.cpp
Normal file
293
apps/openmw/mwrender/water.cpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
#include "water.hpp"
|
||||
#include <components/settings/settings.hpp>
|
||||
#include "sky.hpp"
|
||||
#include "renderingmanager.hpp"
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
Water::Water (Ogre::Camera *camera, SkyManager* sky, const ESM::Cell* cell) :
|
||||
mCamera (camera), mViewport (camera->getViewport()), mSceneManager (camera->getSceneManager()),
|
||||
mIsUnderwater(false), mVisibilityFlags(0),
|
||||
mReflectionTarget(0), mActive(1), mToggled(1)
|
||||
{
|
||||
mSky = sky;
|
||||
|
||||
try
|
||||
{
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
|
||||
} catch(...) {}
|
||||
|
||||
mTop = cell->water;
|
||||
|
||||
mIsUnderwater = false;
|
||||
|
||||
mWaterPlane = Plane(Vector3::UNIT_Y, 0);
|
||||
|
||||
MeshManager::getSingleton().createPlane("water", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,3, Vector3::UNIT_Z);
|
||||
|
||||
mWater = mSceneManager->createEntity("water");
|
||||
mWater->setVisibilityFlags(RV_Water);
|
||||
mWater->setRenderQueueGroup(RQG_Water);
|
||||
mWater->setCastShadows(false);
|
||||
|
||||
mVisibilityFlags = RV_Terrain * Settings::Manager::getBool("reflect terrain", "Water")
|
||||
+ RV_Statics * Settings::Manager::getBool("reflect statics", "Water")
|
||||
+ RV_StaticsSmall * Settings::Manager::getBool("reflect small statics", "Water")
|
||||
+ RV_Actors * Settings::Manager::getBool("reflect actors", "Water")
|
||||
+ RV_Misc * Settings::Manager::getBool("reflect misc", "Water")
|
||||
+ RV_Sky;
|
||||
|
||||
mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
|
||||
mWaterNode->setPosition(0, mTop, 0);
|
||||
|
||||
mReflectionCamera = mSceneManager->createCamera("ReflectionCamera");
|
||||
|
||||
if(!(cell->data.flags & cell->Interior))
|
||||
{
|
||||
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
|
||||
}
|
||||
mWaterNode->attachObject(mWater);
|
||||
|
||||
// Create rendertarget for reflection
|
||||
int rttsize = Settings::Manager::getInt("rtt size", "Water");
|
||||
|
||||
TexturePtr tex;
|
||||
if (Settings::Manager::getBool("shader", "Water"))
|
||||
{
|
||||
tex = TextureManager::getSingleton().createManual("WaterReflection",
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, rttsize, rttsize, 0, PF_FLOAT16_RGBA, TU_RENDERTARGET);
|
||||
|
||||
RenderTarget* rtt = tex->getBuffer()->getRenderTarget();
|
||||
Viewport* vp = rtt->addViewport(mReflectionCamera);
|
||||
vp->setOverlaysEnabled(false);
|
||||
vp->setBackgroundColour(ColourValue(0.8f, 0.9f, 1.0f));
|
||||
vp->setShadowsEnabled(false);
|
||||
vp->setVisibilityMask( mVisibilityFlags );
|
||||
// use fallback techniques without shadows and without mrt (currently not implemented for sky and terrain)
|
||||
//vp->setMaterialScheme("Fallback");
|
||||
rtt->addListener(this);
|
||||
rtt->setActive(true);
|
||||
|
||||
mReflectionTarget = rtt;
|
||||
}
|
||||
|
||||
mCompositorName = RenderingManager::useMRT() ? "Underwater" : "UnderwaterNoMRT";
|
||||
|
||||
createMaterial();
|
||||
mWater->setMaterial(mMaterial);
|
||||
|
||||
mUnderwaterEffect = Settings::Manager::getBool("underwater effect", "Water");
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// ---------------------------------- reflection debug overlay ----------------------------------
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
/*
|
||||
if (Settings::Manager::getBool("shader", "Water"))
|
||||
{
|
||||
OverlayManager& mgr = OverlayManager::getSingleton();
|
||||
Overlay* overlay;
|
||||
// destroy if already exists
|
||||
if (overlay = mgr.getByName("ReflectionDebugOverlay"))
|
||||
mgr.destroy(overlay);
|
||||
|
||||
overlay = mgr.create("ReflectionDebugOverlay");
|
||||
|
||||
if (MaterialManager::getSingleton().resourceExists("Ogre/ReflectionDebugTexture"))
|
||||
MaterialManager::getSingleton().remove("Ogre/ReflectionDebugTexture");
|
||||
MaterialPtr debugMat = MaterialManager::getSingleton().create(
|
||||
"Ogre/ReflectionDebugTexture",
|
||||
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
||||
|
||||
debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false);
|
||||
TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName());
|
||||
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
|
||||
|
||||
OverlayContainer* debugPanel;
|
||||
|
||||
// destroy container if exists
|
||||
try
|
||||
{
|
||||
if (debugPanel =
|
||||
static_cast<OverlayContainer*>(
|
||||
mgr.getOverlayElement("Ogre/ReflectionDebugTexPanel"
|
||||
)))
|
||||
mgr.destroyOverlayElement(debugPanel);
|
||||
}
|
||||
catch (Ogre::Exception&) {}
|
||||
|
||||
debugPanel = (OverlayContainer*)
|
||||
(OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/ReflectionDebugTexPanel"));
|
||||
debugPanel->_setPosition(0, 0.55);
|
||||
debugPanel->_setDimensions(0.3, 0.3);
|
||||
debugPanel->setMaterialName(debugMat->getName());
|
||||
debugPanel->show();
|
||||
overlay->add2D(debugPanel);
|
||||
overlay->show();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void Water::setActive(bool active)
|
||||
{
|
||||
mActive = active;
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
Water::~Water()
|
||||
{
|
||||
MeshManager::getSingleton().remove("water");
|
||||
|
||||
mWaterNode->detachObject(mWater);
|
||||
mSceneManager->destroyEntity(mWater);
|
||||
mSceneManager->destroySceneNode(mWaterNode);
|
||||
|
||||
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()
|
||||
{
|
||||
mToggled = !mToggled;
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
void Water::checkUnderwater(float y)
|
||||
{
|
||||
if (!mActive) return;
|
||||
if ((mIsUnderwater && y > mTop) || !mWater->isVisible() || mCamera->getPolygonMode() != Ogre::PM_SOLID)
|
||||
{
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mViewport, mCompositorName, false);
|
||||
|
||||
// tell the shader we are not underwater
|
||||
Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0);
|
||||
if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false))
|
||||
pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(0));
|
||||
|
||||
mWater->setRenderQueueGroup(RQG_Water);
|
||||
|
||||
mIsUnderwater = false;
|
||||
}
|
||||
|
||||
if (!mIsUnderwater && y < mTop && mWater->isVisible() && mCamera->getPolygonMode() == Ogre::PM_SOLID)
|
||||
{
|
||||
if (mUnderwaterEffect)
|
||||
CompositorManager::getSingleton().setCompositorEnabled(mViewport, mCompositorName, true);
|
||||
|
||||
// tell the shader we are underwater
|
||||
Ogre::Pass* pass = mMaterial->getTechnique(0)->getPass(0);
|
||||
if (pass->hasFragmentProgram() && pass->getFragmentProgramParameters()->_findNamedConstantDefinition("isUnderwater", false))
|
||||
pass->getFragmentProgramParameters()->setNamedConstant("isUnderwater", Real(1));
|
||||
|
||||
mWater->setRenderQueueGroup(RQG_UnderWater);
|
||||
|
||||
mIsUnderwater = true;
|
||||
}
|
||||
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY)
|
||||
{
|
||||
return Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2));
|
||||
}
|
||||
|
||||
void Water::preRenderTargetUpdate(const RenderTargetEvent& evt)
|
||||
{
|
||||
if (evt.source == mReflectionTarget)
|
||||
{
|
||||
mReflectionCamera->setOrientation(mCamera->getDerivedOrientation());
|
||||
mReflectionCamera->setPosition(mCamera->getDerivedPosition());
|
||||
mReflectionCamera->setNearClipDistance(mCamera->getNearClipDistance());
|
||||
mReflectionCamera->setFarClipDistance(mCamera->getFarClipDistance());
|
||||
mReflectionCamera->setAspectRatio(mCamera->getAspectRatio());
|
||||
mReflectionCamera->setFOVy(mCamera->getFOVy());
|
||||
|
||||
// Some messy code to get the skybox to show up at all
|
||||
// The problem here is that it gets clipped by the water plane
|
||||
// Therefore scale it up a bit
|
||||
Vector3 pos = mCamera->getRealPosition();
|
||||
pos.y = mTop*2 - pos.y;
|
||||
mSky->setSkyPosition(pos);
|
||||
mSky->scaleSky(mCamera->getFarClipDistance() / 1000.f);
|
||||
|
||||
mReflectionCamera->enableCustomNearClipPlane(Plane(Vector3::UNIT_Y, mTop));
|
||||
mReflectionCamera->enableReflection(Plane(Vector3::UNIT_Y, mTop));
|
||||
}
|
||||
}
|
||||
|
||||
void Water::postRenderTargetUpdate(const RenderTargetEvent& evt)
|
||||
{
|
||||
if (evt.source == mReflectionTarget)
|
||||
{
|
||||
mSky->resetSkyPosition();
|
||||
mSky->scaleSky(1);
|
||||
mReflectionCamera->disableCustomNearClipPlane();
|
||||
mReflectionCamera->disableReflection();
|
||||
}
|
||||
}
|
||||
|
||||
void Water::createMaterial()
|
||||
{
|
||||
mMaterial = MaterialManager::getSingleton().getByName("Water");
|
||||
|
||||
// these have to be set in code
|
||||
std::string textureNames[32];
|
||||
for (int i=0; i<32; ++i)
|
||||
{
|
||||
textureNames[i] = "textures\\water\\water" + StringConverter::toString(i, 2, '0') + ".dds";
|
||||
}
|
||||
mMaterial->getTechnique(1)->getPass(0)->getTextureUnitState(0)->setAnimatedTextureName(textureNames, 32, 2);
|
||||
|
||||
// use technique without shaders if reflection is disabled
|
||||
if (mReflectionTarget == 0)
|
||||
mMaterial->removeTechnique(0);
|
||||
|
||||
if (Settings::Manager::getBool("shader", "Water"))
|
||||
{
|
||||
CompositorInstance* compositor = CompositorManager::getSingleton().getCompositorChain(mViewport)->getCompositor("gbuffer");
|
||||
|
||||
TexturePtr colorTexture = compositor->getTextureInstance("mrt_output", 0);
|
||||
TextureUnitState* tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("refractionMap");
|
||||
if (tus != 0)
|
||||
tus->setTexture(colorTexture);
|
||||
|
||||
TexturePtr depthTexture = compositor->getTextureInstance("mrt_output", 1);
|
||||
tus = mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState("depthMap");
|
||||
if (tus != 0)
|
||||
tus->setTexture(depthTexture);
|
||||
}
|
||||
}
|
||||
|
||||
void Water::setViewportBackground(const ColourValue& bg)
|
||||
{
|
||||
if (mReflectionTarget)
|
||||
mReflectionTarget->getViewport(0)->setBackgroundColour(bg);
|
||||
}
|
||||
|
||||
void Water::updateVisible()
|
||||
{
|
||||
mWater->setVisible(mToggled && mActive);
|
||||
if (mReflectionTarget)
|
||||
mReflectionTarget->setActive(mToggled && mActive && !mIsUnderwater);
|
||||
}
|
||||
|
||||
} // namespace
|
69
apps/openmw/mwrender/water.hpp
Normal file
69
apps/openmw/mwrender/water.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#ifndef GAME_MWRENDER_WATER_H
|
||||
#define GAME_MWRENDER_WATER_H
|
||||
|
||||
#include <Ogre.h>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
#include "renderconst.hpp"
|
||||
|
||||
namespace MWRender {
|
||||
|
||||
class SkyManager;
|
||||
|
||||
/// Water rendering
|
||||
class Water : public Ogre::RenderTargetListener
|
||||
{
|
||||
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;
|
||||
bool mActive;
|
||||
bool mToggled;
|
||||
int mTop;
|
||||
|
||||
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
|
||||
|
||||
protected:
|
||||
void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
|
||||
void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);
|
||||
void updateVisible();
|
||||
|
||||
SkyManager* mSky;
|
||||
|
||||
std::string mCompositorName;
|
||||
|
||||
void createMaterial();
|
||||
Ogre::MaterialPtr mMaterial;
|
||||
|
||||
Ogre::Camera* mReflectionCamera;
|
||||
|
||||
Ogre::RenderTarget* mReflectionTarget;
|
||||
|
||||
bool mUnderwaterEffect;
|
||||
int mVisibilityFlags;
|
||||
|
||||
public:
|
||||
Water (Ogre::Camera *camera, SkyManager* sky, const ESM::Cell* cell);
|
||||
~Water();
|
||||
|
||||
void setActive(bool active);
|
||||
|
||||
void toggle();
|
||||
|
||||
void setViewportBackground(const Ogre::ColourValue& bg);
|
||||
|
||||
void checkUnderwater(float y);
|
||||
void changeCell(const ESM::Cell* cell);
|
||||
void setHeight(const float height);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -133,11 +133,70 @@ namespace MWScript
|
|||
}
|
||||
};
|
||||
|
||||
class OpGetWaterLevel : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
InterpreterContext& context
|
||||
= static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
|
||||
runtime.push (cell->mWaterLevel);
|
||||
}
|
||||
};
|
||||
|
||||
class OpSetWaterLevel : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
InterpreterContext& context
|
||||
= static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
Interpreter::Type_Float level = runtime[0].mFloat;
|
||||
|
||||
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
|
||||
|
||||
if (!(cell->cell->data.flags & ESM::Cell::Interior))
|
||||
throw std::runtime_error("Can't set water level in exterior cell");
|
||||
|
||||
cell->mWaterLevel = level;
|
||||
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
|
||||
}
|
||||
};
|
||||
|
||||
class OpModWaterLevel : public Interpreter::Opcode0
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
InterpreterContext& context
|
||||
= static_cast<InterpreterContext&> (runtime.getContext());
|
||||
|
||||
Interpreter::Type_Float level = runtime[0].mFloat;
|
||||
|
||||
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
|
||||
|
||||
if (!(cell->cell->data.flags & ESM::Cell::Interior))
|
||||
throw std::runtime_error("Can't set water level in exterior cell");
|
||||
|
||||
cell->mWaterLevel +=level;
|
||||
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
|
||||
}
|
||||
};
|
||||
|
||||
const int opcodeCellChanged = 0x2000000;
|
||||
const int opcodeCOC = 0x2000026;
|
||||
const int opcodeCOE = 0x200008e;
|
||||
const int opcodeGetInterior = 0x2000131;
|
||||
const int opcodeGetPCCell = 0x2000136;
|
||||
const int opcodeGetWaterLevel = 0x2000141;
|
||||
const int opcodeSetWaterLevel = 0x2000142;
|
||||
const int opcodeModWaterLevel = 0x2000143;
|
||||
|
||||
void registerExtensions (Compiler::Extensions& extensions)
|
||||
{
|
||||
|
@ -146,8 +205,11 @@ namespace MWScript
|
|||
extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
|
||||
extensions.registerInstruction ("coe", "ll", opcodeCOE);
|
||||
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
|
||||
extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel);
|
||||
extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel);
|
||||
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
|
||||
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
|
||||
extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel);
|
||||
}
|
||||
|
||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||
|
@ -157,6 +219,9 @@ namespace MWScript
|
|||
interpreter.installSegment5 (opcodeCOE, new OpCOE);
|
||||
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
|
||||
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
|
||||
interpreter.installSegment5 (opcodeGetWaterLevel, new OpGetWaterLevel);
|
||||
interpreter.installSegment5 (opcodeSetWaterLevel, new OpSetWaterLevel);
|
||||
interpreter.installSegment5 (opcodeModWaterLevel, new OpModWaterLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue