summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis-build-docker.sh20
-rwxr-xr-x.travis-build.sh10
-rwxr-xr-x.travis-deps.sh30
-rwxr-xr-x.travis-upload.sh235
-rw-r--r--.travis.yml18
-rw-r--r--appveyor.yml32
m---------externals/cryptopp/cryptopp0
-rw-r--r--src/citra/default_ini.h6
-rw-r--r--src/common/quaternion.h5
-rw-r--r--src/common/vector_math.h7
-rw-r--r--src/core/hle/service/cfg/cfg.cpp2
-rw-r--r--src/core/hle/service/dlp/dlp_clnt.cpp21
-rw-r--r--src/core/hle/service/dlp/dlp_fkcl.cpp18
-rw-r--r--src/core/hle/service/dlp/dlp_srvr.cpp9
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp2
-rw-r--r--src/input_common/sdl/sdl.cpp2
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/command_processor.cpp62
-rw-r--r--src/video_core/pica_state.h2
-rw-r--r--src/video_core/regs_pipeline.h9
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp9
-rw-r--r--src/video_core/swrasterizer/clipper.cpp11
-rw-r--r--src/video_core/swrasterizer/lighting.cpp250
-rw-r--r--src/video_core/swrasterizer/lighting.h18
-rw-r--r--src/video_core/swrasterizer/rasterizer.cpp29
26 files changed, 579 insertions, 232 deletions
diff --git a/.travis-build-docker.sh b/.travis-build-docker.sh
new file mode 100644
index 000000000..ca6fae42b
--- /dev/null
+++ b/.travis-build-docker.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -e
+set -x
+
+cd /citra
+
+apt-get update
+apt-get install -y build-essential libsdl2-dev qtbase5-dev libqt5opengl5-dev libcurl4-openssl-dev libssl-dev wget git
+
+# Get a recent version of CMake
+wget https://cmake.org/files/v3.9/cmake-3.9.0-Linux-x86_64.sh
+echo y | sh cmake-3.9.0-Linux-x86_64.sh --prefix=cmake
+export PATH=/citra/cmake/cmake-3.9.0-Linux-x86_64/bin:$PATH
+
+mkdir build && cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+make -j4
+
+ctest -VV -C Release
diff --git a/.travis-build.sh b/.travis-build.sh
index df6e236b6..64f5aed94 100755
--- a/.travis-build.sh
+++ b/.travis-build.sh
@@ -44,15 +44,7 @@ fi
#if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
- export CC=gcc-6
- export CXX=g++-6
- export PKG_CONFIG_PATH=$HOME/.local/lib/pkgconfig:$PKG_CONFIG_PATH
-
- mkdir build && cd build
- cmake ..
- make -j4
-
- ctest -VV -C Release
+ docker run -v $(pwd):/citra ubuntu:16.04 /bin/bash /citra/.travis-build-docker.sh
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
set -o pipefail
diff --git a/.travis-deps.sh b/.travis-deps.sh
index 25a287c7f..0cee68041 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -5,35 +5,7 @@ set -x
#if OS is linux or is not set
if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
- export CC=gcc-6
- export CXX=g++-6
- mkdir -p $HOME/.local
-
- if [ ! -e $HOME/.local/bin/cmake ]; then
- echo "CMake not found in the cache, get and extract it..."
- curl -L http://www.cmake.org/files/v3.6/cmake-3.6.3-Linux-x86_64.tar.gz \
- | tar -xz -C $HOME/.local --strip-components=1
- else
- echo "Using cached CMake"
- fi
-
- if [ ! -e $HOME/.local/lib/libSDL2.la ]; then
- echo "SDL2 not found in cache, get and build it..."
- wget http://libsdl.org/release/SDL2-2.0.5.tar.gz -O - | tar xz
- cd SDL2-2.0.5
- ./configure --prefix=$HOME/.local
- make -j4 && make install
- else
- echo "Using cached SDL2"
- fi
-
- export DEBIAN_FRONTEND=noninteractive
- # Amazing placebo security
- curl http://apt.llvm.org/llvm-snapshot.gpg.key | sudo -E apt-key add -
- sudo -E add-apt-repository "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
- sudo -E apt-get -yq update
- sudo -E apt-get -yq install clang-format-3.9
-
+ docker pull ubuntu:16.04
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update
brew install qt5 sdl2 dylibbundler p7zip
diff --git a/.travis-upload.sh b/.travis-upload.sh
index 8cfab31cb..8c1fa21c5 100755
--- a/.travis-upload.sh
+++ b/.travis-upload.sh
@@ -1,134 +1,139 @@
-if [ "$TRAVIS_EVENT_TYPE" = "push" ]&&[ "$TRAVIS_BRANCH" = "master" ]; then
- GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
- GITREV="`git show -s --format='%h'`"
- mkdir -p artifacts
-
- if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
- REV_NAME="citra-linux-${GITDATE}-${GITREV}"
- ARCHIVE_NAME="${REV_NAME}.tar.xz"
- COMPRESSION_FLAGS="-cJvf"
- mkdir "$REV_NAME"
-
- cp build/src/citra/citra "$REV_NAME"
- cp build/src/citra_qt/citra-qt "$REV_NAME"
- elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
- REV_NAME="citra-osx-${GITDATE}-${GITREV}"
- ARCHIVE_NAME="${REV_NAME}.tar.gz"
- COMPRESSION_FLAGS="-czvf"
- mkdir "$REV_NAME"
-
- cp build/src/citra/Release/citra "$REV_NAME"
- cp -r build/src/citra_qt/Release/citra-qt.app "$REV_NAME"
-
- # move qt libs into app bundle for deployment
- $(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/citra-qt.app"
-
- # move SDL2 libs into folder for deployment
- dylibbundler -b -x "${REV_NAME}/citra" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/"
-
- # Make the changes to make the citra-qt app standalone (i.e. not dependent on the current brew installation).
- # To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks
- # (in the Contents/Frameworks folder).
- # The "install_name_tool" is used to do so.
-
- # Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e:
- # ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1
- # grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1
- brew install coreutils
-
- REV_NAME_ALT=$REV_NAME/
- # grealpath is located in coreutils, there is no "realpath" for OS X :(
- QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)")
- BREW_PATH=$(brew --prefix)
- QT_VERSION_NUM=5
-
- $BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}citra-qt.app" \
- -executable="${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt"
-
- # These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them.
- declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport")
-
- for macos_lib in "${macos_libs[@]}"
+GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
+GITREV="`git show -s --format='%h'`"
+mkdir -p artifacts
+
+if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
+ REV_NAME="citra-linux-${GITDATE}-${GITREV}"
+ ARCHIVE_NAME="${REV_NAME}.tar.xz"
+ COMPRESSION_FLAGS="-cJvf"
+ mkdir "$REV_NAME"
+
+ cp build/src/citra/citra "$REV_NAME"
+ cp build/src/citra_qt/citra-qt "$REV_NAME"
+elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
+ REV_NAME="citra-osx-${GITDATE}-${GITREV}"
+ ARCHIVE_NAME="${REV_NAME}.tar.gz"
+ COMPRESSION_FLAGS="-czvf"
+ mkdir "$REV_NAME"
+
+ cp build/src/citra/Release/citra "$REV_NAME"
+ cp -r build/src/citra_qt/Release/citra-qt.app "$REV_NAME"
+
+ # move qt libs into app bundle for deployment
+ $(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/citra-qt.app"
+
+ # move SDL2 libs into folder for deployment
+ dylibbundler -b -x "${REV_NAME}/citra" -cd -d "${REV_NAME}/libs" -p "@executable_path/libs/"
+
+ # Make the changes to make the citra-qt app standalone (i.e. not dependent on the current brew installation).
+ # To do this, the absolute references to each and every QT framework must be re-written to point to the local frameworks
+ # (in the Contents/Frameworks folder).
+ # The "install_name_tool" is used to do so.
+
+ # Coreutils is a hack to coerce Homebrew to point to the absolute Cellar path (symlink dereferenced). i.e:
+ # ls -l /usr/local/opt/qt5:: /usr/local/opt/qt5 -> ../Cellar/qt5/5.6.1-1
+ # grealpath ../Cellar/qt5/5.6.1-1:: /usr/local/Cellar/qt5/5.6.1-1
+ brew install coreutils
+
+ REV_NAME_ALT=$REV_NAME/
+ # grealpath is located in coreutils, there is no "realpath" for OS X :(
+ QT_BREWS_PATH=$(grealpath "$(brew --prefix qt5)")
+ BREW_PATH=$(brew --prefix)
+ QT_VERSION_NUM=5
+
+ $BREW_PATH/opt/qt5/bin/macdeployqt "${REV_NAME_ALT}citra-qt.app" \
+ -executable="${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt"
+
+ # These are the files that macdeployqt packed into Contents/Frameworks/ - we don't want those, so we replace them.
+ declare -a macos_libs=("QtCore" "QtWidgets" "QtGui" "QtOpenGL" "QtPrintSupport")
+
+ for macos_lib in "${macos_libs[@]}"
+ do
+ SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib
+ # Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/)
+ cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
+
+ # Replace references within the embedded Framework files with "internal" versions.
+ for macos_lib2 in "${macos_libs[@]}"
do
- SC_FRAMEWORK_PART=$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib
- # Replace macdeployqt versions of the Frameworks with our own (from /usr/local/opt/qt5/lib/)
- cp "$BREW_PATH/opt/qt5/lib/$SC_FRAMEWORK_PART" "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
-
- # Replace references within the embedded Framework files with "internal" versions.
- for macos_lib2 in "${macos_libs[@]}"
- do
- # Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated.
- # /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files.
- # So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't.
- RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
- install_name_tool -change \
- $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
- @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
- "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
- install_name_tool -change \
- "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
- @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
- "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
- done
+ # Since brew references both the non-symlinked and symlink paths of QT5, it needs to be duplicated.
+ # /usr/local/Cellar/qt5/5.6.1-1/lib and /usr/local/opt/qt5/lib both resolve to the same files.
+ # So the two lines below are effectively duplicates when resolved as a path, but as strings, they aren't.
+ RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
+ install_name_tool -change \
+ $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
+ @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
+ "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
+ install_name_tool -change \
+ "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
+ @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
+ "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$SC_FRAMEWORK_PART"
done
-
- # Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"`
- # Which manifests itself as:
- # "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY"
- # There may be more dylibs needed to be fixed...
- declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib")
-
- for macos_lib in "${macos_plugins[@]}"
+ done
+
+ # Handles `This application failed to start because it could not find or load the Qt platform plugin "cocoa"`
+ # Which manifests itself as:
+ # "Exception Type: EXC_CRASH (SIGABRT) | Exception Codes: 0x0000000000000000, 0x0000000000000000 | Exception Note: EXC_CORPSE_NOTIFY"
+ # There may be more dylibs needed to be fixed...
+ declare -a macos_plugins=("Plugins/platforms/libqcocoa.dylib")
+
+ for macos_lib in "${macos_plugins[@]}"
+ do
+ install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
+ for macos_lib2 in "${macos_libs[@]}"
do
- install_name_tool -id @executable_path/../$macos_lib "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
- for macos_lib2 in "${macos_libs[@]}"
- do
- RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
- install_name_tool -change \
- $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
- @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
- "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
- install_name_tool -change \
- "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
- @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
- "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
- done
+ RM_FRAMEWORK_PART=$macos_lib2.framework/Versions/$QT_VERSION_NUM/$macos_lib2
+ install_name_tool -change \
+ $QT_BREWS_PATH/lib/$RM_FRAMEWORK_PART \
+ @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
+ "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
+ install_name_tool -change \
+ "$BREW_PATH/opt/qt5/lib/$RM_FRAMEWORK_PART" \
+ @executable_path/../Frameworks/$RM_FRAMEWORK_PART \
+ "${REV_NAME_ALT}citra-qt.app/Contents/$macos_lib"
done
+ done
- for macos_lib in "${macos_libs[@]}"
- do
- # Debugging info for Travis-CI
- otool -L "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib"
- done
+ for macos_lib in "${macos_libs[@]}"
+ do
+ # Debugging info for Travis-CI
+ otool -L "${REV_NAME_ALT}citra-qt.app/Contents/Frameworks/$macos_lib.framework/Versions/$QT_VERSION_NUM/$macos_lib"
+ done
- # Make the citra-qt.app application launch a debugging terminal.
- # Store away the actual binary
- mv ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt-bin
+ # Make the citra-qt.app application launch a debugging terminal.
+ # Store away the actual binary
+ mv ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt-bin
- cat > ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt <<EOL
+ cat > ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt <<EOL
#!/usr/bin/env bash
cd "\`dirname "\$0"\`"
chmod +x citra-qt-bin
open citra-qt-bin --args "\$@"
EOL
- # Content that will serve as the launching script for citra (within the .app folder)
+ # Content that will serve as the launching script for citra (within the .app folder)
- # Make the launching script executable
- chmod +x ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt
+ # Make the launching script executable
+ chmod +x ${REV_NAME_ALT}citra-qt.app/Contents/MacOS/citra-qt
- fi
+fi
+
+# Copy documentation
+cp license.txt "$REV_NAME"
+cp README.md "$REV_NAME"
- # Copy documentation
- cp license.txt "$REV_NAME"
- cp README.md "$REV_NAME"
+tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$REV_NAME"
- tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$REV_NAME"
+# Find out what release we are building
+if [ -z $TRAVIS_TAG ]; then
+ RELEASE_NAME=head
+else
+ RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1)
+fi
- mv "$REV_NAME" nightly
+mv "$REV_NAME" $RELEASE_NAME
- 7z a "$REV_NAME.7z" nightly
+7z a "$REV_NAME.7z" $RELEASE_NAME
- # move the compiled archive into the artifacts directory to be uploaded by travis releases
- mv "$ARCHIVE_NAME" artifacts/
- mv "$REV_NAME.7z" artifacts/
-fi
+# move the compiled archive into the artifacts directory to be uploaded by travis releases
+mv "$ARCHIVE_NAME" artifacts/
+mv "$REV_NAME.7z" artifacts/
diff --git a/.travis.yml b/.travis.yml
index 846758881..b92d7f236 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,23 +8,15 @@ matrix:
sudo: false
osx_image: xcode7.3
+services:
+ - docker
+
addons:
apt:
- sources:
- - ubuntu-toolchain-r-test
packages:
- - gcc-6
- - g++-6
- - qt5-default
- - libqt5opengl5-dev
- - xorg-dev
- - lib32stdc++6 # For CMake
+ - clang-format-3.9
- p7zip-full
-cache:
- directories:
- - "$HOME/.local"
-
install: "./.travis-deps.sh"
script: "./.travis-build.sh"
after_success: "./.travis-upload.sh"
@@ -37,4 +29,4 @@ deploy:
file: "artifacts/*"
skip_cleanup: true
on:
- repo: citra-emu/citra-nightly
+ tags: true
diff --git a/appveyor.yml b/appveyor.yml
index d062a1f3e..94e9969f5 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,9 +1,6 @@
# shallow clone
clone_depth: 10
-# don't build on tag
-skip_tags: true
-
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
@@ -49,13 +46,21 @@ after_build:
7z a -tzip $MSVC_BUILD_PDB .\build\bin\release\*.pdb
rm .\build\bin\release\*.pdb
- mkdir nightly
- Copy-Item .\build\bin\release\* -Destination nightly -Recurse
- Copy-Item .\license.txt -Destination nightly
- Copy-Item .\README.md -Destination nightly
+ # Find out which kind of release we are producing by tag name
+ if ($env:APPVEYOR_REPO_TAG_NAME) {
+ $RELEASE_DIST, $RELEASE_VERSION = $env:APPVEYOR_REPO_TAG_NAME.split('-')
+ } else {
+ # There is no repo tag - make assumptions
+ $RELEASE_DIST = "head"
+ }
+
+ mkdir $RELEASE_DIST
+ Copy-Item .\build\bin\release\* -Destination $RELEASE_DIST -Recurse
+ Copy-Item .\license.txt -Destination $RELEASE_DIST
+ Copy-Item .\README.md -Destination $RELEASE_DIST
- 7z a -tzip $MSVC_BUILD_NAME nightly\*
- 7z a $MSVC_SEVENZIP nightly
+ 7z a -tzip $MSVC_BUILD_NAME $RELEASE_DIST\*
+ 7z a $MSVC_SEVENZIP $RELEASE_DIST
test_script:
- cd build && ctest -VV -C Release && cd ..
@@ -72,16 +77,11 @@ artifacts:
deploy:
provider: GitHub
- release: nightly-$(appveyor_build_number)
- description: |
- Citra nightly releases. Please choose the correct download for your operating system from the list below.
-
- Short Commit Hash $(GITREV)
+ release: $(appveyor_repo_tag_name)
auth_token:
secure: "dbpsMC/MgPKWFNJCXpQl4cR8FYhepkPLjgNp/pRMktZ8oLKTqPYErfreaIxb/4P1"
artifact: msvcupdate,msvcbuild
draft: false
prerelease: false
on:
- branch: master
- appveyor_repo_name: citra-emu/citra-nightly
+ appveyor_repo_tag: true
diff --git a/externals/cryptopp/cryptopp b/externals/cryptopp/cryptopp
-Subproject 841c37e34765487a2968357369ab74db8b10a62
+Subproject 24bc2b85674254fb294e717eb5b47d9f53e786b
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index a12498e0f..b0a0ebd3b 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -12,7 +12,7 @@ const char* sdl2_config_file = R"(
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
-# for button input, the following devices are avaible:
+# for button input, the following devices are available:
# - "keyboard" (default) for keyboard input. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for joystick input using SDL. Required parameters:
@@ -21,7 +21,7 @@ const char* sdl2_config_file = R"(
# - "hat"(optional): the index of the hat to bind as direction buttons
# - "axis"(optional): the index of the axis to bind
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
-# - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is
+# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
# triggered if the axis value crosses
# - "direction"(only used for axis): "+" means the button is triggered when the axis value
# is greater than the threshold; "-" means the button is triggered when the axis value
@@ -42,7 +42,7 @@ button_zl=
button_zr=
button_home=
-# for analog input, the following devices are avaible:
+# for analog input, the following devices are available:
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
# - "up", "down", "left", "right": sub-devices for each direction.
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index 84ac82ed3..77f626bcb 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -30,6 +30,11 @@ public:
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
w * other.w - Dot(xyz, other.xyz)};
}
+
+ Quaternion<T> Normalized() const {
+ T length = std::sqrt(xyz.Length2() + w * w);
+ return {xyz / length, w / length};
+ }
};
template <typename T>
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index c7a461a1e..6e2a5ad60 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -31,7 +31,6 @@
#pragma once
#include <cmath>
-#include <type_traits>
namespace Math {
@@ -90,7 +89,7 @@ public:
x -= other.x;
y -= other.y;
}
- template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type>
+
Vec2<decltype(-T{})> operator-() const {
return MakeVec(-x, -y);
}
@@ -247,7 +246,7 @@ public:
y -= other.y;
z -= other.z;
}
- template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type>
+
Vec3<decltype(-T{})> operator-() const {
return MakeVec(-x, -y, -z);
}
@@ -462,7 +461,7 @@ public:
z -= other.z;
w -= other.w;
}
- template <typename Q = T, class = typename std::enable_if<std::is_signed<Q>::value>::type>
+
Vec4<decltype(-T{})> operator-() const {
return MakeVec(-x, -y, -z, -w);
}
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 6624f1711..3dbeb27cc 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -681,7 +681,7 @@ void GenerateConsoleUniqueId(u32& random_number, u64& console_id) {
CryptoPP::AutoSeededRandomPool rng;
random_number = rng.GenerateWord32(0, 0xFFFF);
u64_le local_friend_code_seed;
- rng.GenerateBlock(reinterpret_cast<byte*>(&local_friend_code_seed),
+ rng.GenerateBlock(reinterpret_cast<CryptoPP::byte*>(&local_friend_code_seed),
sizeof(local_friend_code_seed));
console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48);
}
diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp
index 56f934b3f..6f2bf2061 100644
--- a/src/core/hle/service/dlp/dlp_clnt.cpp
+++ b/src/core/hle/service/dlp/dlp_clnt.cpp
@@ -8,7 +8,26 @@ namespace Service {
namespace DLP {
const Interface::FunctionInfo FunctionTable[] = {
- {0x000100C3, nullptr, "Initialize"}, {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x000100C3, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Finalize"},
+ {0x00030000, nullptr, "GetEventDesc"},
+ {0x00040000, nullptr, "GetChannel"},
+ {0x00050180, nullptr, "StartScan"},
+ {0x00060000, nullptr, "StopScan"},
+ {0x00070080, nullptr, "GetServerInfo"},
+ {0x00080100, nullptr, "GetTitleInfo"},
+ {0x00090040, nullptr, "GetTitleInfoInOrder"},
+ {0x000A0080, nullptr, "DeleteScanInfo"},
+ {0x000B0100, nullptr, "PrepareForSystemDownload"},
+ {0x000C0000, nullptr, "StartSystemDownload"},
+ {0x000D0100, nullptr, "StartTitleDownload"},
+ {0x000E0000, nullptr, "GetMyStatus"},
+ {0x000F0040, nullptr, "GetConnectingNodes"},
+ {0x00100040, nullptr, "GetNodeInfo"},
+ {0x00110000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x00120000, nullptr, "StopSession"},
+ {0x00130100, nullptr, "GetCupVersion"},
+ {0x00140100, nullptr, "GetDupAvailability"},
};
DLP_CLNT_Interface::DLP_CLNT_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp
index 29b9d52e0..fe6be7d32 100644
--- a/src/core/hle/service/dlp/dlp_fkcl.cpp
+++ b/src/core/hle/service/dlp/dlp_fkcl.cpp
@@ -8,7 +8,23 @@ namespace Service {
namespace DLP {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010083, nullptr, "Initialize"}, {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x00010083, nullptr, "Initialize"},
+ {0x00020000, nullptr, "Finalize"},
+ {0x00030000, nullptr, "GetEventDesc"},
+ {0x00040000, nullptr, "GetChannels"},
+ {0x00050180, nullptr, "StartScan"},
+ {0x00060000, nullptr, "StopScan"},
+ {0x00070080, nullptr, "GetServerInfo"},
+ {0x00080100, nullptr, "GetTitleInfo"},
+ {0x00090040, nullptr, "GetTitleInfoInOrder"},
+ {0x000A0080, nullptr, "DeleteScanInfo"},
+ {0x000B0100, nullptr, "StartFakeSession"},
+ {0x000C0000, nullptr, "GetMyStatus"},
+ {0x000D0040, nullptr, "GetConnectingNodes"},
+ {0x000E0040, nullptr, "GetNodeInfo"},
+ {0x000F0000, nullptr, "GetWirelessRebootPassphrase"},
+ {0x00100000, nullptr, "StopSession"},
+ {0x00110203, nullptr, "Initialize2"},
};
DLP_FKCL_Interface::DLP_FKCL_Interface() {
diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp
index 32cfa2c44..1bcea43d3 100644
--- a/src/core/hle/service/dlp/dlp_srvr.cpp
+++ b/src/core/hle/service/dlp/dlp_srvr.cpp
@@ -11,7 +11,7 @@
namespace Service {
namespace DLP {
-static void unk_0x000E0040(Interface* self) {
+static void IsChild(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = RESULT_SUCCESS.raw;
@@ -24,14 +24,19 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00010183, nullptr, "Initialize"},
{0x00020000, nullptr, "Finalize"},
{0x00030000, nullptr, "GetServerState"},
+ {0x00040000, nullptr, "GetEventDescription"},
{0x00050080, nullptr, "StartAccepting"},
+ {0x00060000, nullptr, "EndAccepting"},
{0x00070000, nullptr, "StartDistribution"},
{0x000800C0, nullptr, "SendWirelessRebootPassphrase"},
{0x00090040, nullptr, "AcceptClient"},
+ {0x000A0040, nullptr, "DisconnectClient"},
{0x000B0042, nullptr, "GetConnectingClients"},
{0x000C0040, nullptr, "GetClientInfo"},
{0x000D0040, nullptr, "GetClientState"},
- {0x000E0040, unk_0x000E0040, "unk_0x000E0040"},
+ {0x000E0040, IsChild, "IsChild"},
+ {0x000F0303, nullptr, "InitializeWithName"},
+ {0x00100000, nullptr, "GetDupNoticeNeed"},
};
DLP_SRVR_Interface::DLP_SRVR_Interface() {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 1ef972e70..ef25926b5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -24,7 +24,7 @@ namespace HID {
*/
struct PadState {
union {
- u32 hex;
+ u32 hex{};
BitField<0, 1, u32> a;
BitField<1, 1, u32> b;
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
index 837413f93..0912d5756 100644
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ b/src/core/hle/service/ir/ir_rst.cpp
@@ -18,7 +18,7 @@ namespace Service {
namespace IR {
union PadState {
- u32_le hex;
+ u32_le hex{};
BitField<14, 1, u32_le> zl;
BitField<15, 1, u32_le> zr;
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index 756ee58b7..d404afa89 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -159,7 +159,7 @@ public:
* - "axis"(optional): the index of the axis to bind
* - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
* "down", "left" or "right"
- * - "threshould"(only used for axis): a float value in (-1.0, 1.0) which the button is
+ * - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
* triggered if the axis value crosses
* - "direction"(only used for axis): "+" means the button is triggered when the axis value
* is greater than the threshold; "-" means the button is triggered when the axis value
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 0961a3251..cffa4c952 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -15,6 +15,7 @@ set(SRCS
shader/shader_interpreter.cpp
swrasterizer/clipper.cpp
swrasterizer/framebuffer.cpp
+ swrasterizer/lighting.cpp
swrasterizer/proctex.cpp
swrasterizer/rasterizer.cpp
swrasterizer/swrasterizer.cpp
@@ -55,6 +56,7 @@ set(HEADERS
shader/shader_interpreter.h
swrasterizer/clipper.h
swrasterizer/framebuffer.h
+ swrasterizer/lighting.h
swrasterizer/proctex.h
swrasterizer/rasterizer.h
swrasterizer/swrasterizer.h
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 4633a1df1..f98ca3302 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -119,27 +119,6 @@ static void WriteUniformFloatReg(ShaderRegs& config, Shader::ShaderSetup& setup,
}
}
-static void WriteProgramCode(ShaderRegs& config, Shader::ShaderSetup& setup,
- unsigned max_program_code_length, u32 value) {
- if (config.program.offset >= max_program_code_length) {
- LOG_ERROR(HW_GPU, "Invalid %s program offset %d", GetShaderSetupTypeName(setup),
- (int)config.program.offset);
- } else {
- setup.program_code[config.program.offset] = value;
- config.program.offset++;
- }
-}
-
-static void WriteSwizzlePatterns(ShaderRegs& config, Shader::ShaderSetup& setup, u32 value) {
- if (config.swizzle_patterns.offset >= setup.swizzle_data.size()) {
- LOG_ERROR(HW_GPU, "Invalid %s swizzle pattern offset %d", GetShaderSetupTypeName(setup),
- (int)config.swizzle_patterns.offset);
- } else {
- setup.swizzle_data[config.swizzle_patterns.offset] = value;
- config.swizzle_patterns.offset++;
- }
-}
-
static void WritePicaReg(u32 id, u32 value, u32 mask) {
auto& regs = g_state.regs;
@@ -458,7 +437,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[5], 0x2a1):
case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[6], 0x2a2):
case PICA_REG_INDEX_WORKAROUND(gs.program.set_word[7], 0x2a3): {
- WriteProgramCode(g_state.regs.gs, g_state.gs, 4096, value);
+ u32& offset = g_state.regs.gs.program.offset;
+ if (offset >= 4096) {
+ LOG_ERROR(HW_GPU, "Invalid GS program offset %u", offset);
+ } else {
+ g_state.gs.program_code[offset] = value;
+ offset++;
+ }
break;
}
@@ -470,11 +455,18 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[5], 0x2ab):
case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[6], 0x2ac):
case PICA_REG_INDEX_WORKAROUND(gs.swizzle_patterns.set_word[7], 0x2ad): {
- WriteSwizzlePatterns(g_state.regs.gs, g_state.gs, value);
+ u32& offset = g_state.regs.gs.swizzle_patterns.offset;
+ if (offset >= g_state.gs.swizzle_data.size()) {
+ LOG_ERROR(HW_GPU, "Invalid GS swizzle pattern offset %u", offset);
+ } else {
+ g_state.gs.swizzle_data[offset] = value;
+ offset++;
+ }
break;
}
case PICA_REG_INDEX(vs.bool_uniforms):
+ // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?
WriteUniformBoolReg(g_state.vs, g_state.regs.vs.bool_uniforms.Value());
break;
@@ -482,6 +474,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[1], 0x2b2):
case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[2], 0x2b3):
case PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[3], 0x2b4): {
+ // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?
unsigned index = (id - PICA_REG_INDEX_WORKAROUND(vs.int_uniforms[0], 0x2b1));
auto values = regs.vs.int_uniforms[index];
WriteUniformIntReg(g_state.vs, index,
@@ -497,6 +490,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[5], 0x2c6):
case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[6], 0x2c7):
case PICA_REG_INDEX_WORKAROUND(vs.uniform_setup.set_value[7], 0x2c8): {
+ // TODO (wwylele): does regs.pipeline.gs_unit_exclusive_configuration affect this?
WriteUniformFloatReg(g_state.regs.vs, g_state.vs, vs_float_regs_counter,
vs_uniform_write_buffer, value);
break;
@@ -510,7 +504,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[5], 0x2d1):
case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[6], 0x2d2):
case PICA_REG_INDEX_WORKAROUND(vs.program.set_word[7], 0x2d3): {
- WriteProgramCode(g_state.regs.vs, g_state.vs, 512, value);
+ u32& offset = g_state.regs.vs.program.offset;
+ if (offset >= 512) {
+ LOG_ERROR(HW_GPU, "Invalid VS program offset %u", offset);
+ } else {
+ g_state.vs.program_code[offset] = value;
+ if (!g_state.regs.pipeline.gs_unit_exclusive_configuration) {
+ g_state.gs.program_code[offset] = value;
+ }
+ offset++;
+ }
break;
}
@@ -522,7 +525,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[5], 0x2db):
case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[6], 0x2dc):
case PICA_REG_INDEX_WORKAROUND(vs.swizzle_patterns.set_word[7], 0x2dd): {
- WriteSwizzlePatterns(g_state.regs.vs, g_state.vs, value);
+ u32& offset = g_state.regs.vs.swizzle_patterns.offset;
+ if (offset >= g_state.vs.swizzle_data.size()) {
+ LOG_ERROR(HW_GPU, "Invalid VS swizzle pattern offset %u", offset);
+ } else {
+ g_state.vs.swizzle_data[offset] = value;
+ if (!g_state.regs.pipeline.gs_unit_exclusive_configuration) {
+ g_state.gs.swizzle_data[offset] = value;
+ }
+ offset++;
+ }
break;
}
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index 2d23d34e6..864a2c9e6 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -79,7 +79,7 @@ struct State {
std::array<ColorDifferenceEntry, 256> color_diff_table;
} proctex;
- struct {
+ struct Lighting {
union LutEntry {
// Used for raw access
u32 raw;
diff --git a/src/video_core/regs_pipeline.h b/src/video_core/regs_pipeline.h
index 31c747d77..8b6369297 100644
--- a/src/video_core/regs_pipeline.h
+++ b/src/video_core/regs_pipeline.h
@@ -202,7 +202,14 @@ struct PipelineRegs {
/// Number of input attributes to the vertex shader minus 1
BitField<0, 4, u32> max_input_attrib_index;
- INSERT_PADDING_WORDS(2);
+ INSERT_PADDING_WORDS(1);
+
+ // The shader unit 3, which can be used for both vertex and geometry shader, gets its
+ // configuration depending on this register. If this is not set, unit 3 will share some
+ // configuration with other units. It is known that program code and swizzle pattern uploaded
+ // via regs.vs will be also uploaded to unit 3 if this is not set. Although very likely, it is
+ // still unclear whether uniforms and other configuration can be also shared.
+ BitField<0, 1, u32> gs_unit_exclusive_configuration;
enum class GPUMode : u32 {
Drawing = 0,
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index bb192affd..ae67aab05 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -525,11 +525,12 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
"float geo_factor = 1.0;\n";
// Compute fragment normals and tangents
- const std::string pertubation =
- "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0";
+ auto Perturbation = [&]() {
+ return "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0";
+ };
if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
// Bump mapping is enabled using a normal map
- out += "vec3 surface_normal = " + pertubation + ";\n";
+ out += "vec3 surface_normal = " + Perturbation() + ";\n";
// Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher
// precision result
@@ -543,7 +544,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n";
} else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {
// Bump mapping is enabled using a tangent map
- out += "vec3 surface_tangent = " + pertubation + ";\n";
+ out += "vec3 surface_tangent = " + Perturbation() + ";\n";
// Mathematically, recomputing Z-component of the tangent vector won't affect the relevant
// computation below, which is also confirmed on 3DS. So we don't bother recomputing here
// even if 'renorm' is enabled.
diff --git a/src/video_core/swrasterizer/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp
index 6fb923756..7537689b7 100644
--- a/src/video_core/swrasterizer/clipper.cpp
+++ b/src/video_core/swrasterizer/clipper.cpp
@@ -95,6 +95,17 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
static const size_t MAX_VERTICES = 9;
static_vector<Vertex, MAX_VERTICES> buffer_a = {v0, v1, v2};
static_vector<Vertex, MAX_VERTICES> buffer_b;
+
+ auto FlipQuaternionIfOpposite = [](auto& a, const auto& b) {
+ if (Math::Dot(a, b) < float24::Zero())
+ a = -a;
+ };
+
+ // Flip the quaternions if they are opposite to prevent interpolating them over the wrong
+ // direction.
+ FlipQuaternionIfOpposite(buffer_a[1].quat, buffer_a[0].quat);
+ FlipQuaternionIfOpposite(buffer_a[2].quat, buffer_a[0].quat);
+
auto* output_list = &buffer_a;
auto* input_list = &buffer_b;
diff --git a/src/video_core/swrasterizer/lighting.cpp b/src/video_core/swrasterizer/lighting.cpp
new file mode 100644
index 000000000..d61e6d572
--- /dev/null
+++ b/src/video_core/swrasterizer/lighting.cpp
@@ -0,0 +1,250 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/math_util.h"
+#include "video_core/swrasterizer/lighting.h"
+
+namespace Pica {
+
+static float LookupLightingLut(const Pica::State::Lighting& lighting, size_t lut_index, u8 index,
+ float delta) {
+ ASSERT_MSG(lut_index < lighting.luts.size(), "Out of range lut");
+ ASSERT_MSG(index < lighting.luts[lut_index].size(), "Out of range index");
+
+ const auto& lut = lighting.luts[lut_index][index];
+
+ float lut_value = lut.ToFloat();
+ float lut_diff = lut.DiffToFloat();
+
+ return lut_value + lut_diff * delta;
+}
+
+std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
+ const Pica::LightingRegs& lighting, const Pica::State::Lighting& lighting_state,
+ const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view) {
+
+ // TODO(Subv): Bump mapping
+ Math::Vec3<float> surface_normal = {0.0f, 0.0f, 1.0f};
+
+ if (lighting.config0.bump_mode != LightingRegs::LightingBumpMode::None) {
+ LOG_CRITICAL(HW_GPU, "unimplemented bump mapping");
+ UNIMPLEMENTED();
+ }
+
+ // Use the normalized the quaternion when performing the rotation
+ auto normal = Math::QuaternionRotate(normquat, surface_normal);
+
+ Math::Vec4<float> diffuse_sum = {0.0f, 0.0f, 0.0f, 1.0f};
+ Math::Vec4<float> specular_sum = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ for (unsigned light_index = 0; light_index <= lighting.max_light_index; ++light_index) {
+ unsigned num = lighting.light_enable.GetNum(light_index);
+ const auto& light_config = lighting.light[num];
+
+ Math::Vec3<float> refl_value = {};
+ Math::Vec3<float> position = {float16::FromRaw(light_config.x).ToFloat32(),
+ float16::FromRaw(light_config.y).ToFloat32(),
+ float16::FromRaw(light_config.z).ToFloat32()};
+ Math::Vec3<float> light_vector;
+
+ if (light_config.config.directional)
+ light_vector = position;
+ else
+ light_vector = position + view;
+
+ light_vector.Normalize();
+
+ float dist_atten = 1.0f;
+ if (!lighting.IsDistAttenDisabled(num)) {
+ auto distance = (-view - position).Length();
+ float scale = Pica::float20::FromRaw(light_config.dist_atten_scale).ToFloat32();
+ float bias = Pica::float20::FromRaw(light_config.dist_atten_bias).ToFloat32();
+ size_t lut =
+ static_cast<size_t>(LightingRegs::LightingSampler::DistanceAttenuation) + num;
+
+ float sample_loc = MathUtil::Clamp(scale * distance + bias, 0.0f, 1.0f);
+
+ u8 lutindex =
+ static_cast<u8>(MathUtil::Clamp(std::floor(sample_loc * 256.0f), 0.0f, 255.0f));
+ float delta = sample_loc * 256 - lutindex;
+ dist_atten = LookupLightingLut(lighting_state, lut, lutindex, delta);
+ }
+
+ auto GetLutValue = [&](LightingRegs::LightingLutInput input, bool abs,
+ LightingRegs::LightingScale scale_enum,
+ LightingRegs::LightingSampler sampler) {
+ Math::Vec3<float> norm_view = view.Normalized();
+ Math::Vec3<float> half_angle = (norm_view + light_vector).Normalized();
+ float result = 0.0f;
+
+ switch (input) {
+ case LightingRegs::LightingLutInput::NH:
+ result = Math::Dot(normal, half_angle);
+ break;
+
+ case LightingRegs::LightingLutInput::VH:
+ result = Math::Dot(norm_view, half_angle);
+ break;
+
+ case LightingRegs::LightingLutInput::NV:
+ result = Math::Dot(normal, norm_view);
+ break;
+
+ case LightingRegs::LightingLutInput::LN:
+ result = Math::Dot(light_vector, normal);
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %u\n", static_cast<u32>(input));
+ UNIMPLEMENTED();
+ result = 0.0f;
+ }
+
+ u8 index;
+ float delta;
+
+ if (abs) {
+ if (light_config.config.two_sided_diffuse)
+ result = std::abs(result);
+ else
+ result = std::max(result, 0.0f);
+
+ float flr = std::floor(result * 256.0f);
+ index = static_cast<u8>(MathUtil::Clamp(flr, 0.0f, 255.0f));
+ delta = result * 256 - index;
+ } else {
+ float flr = std::floor(result * 128.0f);
+ s8 signed_index = static_cast<s8>(MathUtil::Clamp(flr, -128.0f, 127.0f));
+ delta = result * 128.0f - signed_index;
+ index = static_cast<u8>(signed_index);
+ }
+
+ float scale = lighting.lut_scale.GetScale(scale_enum);
+ return scale *
+ LookupLightingLut(lighting_state, static_cast<size_t>(sampler), index, delta);
+ };
+
+ // Specular 0 component
+ float d0_lut_value = 1.0f;
+ if (lighting.config1.disable_lut_d0 == 0 &&
+ LightingRegs::IsLightingSamplerSupported(
+ lighting.config0.config, LightingRegs::LightingSampler::Distribution0)) {
+ d0_lut_value =
+ GetLutValue(lighting.lut_input.d0, lighting.abs_lut_input.disable_d0 == 0,
+ lighting.lut_scale.d0, LightingRegs::LightingSampler::Distribution0);
+ }
+
+ Math::Vec3<float> specular_0 = d0_lut_value * light_config.specular_0.ToVec3f();
+
+ // If enabled, lookup ReflectRed value, otherwise, 1.0 is used
+ if (lighting.config1.disable_lut_rr == 0 &&
+ LightingRegs::IsLightingSamplerSupported(lighting.config0.config,
+ LightingRegs::LightingSampler::ReflectRed)) {
+ refl_value.x =
+ GetLutValue(lighting.lut_input.rr, lighting.abs_lut_input.disable_rr == 0,
+ lighting.lut_scale.rr, LightingRegs::LightingSampler::ReflectRed);
+ } else {
+ refl_value.x = 1.0f;
+ }
+
+ // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used
+ if (lighting.config1.disable_lut_rg == 0 &&
+ LightingRegs::IsLightingSamplerSupported(lighting.config0.config,
+ LightingRegs::LightingSampler::ReflectGreen)) {
+ refl_value.y =
+ GetLutValue(lighting.lut_input.rg, lighting.abs_lut_input.disable_rg == 0,
+ lighting.lut_scale.rg, LightingRegs::LightingSampler::ReflectGreen);
+ } else {
+ refl_value.y = refl_value.x;
+ }
+
+ // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used
+ if (lighting.config1.disable_lut_rb == 0 &&
+ LightingRegs::IsLightingSamplerSupported(lighting.config0.config,
+ LightingRegs::LightingSampler::ReflectBlue)) {
+ refl_value.z =
+ GetLutValue(lighting.lut_input.rb, lighting.abs_lut_input.disable_rb == 0,
+ lighting.lut_scale.rb, LightingRegs::LightingSampler::ReflectBlue);
+ } else {
+ refl_value.z = refl_value.x;
+ }
+
+ // Specular 1 component
+ float d1_lut_value = 1.0f;
+ if (lighting.config1.disable_lut_d1 == 0 &&
+ LightingRegs::IsLightingSamplerSupported(
+ lighting.config0.config, LightingRegs::LightingSampler::Distribution1)) {
+ d1_lut_value =
+ GetLutValue(lighting.lut_input.d1, lighting.abs_lut_input.disable_d1 == 0,
+ lighting.lut_scale.d1, LightingRegs::LightingSampler::Distribution1);
+ }
+
+ Math::Vec3<float> specular_1 =
+ d1_lut_value * refl_value * light_config.specular_1.ToVec3f();
+
+ // Fresnel
+ if (lighting.config1.disable_lut_fr == 0 &&
+ LightingRegs::IsLightingSamplerSupported(lighting.config0.config,
+ LightingRegs::LightingSampler::Fresnel)) {
+
+ float lut_value =
+ GetLutValue(lighting.lut_input.fr, lighting.abs_lut_input.disable_fr == 0,
+ lighting.lut_scale.fr, LightingRegs::LightingSampler::Fresnel);
+
+ // Enabled for diffuse lighting alpha component
+ if (lighting.config0.fresnel_selector ==
+ LightingRegs::LightingFresnelSelector::PrimaryAlpha ||
+ lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) {
+ diffuse_sum.a() *= lut_value;
+ }
+
+ // Enabled for the specular lighting alpha component
+ if (lighting.config0.fresnel_selector ==
+ LightingRegs::LightingFresnelSelector::SecondaryAlpha ||
+ lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) {
+ specular_sum.a() *= lut_value;
+ }
+ }
+
+ auto dot_product = Math::Dot(light_vector, normal);
+
+ // Calculate clamp highlights before applying the two-sided diffuse configuration to the dot
+ // product.
+ float clamp_highlights = 1.0f;
+ if (lighting.config0.clamp_highlights) {
+ if (dot_product <= 0.0f)
+ clamp_highlights = 0.0f;
+ else
+ clamp_highlights = 1.0f;
+ }
+
+ if (light_config.config.two_sided_diffuse)
+ dot_product = std::abs(dot_product);
+ else
+ dot_product = std::max(dot_product, 0.0f);
+
+ auto diffuse =
+ light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f();
+ diffuse_sum += Math::MakeVec(diffuse * dist_atten, 0.0f);
+
+ specular_sum +=
+ Math::MakeVec((specular_0 + specular_1) * clamp_highlights * dist_atten, 0.0f);
+ }
+
+ diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f);
+
+ auto diffuse = Math::MakeVec<float>(MathUtil::Clamp(diffuse_sum.x, 0.0f, 1.0f) * 255,
+ MathUtil::Clamp(diffuse_sum.y, 0.0f, 1.0f) * 255,
+ MathUtil::Clamp(diffuse_sum.z, 0.0f, 1.0f) * 255,
+ MathUtil::Clamp(diffuse_sum.w, 0.0f, 1.0f) * 255)
+ .Cast<u8>();
+ auto specular = Math::MakeVec<float>(MathUtil::Clamp(specular_sum.x, 0.0f, 1.0f) * 255,
+ MathUtil::Clamp(specular_sum.y, 0.0f, 1.0f) * 255,
+ MathUtil::Clamp(specular_sum.z, 0.0f, 1.0f) * 255,
+ MathUtil::Clamp(specular_sum.w, 0.0f, 1.0f) * 255)
+ .Cast<u8>();
+ return std::make_tuple(diffuse, specular);
+}
+
+} // namespace Pica
diff --git a/src/video_core/swrasterizer/lighting.h b/src/video_core/swrasterizer/lighting.h
new file mode 100644
index 000000000..438dca926
--- /dev/null
+++ b/src/video_core/swrasterizer/lighting.h
@@ -0,0 +1,18 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <tuple>
+#include "common/quaternion.h"
+#include "common/vector_math.h"
+#include "video_core/pica_state.h"
+
+namespace Pica {
+
+std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors(
+ const Pica::LightingRegs& lighting, const Pica::State::Lighting& lighting_state,
+ const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view);
+
+} // namespace Pica
diff --git a/src/video_core/swrasterizer/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp
index 512e81c08..fdc1df199 100644
--- a/src/video_core/swrasterizer/rasterizer.cpp
+++ b/src/video_core/swrasterizer/rasterizer.cpp
@@ -13,6 +13,7 @@
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/microprofile.h"
+#include "common/quaternion.h"
#include "common/vector_math.h"
#include "core/hw/gpu.h"
#include "core/memory.h"
@@ -24,6 +25,7 @@
#include "video_core/regs_texturing.h"
#include "video_core/shader/shader.h"
#include "video_core/swrasterizer/framebuffer.h"
+#include "video_core/swrasterizer/lighting.h"
#include "video_core/swrasterizer/proctex.h"
#include "video_core/swrasterizer/rasterizer.h"
#include "video_core/swrasterizer/texturing.h"
@@ -419,6 +421,26 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
regs.texturing.tev_combiner_buffer_color.a,
};
+ Math::Vec4<u8> primary_fragment_color = {0, 0, 0, 0};
+ Math::Vec4<u8> secondary_fragment_color = {0, 0, 0, 0};
+
+ if (!g_state.regs.lighting.disable) {
+ Math::Quaternion<float> normquat = Math::Quaternion<float>{
+ {GetInterpolatedAttribute(v0.quat.x, v1.quat.x, v2.quat.x).ToFloat32(),
+ GetInterpolatedAttribute(v0.quat.y, v1.quat.y, v2.quat.y).ToFloat32(),
+ GetInterpolatedAttribute(v0.quat.z, v1.quat.z, v2.quat.z).ToFloat32()},
+ GetInterpolatedAttribute(v0.quat.w, v1.quat.w, v2.quat.w).ToFloat32(),
+ }.Normalized();
+
+ Math::Vec3<float> view{
+ GetInterpolatedAttribute(v0.view.x, v1.view.x, v2.view.x).ToFloat32(),
+ GetInterpolatedAttribute(v0.view.y, v1.view.y, v2.view.y).ToFloat32(),
+ GetInterpolatedAttribute(v0.view.z, v1.view.z, v2.view.z).ToFloat32(),
+ };
+ std::tie(primary_fragment_color, secondary_fragment_color) =
+ ComputeFragmentsColors(g_state.regs.lighting, g_state.lighting, normquat, view);
+ }
+
for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size();
++tev_stage_index) {
const auto& tev_stage = tev_stages[tev_stage_index];
@@ -427,14 +449,13 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve
auto GetSource = [&](Source source) -> Math::Vec4<u8> {
switch (source) {
case Source::PrimaryColor:
+ return primary_color;
- // HACK: Until we implement fragment lighting, use primary_color
case Source::PrimaryFragmentColor:
- return primary_color;
+ return primary_fragment_color;
- // HACK: Until we implement fragment lighting, use zero
case Source::SecondaryFragmentColor:
- return {0, 0, 0, 0};
+ return secondary_fragment_color;
case Source::Texture0:
return texture_color[0];