Compare commits
29 Commits
v0.0.0-tec
...
newtps
Author | SHA1 | Date | |
---|---|---|---|
a984333c3a | |||
4a1f1b7545 | |||
af7b39d8e9 | |||
a0acd16008 | |||
330ed1ab9b | |||
7e852ff05f | |||
6e1b0e3f69 | |||
411780b120 | |||
5359b93738 | |||
f9aae94bbc | |||
5c57a57e9a | |||
468b6dc327 | |||
69942ad17b | |||
3c74a808f3 | |||
8327fcfd19 | |||
32851b8fb0
|
|||
c49fdfa5ff
|
|||
9d7f69892b
|
|||
73d24d36f4 | |||
bdb3bff570 | |||
6997bb1104 | |||
eac0a34516 | |||
127d1c3d87 | |||
26a35f306c
|
|||
52f3f653d8 | |||
fc85eb5658
|
|||
260562310a
|
|||
85edc07c75 | |||
fcf225f9c7 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -30,6 +30,8 @@ build_packages/*
|
|||||||
!build_packages/NSIS
|
!build_packages/NSIS
|
||||||
build_packages/NSIS/*
|
build_packages/NSIS/*
|
||||||
!build_packages/NSIS/ProgressiaInstaller.nsi
|
!build_packages/NSIS/ProgressiaInstaller.nsi
|
||||||
|
!build_packages/NSIS/logo.ico
|
||||||
|
!build_packages/NSIS/left_side.bmp
|
||||||
|
|
||||||
# ... and except build_packages/DEB/template
|
# ... and except build_packages/DEB/template
|
||||||
!build_packages/DEB
|
!build_packages/DEB
|
||||||
|
@ -16,7 +16,7 @@ temperature mechanics and a parallelism-capable server.
|
|||||||
- GNU/Linux (x64, arm32 or arm64), Windows XP or later (x64 or x86) or MacOS (x64)
|
- GNU/Linux (x64, arm32 or arm64), Windows XP or later (x64 or x86) or MacOS (x64)
|
||||||
- Java 8 or later
|
- Java 8 or later
|
||||||
- OpenGL 2.1 or later
|
- OpenGL 2.1 or later
|
||||||
- Probably at least 4 GiB RAM
|
- Probably about 0.5 GiB RAM
|
||||||
- Less than 1 GiB of storage space
|
- Less than 1 GiB of storage space
|
||||||
|
|
||||||
See [Build Guide](docs/building/BuildGuide.md) for compilation requirements.
|
See [Build Guide](docs/building/BuildGuide.md) for compilation requirements.
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
#
|
#
|
||||||
# Progressia
|
# Progressia
|
||||||
# Copyright (C) 2020-2021 Wind Corporation and contributors
|
# Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
@ -24,24 +24,24 @@ buildDebianPackage() {
|
|||||||
|
|
||||||
# Commands that must be available to execute this action
|
# Commands that must be available to execute this action
|
||||||
requiredCommands='dpkg-deb fakeroot'
|
requiredCommands='dpkg-deb fakeroot'
|
||||||
|
|
||||||
# Package name. Sync with control file manually!
|
# Package name. Sync with control file manually!
|
||||||
name='progressia-techdemo'
|
name='progressia-techdemo'
|
||||||
# Version that the package will receive. Sync with control file manually!
|
# Version that the package will receive. Sync with control file manually!
|
||||||
version='1.0_all'
|
version='1.0_all'
|
||||||
|
|
||||||
# This directory will be copied into $tmpDir
|
# This directory will be copied into $tmpDir
|
||||||
templateDirectory="build_packages/DEB/template"
|
templateDirectory="build_packages/DEB/template"
|
||||||
|
|
||||||
# Files that must be present
|
# Files that must be present
|
||||||
requiredFiles="$templateDirectory/DEBIAN/control"
|
requiredFiles="$templateDirectory/DEBIAN/control"
|
||||||
|
|
||||||
nameAndVersion="$name-$version"
|
nameAndVersion="$name-$version"
|
||||||
tmpDir="build_packages/DEB/$nameAndVersion"
|
tmpDir="build_packages/DEB/$nameAndVersion"
|
||||||
outputFile="build_packages/DEB/$nameAndVersion.deb"
|
outputFile="build_packages/DEB/$nameAndVersion.deb"
|
||||||
|
|
||||||
echo "Checking environment to build Debian package"
|
echo "Checking environment to build Debian package"
|
||||||
|
|
||||||
for item in $requiredCommands; do
|
for item in $requiredCommands; do
|
||||||
if command -v "$item" &> /dev/null; then
|
if command -v "$item" &> /dev/null; then
|
||||||
echo "- $item found"
|
echo "- $item found"
|
||||||
@ -50,7 +50,7 @@ buildDebianPackage() {
|
|||||||
exit 100
|
exit 100
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
for file in $requiredFiles; do
|
for file in $requiredFiles; do
|
||||||
if ! [ -r "$file" ]; then
|
if ! [ -r "$file" ]; then
|
||||||
echoerr "$file is missing or not readable, cannot package"
|
echoerr "$file is missing or not readable, cannot package"
|
||||||
@ -59,13 +59,13 @@ buildDebianPackage() {
|
|||||||
echo "- $file is present and readable"
|
echo "- $file is present and readable"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Environment OK; packaging Debian package"
|
echo "Environment OK; packaging Debian package"
|
||||||
exitCode=0
|
exitCode=0
|
||||||
|
|
||||||
{
|
{
|
||||||
shareDir="$tmpDir/usr/share/progressia"
|
shareDir="$tmpDir/usr/share/progressia"
|
||||||
|
|
||||||
mkdir -p "$tmpDir" &&
|
mkdir -p "$tmpDir" &&
|
||||||
mkdir -p "$shareDir" &&
|
mkdir -p "$shareDir" &&
|
||||||
cp -r "$templateDirectory"/* "$tmpDir" &&
|
cp -r "$templateDirectory"/* "$tmpDir" &&
|
||||||
@ -79,7 +79,7 @@ buildDebianPackage() {
|
|||||||
echoerr "Could not create Debian package"
|
echoerr "Could not create Debian package"
|
||||||
exitCode=1
|
exitCode=1
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if [ -d "$tmpDir" ]; then
|
if [ -d "$tmpDir" ]; then
|
||||||
rm -r "$tmpDir"
|
rm -r "$tmpDir"
|
||||||
@ -89,7 +89,7 @@ buildDebianPackage() {
|
|||||||
echoerr "Could not clean up after packaging Debian package"
|
echoerr "Could not clean up after packaging Debian package"
|
||||||
exitCode=2
|
exitCode=2
|
||||||
}
|
}
|
||||||
|
|
||||||
exit "$exitCode"
|
exit "$exitCode"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,15 +97,15 @@ buildWindowsInstaller() {
|
|||||||
|
|
||||||
# Commands that must be available to execute this action
|
# Commands that must be available to execute this action
|
||||||
requiredCommands='makensis'
|
requiredCommands='makensis'
|
||||||
|
|
||||||
# NSIS configuration file that must be present
|
# NSIS configuration file that must be present
|
||||||
configurationFile='build_packages/NSIS/ProgressiaInstaller.nsi'
|
configurationFile='build_packages/NSIS/ProgressiaInstaller.nsi'
|
||||||
|
|
||||||
# File that will be output
|
# File that will be output
|
||||||
outputFile='build_packages/NSIS/ProgressiaInstaller.exe'
|
outputFile='build_packages/NSIS/ProgressiaInstaller.exe'
|
||||||
|
|
||||||
echo "Checking environment to build Windows installer"
|
echo "Checking environment to build Windows installer"
|
||||||
|
|
||||||
for item in $requiredCommands; do
|
for item in $requiredCommands; do
|
||||||
if command -v "$item" &> /dev/null; then
|
if command -v "$item" &> /dev/null; then
|
||||||
echo "- $item found"
|
echo "- $item found"
|
||||||
@ -114,20 +114,21 @@ buildWindowsInstaller() {
|
|||||||
exit 100
|
exit 100
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if ! [ -r "$configurationFile" ]; then
|
if ! [ -r "$configurationFile" ]; then
|
||||||
echoerr "$configurationFile is missing or not readable, cannot build"
|
echoerr "$configurationFile is missing or not readable, cannot build"
|
||||||
exit 101
|
exit 101
|
||||||
else
|
else
|
||||||
echo "- $configurationFile is present and readable"
|
echo "- $configurationFile is present and readable"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Environment OK; building Windows installer"
|
echo "Environment OK; building Windows installer"
|
||||||
exitCode=0
|
exitCode=0
|
||||||
|
|
||||||
{
|
{
|
||||||
cp -r 'build/libs/lib' 'build_packages/NSIS/lib' &&
|
cp -r 'build/libs/lib' 'build_packages/NSIS/lib' &&
|
||||||
cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' &&
|
cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' &&
|
||||||
|
cp 'LICENSE' 'build_packages/NSIS/LICENSE.txt' &&
|
||||||
echo "------ NSIS ------" &&
|
echo "------ NSIS ------" &&
|
||||||
makensis "$configurationFile" &&
|
makensis "$configurationFile" &&
|
||||||
echo "---- NSIS END ----" &&
|
echo "---- NSIS END ----" &&
|
||||||
@ -136,7 +137,7 @@ buildWindowsInstaller() {
|
|||||||
echoerr "Could not build Windows installer"
|
echoerr "Could not build Windows installer"
|
||||||
exitCode=1
|
exitCode=1
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if [ -d 'build_packages/NSIS/lib' ]; then
|
if [ -d 'build_packages/NSIS/lib' ]; then
|
||||||
rm -r 'build_packages/NSIS/lib'
|
rm -r 'build_packages/NSIS/lib'
|
||||||
@ -144,12 +145,15 @@ buildWindowsInstaller() {
|
|||||||
if [ -e 'build_packages/NSIS/Progressia.jar' ]; then
|
if [ -e 'build_packages/NSIS/Progressia.jar' ]; then
|
||||||
rm 'build_packages/NSIS/Progressia.jar'
|
rm 'build_packages/NSIS/Progressia.jar'
|
||||||
fi
|
fi
|
||||||
|
if [ -e 'build_packages/NSIS/LICENSE.txt' ]; then
|
||||||
|
rm 'build_packages/NSIS/LICENSE.txt'
|
||||||
|
fi
|
||||||
echo "Cleaned up"
|
echo "Cleaned up"
|
||||||
} || {
|
} || {
|
||||||
echoerr "Could not clean up after building Windows installer"
|
echoerr "Could not clean up after building Windows installer"
|
||||||
exitCode=2
|
exitCode=2
|
||||||
}
|
}
|
||||||
|
|
||||||
exit "$exitCode"
|
exit "$exitCode"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,16 +10,32 @@
|
|||||||
;--------------------------------
|
;--------------------------------
|
||||||
;General
|
;General
|
||||||
|
|
||||||
|
!define PROJECT_NAME "Progressia"
|
||||||
|
|
||||||
|
; MUI Settings / Icons
|
||||||
|
!define MUI_ICON "logo.ico"
|
||||||
|
;!define MUI_UNICON ;Uninstall icon
|
||||||
|
|
||||||
|
; MUI Settings / Header
|
||||||
|
; !define MUI_HEADERIMAGE
|
||||||
|
; !define MUI_HEADERIMAGE_RIGHT
|
||||||
|
; !define MUI_HEADERIMAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-r-nsis.bmp"
|
||||||
|
; !define MUI_HEADERIMAGE_UNBITMAP "${NSISDIR}\Contrib\Graphics\Header\orange-uninstall-r-nsis.bmp"
|
||||||
|
|
||||||
|
; MUI Settings / Wizard
|
||||||
|
!define MUI_WELCOMEFINISHPAGE_BITMAP "left_side.bmp"
|
||||||
|
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "left_side.bmp"
|
||||||
|
|
||||||
;Name and file
|
;Name and file
|
||||||
Name "Progressia"
|
Name "${PROJECT_NAME}"
|
||||||
OutFile "ProgressiaInstaller.exe"
|
OutFile "${PROJECT_NAME}Installer.exe"
|
||||||
Unicode True
|
Unicode True
|
||||||
|
|
||||||
;Default installation folder
|
;Default installation folder
|
||||||
InstallDir "$PROGRAMFILES\Progressia"
|
InstallDir "$PROGRAMFILES\${PROJECT_NAME}"
|
||||||
|
|
||||||
;Get installation folder from registry if available
|
;Get installation folder from registry if available
|
||||||
InstallDirRegKey HKLM "Software\Progressia" "Install_Dir"
|
InstallDirRegKey HKLM "Software\${PROJECT_NAME}" ""
|
||||||
|
|
||||||
;Request application privileges for Windows Vista
|
;Request application privileges for Windows Vista
|
||||||
RequestExecutionLevel admin
|
RequestExecutionLevel admin
|
||||||
@ -33,14 +49,18 @@
|
|||||||
;Pages
|
;Pages
|
||||||
|
|
||||||
!insertmacro MUI_PAGE_WELCOME
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
;!insertmacro MUI_PAGE_LICENSE "${NSISDIR}\Docs\Modern UI\License.txt"
|
!insertmacro MUI_PAGE_LICENSE "LICENSE.txt"
|
||||||
!insertmacro MUI_PAGE_COMPONENTS
|
!insertmacro MUI_PAGE_COMPONENTS
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
!insertmacro MUI_PAGE_INSTFILES
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
!define MUI_FINISHPAGE_RUN
|
||||||
|
!define MUI_FINISHPAGE_RUN_TEXT "Start ${PROJECT_NAME}"
|
||||||
|
!define MUI_FINISHPAGE_RUN_FUNCTION "LaunchLink"
|
||||||
!insertmacro MUI_PAGE_FINISH
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
!insertmacro MUI_UNPAGE_WELCOME
|
!insertmacro MUI_UNPAGE_WELCOME
|
||||||
!insertmacro MUI_UNPAGE_CONFIRM
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
|
!insertmacro MUI_UNPAGE_COMPONENTS
|
||||||
!insertmacro MUI_UNPAGE_INSTFILES
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
!insertmacro MUI_UNPAGE_FINISH
|
!insertmacro MUI_UNPAGE_FINISH
|
||||||
|
|
||||||
@ -52,35 +72,40 @@
|
|||||||
;--------------------------------
|
;--------------------------------
|
||||||
;Installer Sections
|
;Installer Sections
|
||||||
|
|
||||||
Section "Install Progressia" SecDummy
|
Section "Install ${PROJECT_NAME}" SEC0000
|
||||||
|
|
||||||
|
SectionIn RO ;Make it read-only
|
||||||
SetOutPath "$INSTDIR"
|
SetOutPath "$INSTDIR"
|
||||||
|
SetOverwrite on
|
||||||
|
|
||||||
;Files
|
;Files
|
||||||
File Progressia.jar
|
File Progressia.jar
|
||||||
|
File logo.ico
|
||||||
File /r lib
|
File /r lib
|
||||||
|
|
||||||
;Store installation folder
|
;Store installation folder
|
||||||
WriteRegStr HKLM SOFTWARE\Progressia "Install_Dir" "$INSTDIR"
|
WriteRegStr HKLM SOFTWARE\Progressia "Install_Dir" "$INSTDIR"
|
||||||
|
|
||||||
;Create uninstaller
|
;Create uninstaller
|
||||||
|
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Progressia" "DisplayName" "Progressia (remove only)"
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)"
|
||||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Progressia" "UninstallString" "$INSTDIR\Uninstall.exe"
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
|
||||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
;--------------------------------
|
Section "Create Desktop Shortcut" SEC0001
|
||||||
;Descriptions
|
SetOutPath "$APPDATA\${PROJECT_NAME}"
|
||||||
|
CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
;Language strings
|
Section "Start Menu Shortcuts" SEC0002
|
||||||
LangString DESC_SecDummy ${LANG_ENGLISH} "A test section."
|
|
||||||
|
|
||||||
;Assign language strings to sections
|
CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}"
|
||||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy)
|
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
|
||||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
;Uninstaller Section
|
;Uninstaller Section
|
||||||
@ -92,9 +117,45 @@ Section "Uninstall"
|
|||||||
Delete $INSTDIR\Uninstall.exe
|
Delete $INSTDIR\Uninstall.exe
|
||||||
Delete $INSTDIR\Progressia.jar
|
Delete $INSTDIR\Progressia.jar
|
||||||
Delete $INSTDIR\lib\*.*
|
Delete $INSTDIR\lib\*.*
|
||||||
|
Delete $INSTDIR\logo.ico
|
||||||
|
|
||||||
RMDir /r "$INSTDIR"
|
RMDir $INSTDIR\lib
|
||||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Progressia"
|
|
||||||
DeleteRegKey HKLM "Software\Progressia"
|
Delete $DESKTOP\${PROJECT_NAME}.lnk
|
||||||
|
|
||||||
|
Delete $SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk
|
||||||
|
Delete $SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk
|
||||||
|
|
||||||
|
RMDir $INSTDIR
|
||||||
|
|
||||||
|
RMDir /r $SMPROGRAMS\${PROJECT_NAME}
|
||||||
|
|
||||||
|
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}"
|
||||||
|
DeleteRegKey HKLM "Software\${PROJECT_NAME}"
|
||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
Section "un.Remove user data"
|
||||||
|
|
||||||
|
RMDir /r "$APPDATA\${PROJECT_NAME}"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
;--------------------------------
|
||||||
|
;Functions
|
||||||
|
|
||||||
|
Function LaunchLink
|
||||||
|
SetOutPath "$APPDATA\${PROJECT_NAME}"
|
||||||
|
ExecShell "" "$INSTDIR\${PROJECT_NAME}.jar"
|
||||||
|
FunctionEnd
|
||||||
|
|
||||||
|
;--------------------------------
|
||||||
|
;Descriptions
|
||||||
|
|
||||||
|
;Language strings
|
||||||
|
LangString DESC_SecDummy ${LANG_ENGLISH} "Install ${PROJECT_NAME}."
|
||||||
|
|
||||||
|
;Assign language strings to sections
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||||
|
!insertmacro MUI_DESCRIPTION_TEXT ${SEC0000} $(DESC_SecDummy)
|
||||||
|
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
BIN
build_packages/NSIS/left_side.bmp
Normal file
BIN
build_packages/NSIS/left_side.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
BIN
build_packages/NSIS/logo.ico
Normal file
BIN
build_packages/NSIS/logo.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 187 KiB |
@ -40,6 +40,7 @@ public class ProgressiaLauncher {
|
|||||||
CrashReports.registerProvider(new OpenALContextProvider());
|
CrashReports.registerProvider(new OpenALContextProvider());
|
||||||
CrashReports.registerProvider(new ArgsContextProvider());
|
CrashReports.registerProvider(new ArgsContextProvider());
|
||||||
CrashReports.registerProvider(new LanguageContextProvider());
|
CrashReports.registerProvider(new LanguageContextProvider());
|
||||||
|
CrashReports.registerProvider(new ScreenContextProvider());
|
||||||
// Analyzers
|
// Analyzers
|
||||||
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
CrashReports.registerAnalyzer(new OutOfMemoryAnalyzer());
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import ru.windcorp.progressia.common.resource.ResourceManager;
|
|||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
import ru.windcorp.progressia.test.TestContent;
|
import ru.windcorp.progressia.test.TestContent;
|
||||||
|
import ru.windcorp.progressia.test.TestMusicPlayer;
|
||||||
|
|
||||||
public class ClientProxy implements Proxy {
|
public class ClientProxy implements Proxy {
|
||||||
|
|
||||||
@ -59,6 +60,8 @@ public class ClientProxy implements Proxy {
|
|||||||
|
|
||||||
ServerState.startServer();
|
ServerState.startServer();
|
||||||
ClientState.connectToLocalServer();
|
ClientState.connectToLocalServer();
|
||||||
|
|
||||||
|
TestMusicPlayer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import ru.windcorp.progressia.client.audio.backend.AudioReader;
|
|||||||
import ru.windcorp.progressia.client.audio.backend.Listener;
|
import ru.windcorp.progressia.client.audio.backend.Listener;
|
||||||
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
||||||
|
import ru.windcorp.progressia.common.resource.Resource;
|
||||||
|
|
||||||
import static org.lwjgl.openal.AL11.*;
|
import static org.lwjgl.openal.AL11.*;
|
||||||
import static org.lwjgl.openal.ALC10.*;
|
import static org.lwjgl.openal.ALC10.*;
|
||||||
@ -40,7 +41,6 @@ public class AudioManager {
|
|||||||
|
|
||||||
private static List<Speaker> soundSpeakers = new ArrayList<>(SOUNDS_NUM);
|
private static List<Speaker> soundSpeakers = new ArrayList<>(SOUNDS_NUM);
|
||||||
private static Speaker musicSpeaker;
|
private static Speaker musicSpeaker;
|
||||||
private static ArrayList<SoundType> soundsBuffer = new ArrayList<>();
|
|
||||||
|
|
||||||
public static void initAL() {
|
public static void initAL() {
|
||||||
String defaultDeviceName = alcGetString(
|
String defaultDeviceName = alcGetString(
|
||||||
@ -82,31 +82,19 @@ public class AudioManager {
|
|||||||
return speaker;
|
return speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SoundType findSoundType(String soundID) throws Exception {
|
public static Speaker initSpeaker(SoundType st) {
|
||||||
for (SoundType s : soundsBuffer) {
|
|
||||||
if (s.getId().equals(soundID)) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Exception(
|
|
||||||
"ERROR: The selected sound is not loaded or" +
|
|
||||||
" not exists"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Speaker initSpeaker(String soundID) {
|
|
||||||
Speaker speaker = getLastSpeaker();
|
Speaker speaker = getLastSpeaker();
|
||||||
try {
|
try {
|
||||||
findSoundType(soundID).initSpeaker(speaker);
|
st.initSpeaker(speaker);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
return speaker;
|
return speaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Speaker initMusicSpeaker(String soundID) {
|
public static Speaker initMusicSpeaker(SoundType st) {
|
||||||
try {
|
try {
|
||||||
findSoundType(soundID).initSpeaker(musicSpeaker);
|
st.initSpeaker(musicSpeaker);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
@ -120,11 +108,11 @@ public class AudioManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadSound(String path, String id, AudioFormat format) {
|
public static void loadSound(Resource resource, String id, AudioFormat format) {
|
||||||
if (format == AudioFormat.MONO) {
|
if (format == AudioFormat.MONO) {
|
||||||
soundsBuffer.add(AudioReader.readAsMono(path, id));
|
AudioRegistry.getInstance().register(AudioReader.readAsMono(resource, id));
|
||||||
} else {
|
} else {
|
||||||
soundsBuffer.add(AudioReader.readAsStereo(path, id));
|
AudioRegistry.getInstance().register(AudioReader.readAsStereo(resource, id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.client.audio;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedInstanceRegistry;
|
||||||
|
|
||||||
|
public class AudioRegistry extends NamespacedInstanceRegistry<SoundType> {
|
||||||
|
|
||||||
|
private static final AudioRegistry INSTANCE = new AudioRegistry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the instance
|
||||||
|
*/
|
||||||
|
public static AudioRegistry getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.audio;
|
package ru.windcorp.progressia.client.audio;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
|
||||||
public class AudioSystem {
|
public class AudioSystem {
|
||||||
static public void initialize() {
|
static public void initialize() {
|
||||||
AudioManager.initAL();
|
AudioManager.initAL();
|
||||||
@ -28,7 +30,7 @@ public class AudioSystem {
|
|||||||
|
|
||||||
static void loadAudioData() {
|
static void loadAudioData() {
|
||||||
AudioManager.loadSound(
|
AudioManager.loadSound(
|
||||||
"assets/sounds/block_destroy_clap.ogg",
|
ResourceManager.getResource("assets/sounds/block_destroy_clap.ogg"),
|
||||||
"Progressia:BlockDestroy",
|
"Progressia:BlockDestroy",
|
||||||
AudioFormat.MONO
|
AudioFormat.MONO
|
||||||
);
|
);
|
||||||
|
@ -19,72 +19,37 @@
|
|||||||
package ru.windcorp.progressia.client.audio;
|
package ru.windcorp.progressia.client.audio;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
|
||||||
|
|
||||||
public class Music extends Namespaced {
|
public class Music
|
||||||
private Vec3 position = new Vec3();
|
extends Sound {
|
||||||
private Vec3 velocity = new Vec3();
|
|
||||||
private float pitch = 1.0f;
|
|
||||||
private float gain = 1.0f;
|
|
||||||
|
public Music(SoundType soundType, int timeLength, float pitch, float gain) {
|
||||||
|
super(soundType, timeLength, new Vec3(), new Vec3(), pitch, gain);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Music(SoundType soundType) {
|
||||||
|
super(soundType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Music(String id, int timeLength, float pitch, float gain) {
|
||||||
|
super(id, timeLength, new Vec3(), new Vec3(), pitch, gain);
|
||||||
|
}
|
||||||
|
|
||||||
public Music(String id) {
|
public Music(String id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Music(
|
@Override
|
||||||
String id,
|
protected Speaker initSpeaker() {
|
||||||
Vec3 position,
|
return AudioManager.initMusicSpeaker(soundType);
|
||||||
Vec3 velocity,
|
|
||||||
float pitch,
|
|
||||||
float gain
|
|
||||||
) {
|
|
||||||
this(id);
|
|
||||||
this.position = position;
|
|
||||||
this.velocity = velocity;
|
|
||||||
this.pitch = pitch;
|
|
||||||
this.gain = gain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void play(boolean loop) {
|
@Override
|
||||||
Speaker speaker = AudioManager.initMusicSpeaker(this.getId());
|
public void setPosition(Vec3 position) {
|
||||||
speaker.setGain(gain);
|
throw new UnsupportedOperationException();
|
||||||
speaker.setPitch(pitch);
|
|
||||||
speaker.setPosition(position);
|
|
||||||
speaker.setVelocity(velocity);
|
|
||||||
|
|
||||||
if (loop) {
|
|
||||||
speaker.playLoop();
|
|
||||||
} else {
|
|
||||||
speaker.play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGain(float gain) {
|
|
||||||
this.gain = gain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPitch(float pitch) {
|
|
||||||
this.pitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVelocity(Vec3 velocity) {
|
|
||||||
this.velocity = velocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vec3 getPosition() {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getGain() {
|
|
||||||
return gain;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vec3 getVelocity() {
|
|
||||||
return velocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getPitch() {
|
|
||||||
return pitch;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,23 +19,30 @@
|
|||||||
package ru.windcorp.progressia.client.audio;
|
package ru.windcorp.progressia.client.audio;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
import ru.windcorp.progressia.client.audio.backend.Speaker;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
|
||||||
|
|
||||||
public class SoundEffect
|
public class Sound {
|
||||||
extends Namespaced {
|
|
||||||
|
|
||||||
private Vec3 position = new Vec3();
|
protected Vec3 position = new Vec3(0f, 0f, 0f);
|
||||||
private Vec3 velocity = new Vec3();
|
protected Vec3 velocity = new Vec3(0f, 0f, 0f);
|
||||||
private float pitch = 1.0f;
|
protected float pitch = 1.0f;
|
||||||
private float gain = 1.0f;
|
protected float gain = 1.0f;
|
||||||
|
protected int timeLength = 0;
|
||||||
public SoundEffect(String id) {
|
|
||||||
super(id);
|
protected SoundType soundType;
|
||||||
|
|
||||||
|
public Sound(SoundType soundType) {
|
||||||
|
this.soundType = soundType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundEffect(
|
public Sound(String id) {
|
||||||
|
this(AudioRegistry.getInstance().get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sound(
|
||||||
String id,
|
String id,
|
||||||
|
int timeLength,
|
||||||
Vec3 position,
|
Vec3 position,
|
||||||
Vec3 velocity,
|
Vec3 velocity,
|
||||||
float pitch,
|
float pitch,
|
||||||
@ -47,9 +54,28 @@ public class SoundEffect
|
|||||||
this.pitch = pitch;
|
this.pitch = pitch;
|
||||||
this.gain = gain;
|
this.gain = gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Sound(
|
||||||
|
SoundType soundType,
|
||||||
|
int timeLength,
|
||||||
|
Vec3 position,
|
||||||
|
Vec3 velocity,
|
||||||
|
float pitch,
|
||||||
|
float gain
|
||||||
|
) {
|
||||||
|
this(soundType);
|
||||||
|
this.position = position;
|
||||||
|
this.velocity = velocity;
|
||||||
|
this.pitch = pitch;
|
||||||
|
this.gain = gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Speaker initSpeaker() {
|
||||||
|
return AudioManager.initSpeaker(soundType);
|
||||||
|
}
|
||||||
|
|
||||||
public void play(boolean loop) {
|
public void play(boolean loop) {
|
||||||
Speaker speaker = AudioManager.initSpeaker(this.getId());
|
Speaker speaker = initSpeaker();
|
||||||
speaker.setGain(gain);
|
speaker.setGain(gain);
|
||||||
speaker.setPitch(pitch);
|
speaker.setPitch(pitch);
|
||||||
speaker.setPosition(position);
|
speaker.setPosition(position);
|
||||||
@ -93,4 +119,9 @@ public class SoundEffect
|
|||||||
public float getPitch() {
|
public float getPitch() {
|
||||||
return pitch;
|
return pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getDuration() {
|
||||||
|
return soundType.getDuration();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -33,13 +33,11 @@ public class AudioReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO fix converting from mono-stereo
|
// TODO fix converting from mono-stereo
|
||||||
private static SoundType readAsSpecified(String path, String id, int format) {
|
private static SoundType readAsSpecified(Resource resource, String id, int format) {
|
||||||
IntBuffer channelBuffer = BufferUtils.createIntBuffer(1);
|
IntBuffer channelBuffer = BufferUtils.createIntBuffer(1);
|
||||||
IntBuffer rateBuffer = BufferUtils.createIntBuffer(1);
|
IntBuffer rateBuffer = BufferUtils.createIntBuffer(1);
|
||||||
|
|
||||||
Resource res = ResourceManager.getResource(path);
|
ShortBuffer rawAudio = decodeVorbis(resource, channelBuffer, rateBuffer);
|
||||||
|
|
||||||
ShortBuffer rawAudio = decodeVorbis(res, channelBuffer, rateBuffer);
|
|
||||||
|
|
||||||
return new SoundType(
|
return new SoundType(
|
||||||
id,
|
id,
|
||||||
@ -49,12 +47,12 @@ public class AudioReader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundType readAsMono(String path, String id) {
|
public static SoundType readAsMono(Resource resource, String id) {
|
||||||
return readAsSpecified(path, id, AL_FORMAT_MONO16);
|
return readAsSpecified(resource, id, AL_FORMAT_MONO16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundType readAsStereo(String path, String id) {
|
public static SoundType readAsStereo(Resource resource, String id) {
|
||||||
return readAsSpecified(path, id, AL_FORMAT_STEREO16);
|
return readAsSpecified(resource, id, AL_FORMAT_STEREO16);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ShortBuffer decodeVorbis(
|
private static ShortBuffer decodeVorbis(
|
||||||
|
@ -21,6 +21,9 @@ package ru.windcorp.progressia.client.audio.backend;
|
|||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
|
||||||
import java.nio.ShortBuffer;
|
import java.nio.ShortBuffer;
|
||||||
|
|
||||||
|
import org.lwjgl.openal.AL10;
|
||||||
|
|
||||||
import static org.lwjgl.openal.AL11.*;
|
import static org.lwjgl.openal.AL11.*;
|
||||||
|
|
||||||
public class SoundType extends Namespaced {
|
public class SoundType extends Namespaced {
|
||||||
@ -29,6 +32,7 @@ public class SoundType extends Namespaced {
|
|||||||
private int sampleRate;
|
private int sampleRate;
|
||||||
private int format;
|
private int format;
|
||||||
private int audioBuffer;
|
private int audioBuffer;
|
||||||
|
private double duration;
|
||||||
|
|
||||||
public SoundType(
|
public SoundType(
|
||||||
String id,
|
String id,
|
||||||
@ -46,9 +50,14 @@ public class SoundType extends Namespaced {
|
|||||||
private void createAudioBuffer() {
|
private void createAudioBuffer() {
|
||||||
this.audioBuffer = alGenBuffers();
|
this.audioBuffer = alGenBuffers();
|
||||||
alBufferData(audioBuffer, format, rawAudio, sampleRate);
|
alBufferData(audioBuffer, format, rawAudio, sampleRate);
|
||||||
|
duration = rawAudio.limit() / (double) sampleRate / (format == AL10.AL_FORMAT_STEREO16 ? 2 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initSpeaker(Speaker speaker) {
|
public void initSpeaker(Speaker speaker) {
|
||||||
speaker.setAudioData(audioBuffer);
|
speaker.setAudioData(audioBuffer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public double getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
}
|
@ -120,6 +120,7 @@ public class Speaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setAudioData(int audioData) {
|
public void setAudioData(int audioData) {
|
||||||
|
stop();
|
||||||
this.audioData = audioData;
|
this.audioData = audioData;
|
||||||
alSourcei(this.sourceData, AL_BUFFER, audioData);
|
alSourcei(this.sourceData, AL_BUFFER, audioData);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.windcorp.progressia.client.comms.controls;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.common.comms.controls.PacketControl;
|
||||||
|
|
||||||
|
public class ControlTriggerLocalLambda extends ControlTriggerInputBased {
|
||||||
|
|
||||||
|
private final Predicate<InputEvent> predicate;
|
||||||
|
private final Consumer<InputEvent> action;
|
||||||
|
|
||||||
|
public ControlTriggerLocalLambda(
|
||||||
|
String id,
|
||||||
|
Predicate<InputEvent> predicate,
|
||||||
|
Consumer<InputEvent> action
|
||||||
|
) {
|
||||||
|
super(id);
|
||||||
|
|
||||||
|
this.predicate = predicate;
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PacketControl onInputEvent(InputEvent event) {
|
||||||
|
if (!predicate.test(event))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
action.accept(event);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -142,6 +142,96 @@ public class ControlTriggers {
|
|||||||
predicates
|
predicates
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///
|
||||||
|
///
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
public static ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Consumer<InputEvent> action,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return new ControlTriggerLocalLambda(id, predicate, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<InputEvent> predicate
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
input -> action.run(),
|
||||||
|
predicate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Class<I> inputType,
|
||||||
|
Consumer<I> action,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
createCheckedAction(inputType, action),
|
||||||
|
createCheckedCompoundPredicate(inputType, predicates)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Class<I> inputType,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<I>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
inputType,
|
||||||
|
input -> action.run(),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Consumer<InputEvent> action,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return localOf(
|
||||||
|
id,
|
||||||
|
InputEvent.class,
|
||||||
|
action,
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <I extends InputEvent> ControlTriggerInputBased localOf(
|
||||||
|
String id,
|
||||||
|
Runnable action,
|
||||||
|
Predicate<InputEvent>... predicates
|
||||||
|
) {
|
||||||
|
return of(
|
||||||
|
id,
|
||||||
|
input -> action.run(),
|
||||||
|
predicates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private static <I extends InputEvent> BiConsumer<InputEvent, ControlData> createCheckedDataWriter(
|
private static <I extends InputEvent> BiConsumer<InputEvent, ControlData> createCheckedDataWriter(
|
||||||
Class<I> inputType,
|
Class<I> inputType,
|
||||||
@ -149,6 +239,13 @@ public class ControlTriggers {
|
|||||||
) {
|
) {
|
||||||
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
|
return (inputEvent, control) -> dataWriter.accept(inputType.cast(inputEvent), control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <I extends InputEvent> Consumer<InputEvent> createCheckedAction(
|
||||||
|
Class<I> inputType,
|
||||||
|
Consumer<I> action
|
||||||
|
) {
|
||||||
|
return inputEvent -> action.accept(inputType.cast(inputEvent));
|
||||||
|
}
|
||||||
|
|
||||||
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(
|
private static <I extends InputEvent> Predicate<InputEvent> createCheckedCompoundPredicate(
|
||||||
Class<I> inputType,
|
Class<I> inputType,
|
||||||
|
@ -18,11 +18,11 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.graphics.backend;
|
package ru.windcorp.progressia.client.graphics.backend;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.*;
|
|
||||||
|
|
||||||
import glm.vec._2.i.Vec2i;
|
import glm.vec._2.i.Vec2i;
|
||||||
|
import org.lwjgl.glfw.GLFWVidMode;
|
||||||
|
|
||||||
import static org.lwjgl.glfw.GLFW.*;
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
|
||||||
public class GraphicsBackend {
|
public class GraphicsBackend {
|
||||||
|
|
||||||
@ -38,9 +38,30 @@ public class GraphicsBackend {
|
|||||||
|
|
||||||
private static boolean faceCullingEnabled = false;
|
private static boolean faceCullingEnabled = false;
|
||||||
|
|
||||||
|
private static boolean isFullscreen = false;
|
||||||
|
private static boolean vSyncEnabled = false;
|
||||||
|
private static boolean isGLFWInitialized = false;
|
||||||
|
private static boolean isOpenGLInitialized = false;
|
||||||
|
|
||||||
private GraphicsBackend() {
|
private GraphicsBackend() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isGLFWInitialized() {
|
||||||
|
return isGLFWInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setGLFWInitialized(boolean isGLFWInitialized) {
|
||||||
|
GraphicsBackend.isGLFWInitialized = isGLFWInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isOpenGLInitialized() {
|
||||||
|
return isOpenGLInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setOpenGLInitialized(boolean isOpenGLInitialized) {
|
||||||
|
GraphicsBackend.isOpenGLInitialized = isOpenGLInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
public static void initialize() {
|
public static void initialize() {
|
||||||
startRenderThread();
|
startRenderThread();
|
||||||
}
|
}
|
||||||
@ -128,4 +149,47 @@ public class GraphicsBackend {
|
|||||||
faceCullingEnabled = useFaceCulling;
|
faceCullingEnabled = useFaceCulling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isFullscreen() {
|
||||||
|
return isFullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isVSyncEnabled() {
|
||||||
|
return vSyncEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFullscreen() {
|
||||||
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
glfwSetWindowMonitor(
|
||||||
|
getWindowHandle(),
|
||||||
|
glfwGetPrimaryMonitor(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
vidmode.width(),
|
||||||
|
vidmode.height(),
|
||||||
|
0);
|
||||||
|
isFullscreen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setWindowed() {
|
||||||
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
glfwSetWindowMonitor(
|
||||||
|
getWindowHandle(),
|
||||||
|
0,
|
||||||
|
(vidmode.width() - getFrameWidth()) / 2,
|
||||||
|
(vidmode.height() - getFrameHeight()) / 2,
|
||||||
|
getFrameWidth(),
|
||||||
|
getFrameHeight(),
|
||||||
|
0);
|
||||||
|
isFullscreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setVSyncEnabled(boolean enable) {
|
||||||
|
glfwSwapInterval(enable ? 1 : 0);
|
||||||
|
vSyncEnabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRefreshRate() {
|
||||||
|
GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
return vidmode.refreshRate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,4 +73,13 @@ public class GraphicsInterface {
|
|||||||
GraphicsBackend.startNextLayer();
|
GraphicsBackend.startNextLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void makeFullscreen(boolean state) {
|
||||||
|
if (state) {
|
||||||
|
GraphicsBackend.setFullscreen();
|
||||||
|
} else {
|
||||||
|
GraphicsBackend.setWindowed();
|
||||||
|
}
|
||||||
|
GraphicsBackend.setVSyncEnabled(GraphicsBackend.isVSyncEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ class LWJGLInitializer {
|
|||||||
private static void initializeGLFW() {
|
private static void initializeGLFW() {
|
||||||
// TODO Do GLFW error handling: check glfwInit, setup error callback
|
// TODO Do GLFW error handling: check glfwInit, setup error callback
|
||||||
glfwInit();
|
glfwInit();
|
||||||
|
GraphicsBackend.setGLFWInitialized(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createWindow() {
|
private static void createWindow() {
|
||||||
@ -67,7 +68,7 @@ class LWJGLInitializer {
|
|||||||
glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||||
|
|
||||||
glfwMakeContextCurrent(handle);
|
glfwMakeContextCurrent(handle);
|
||||||
glfwSwapInterval(0);
|
glfwSwapInterval(0); // TODO: remove after config system is added
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void positionWindow() {
|
private static void positionWindow() {
|
||||||
@ -87,6 +88,7 @@ class LWJGLInitializer {
|
|||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
RenderTaskQueue.schedule(OpenGLObjectTracker::deleteEnqueuedObjects);
|
RenderTaskQueue.schedule(OpenGLObjectTracker::deleteEnqueuedObjects);
|
||||||
|
GraphicsBackend.setOpenGLInitialized(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setupWindowCallbacks() {
|
private static void setupWindowCallbacks() {
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import glm.mat._4.Mat4;
|
||||||
|
import glm.vec._2.i.Vec2i;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
||||||
|
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||||
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
|
||||||
|
public class Button extends Interactable {
|
||||||
|
|
||||||
|
public <T extends InputEvent> Button(String name, Label textLabel, Consumer<Button> onClick) {//, InputListener<T> onClick, Class<? extends T> onClickClass) {
|
||||||
|
super(name, textLabel);
|
||||||
|
setPreferredSize(107,34);
|
||||||
|
//Button inButton = (Button) setFocusable(true);
|
||||||
|
|
||||||
|
addListener((Class<KeyEvent>) KeyEvent.class, (InputListener<KeyEvent>) e -> {if ((e.isLeftMouseButton() && containsCursor()) || (e.getKey()==GLFW.GLFW_KEY_ENTER && isFocused()) )
|
||||||
|
{
|
||||||
|
isClicked = e.isPress();
|
||||||
|
if (!isDisabled())
|
||||||
|
{
|
||||||
|
onClick.accept(this);
|
||||||
|
takeFocus();
|
||||||
|
}
|
||||||
|
requestReassembly();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
//Border
|
||||||
|
if (isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX(), getY(), getWidth(), getHeight(), 0xFFE5E5E5);
|
||||||
|
}
|
||||||
|
else if (isClicked() || isHovered() || isFocused())
|
||||||
|
{
|
||||||
|
target.fill(getX(), getY(), getWidth(), getHeight(), 0xFF37A2E6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target.fill(getX(), getY(), getWidth(), getHeight(), 0xFFCBCBD0);
|
||||||
|
}
|
||||||
|
//Inside area
|
||||||
|
if (!isClicked() && isHovered() && !isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+2, getY()+2, getWidth()-4, getHeight()-4, 0xFFC3E4F7);
|
||||||
|
}
|
||||||
|
else if (!isClicked() || isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+2, getY()+2, getWidth()-4, getHeight()-4, Colors.WHITE);
|
||||||
|
}
|
||||||
|
Font tempFont = new Font().withColor(Colors.BLACK);
|
||||||
|
if (isDisabled())
|
||||||
|
{
|
||||||
|
tempFont = tempFont.withColor(Colors.GRAY_A);
|
||||||
|
}
|
||||||
|
else if (isClicked())
|
||||||
|
{
|
||||||
|
tempFont = tempFont.withColor(Colors.WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.pushTransform(new Mat4().identity().translate( getX()+.5f*getWidth()-.5f*label.getPreferredSize().x, getY(), 0));
|
||||||
|
label = new Label(label.getName(), tempFont, label.getContentSupplier());
|
||||||
|
label.assembleSelf(target);
|
||||||
|
target.popTransform();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import glm.mat._4.Mat4;
|
||||||
|
import glm.vec._2.i.Vec2i;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.InputTracker;
|
||||||
|
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||||
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.InputEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
|
||||||
|
public class Checkbox extends Interactable {
|
||||||
|
|
||||||
|
private boolean isActive;
|
||||||
|
|
||||||
|
public <T extends InputEvent> Checkbox(String name, Label textLabel, Consumer<Checkbox> onSet, Consumer<Checkbox> onReset) {//, InputListener<T> onClick, Class<? extends T> onClickClass) {
|
||||||
|
super(name, textLabel);
|
||||||
|
setPreferredSize(44 + textLabel.getPreferredSize().x,textLabel.getPreferredSize().y);
|
||||||
|
//Checkbox inCheck = (Checkbox) setFocusable(true);
|
||||||
|
|
||||||
|
addListener((Class<KeyEvent>) KeyEvent.class, (InputListener<KeyEvent>) e -> {if (e.isLeftMouseButton() && containsCursor()|| (e.getKey()==GLFW.GLFW_KEY_ENTER && isFocused()))
|
||||||
|
{
|
||||||
|
isClicked = e.isPress();
|
||||||
|
if (!isDisabled())
|
||||||
|
{
|
||||||
|
if (!isClicked && !isActive)
|
||||||
|
{
|
||||||
|
onSet.accept(this);
|
||||||
|
isActive = !isActive;
|
||||||
|
}
|
||||||
|
else if (!isClicked && isActive)
|
||||||
|
{
|
||||||
|
onReset.accept(this);
|
||||||
|
isActive = !isActive;
|
||||||
|
}
|
||||||
|
else if (isClicked)
|
||||||
|
{
|
||||||
|
takeFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestReassembly();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive()
|
||||||
|
{
|
||||||
|
return isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
//Border
|
||||||
|
if (isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+label.getPreferredSize().x, getY(), getWidth()-label.getPreferredSize().x, getHeight(), 0xFFE5E5E5);
|
||||||
|
}
|
||||||
|
else if (isClicked() || isHovered() || isFocused())
|
||||||
|
{
|
||||||
|
target.fill(getX()+label.getPreferredSize().x, getY(), getWidth()-label.getPreferredSize().x, getHeight(), 0xFF37A2E6); // blue
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target.fill(getX()+label.getPreferredSize().x, getY(), getWidth()-label.getPreferredSize().x, getHeight(), 0xFFCBCBD0);
|
||||||
|
}
|
||||||
|
//Inside area
|
||||||
|
if (!isClicked() && isHovered() && !isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+2+label.getPreferredSize().x, getY()+2, getWidth()-label.getPreferredSize().x-4, getHeight()-4, 0xFFC3E4F7); // light blue
|
||||||
|
}
|
||||||
|
else if (!isClicked() || isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+2+label.getPreferredSize().x, getY()+2, getWidth()-label.getPreferredSize().x-4, getHeight()-4, Colors.WHITE);
|
||||||
|
}
|
||||||
|
if (isActive() && !isClicked())
|
||||||
|
{
|
||||||
|
if (isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFB3D7EF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFF37A2E6); // blue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!isClicked())
|
||||||
|
{
|
||||||
|
if (isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+label.getPreferredSize().x+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFE5E5E5);
|
||||||
|
}
|
||||||
|
else if (isFocused() || isHovered())
|
||||||
|
{
|
||||||
|
target.fill(getX()+label.getPreferredSize().x+4, getY()+4, getHeight()-8, getHeight()-8, 0xFF37A2E6); // blue
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target.fill(getX()+label.getPreferredSize().x+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFCBCBD0);
|
||||||
|
}
|
||||||
|
target.fill(getX()+label.getPreferredSize().x+6, getY()+6, getHeight()-12, getHeight()-12, Colors.WHITE);
|
||||||
|
}
|
||||||
|
target.pushTransform(new Mat4().identity().translate( getX(), getY(), 0));
|
||||||
|
label.assembleSelf(target);
|
||||||
|
target.popTransform();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.FocusEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.event.HoverEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
|
import glm.vec._2.i.Vec2i;
|
||||||
|
|
||||||
|
public class Interactable extends Component {
|
||||||
|
|
||||||
|
private Vec2i currentSize;
|
||||||
|
protected boolean isDisabled;
|
||||||
|
protected boolean isClicked;
|
||||||
|
protected Label label;
|
||||||
|
|
||||||
|
public Interactable(String name, Label textLabel)
|
||||||
|
{
|
||||||
|
super(name);
|
||||||
|
label = textLabel;
|
||||||
|
addChild(textLabel);
|
||||||
|
|
||||||
|
addListener(new Object() {
|
||||||
|
@Subscribe
|
||||||
|
public void onHoverChanged(HoverEvent e) {
|
||||||
|
requestReassembly();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addListener(new Object() {
|
||||||
|
@Subscribe
|
||||||
|
public void onFocusChanged(FocusEvent e) {
|
||||||
|
//inButton.setText(new Label("dummy",new Font().withColor(Colors.BLACK),e.getNewState() ? "Is Focused" : "Isn't focused"));
|
||||||
|
requestReassembly();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClicked()
|
||||||
|
{
|
||||||
|
return isClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisable(boolean isDisabled)
|
||||||
|
{
|
||||||
|
this.isDisabled = isDisabled;
|
||||||
|
setFocusable(isDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisabled()
|
||||||
|
{
|
||||||
|
return isDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(Label newText)
|
||||||
|
{
|
||||||
|
removeChild(label);
|
||||||
|
label = newText;
|
||||||
|
addChild(label);
|
||||||
|
requestReassembly();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import glm.mat._4.Mat4;
|
||||||
|
import glm.vec._2.i.Vec2i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.flat.RenderTarget;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
|
import ru.windcorp.progressia.client.graphics.input.bus.InputListener;
|
||||||
|
|
||||||
|
public class RadioButton extends Interactable {
|
||||||
|
private RadioManager manager;
|
||||||
|
private boolean isSelected;
|
||||||
|
|
||||||
|
public RadioButton(String name, Label textLabel, Consumer<RadioButton> onSelect, RadioManager myManager)
|
||||||
|
{
|
||||||
|
super(name, textLabel);
|
||||||
|
setPreferredSize(textLabel.getPreferredSize().x+23,textLabel.getPreferredSize().y);
|
||||||
|
manager = myManager;
|
||||||
|
manager.addOption(this);
|
||||||
|
|
||||||
|
addListener((Class<KeyEvent>) KeyEvent.class, (InputListener<KeyEvent>) e -> {if ((e.isLeftMouseButton() && containsCursor()) || (e.getKey()==GLFW.GLFW_KEY_ENTER && isFocused()) )
|
||||||
|
{
|
||||||
|
isClicked = e.isPress();
|
||||||
|
if (!isDisabled() && !isClicked)
|
||||||
|
{
|
||||||
|
onSelect.accept(this);
|
||||||
|
manager.selectSelf(this);
|
||||||
|
}
|
||||||
|
requestReassembly();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSelected()
|
||||||
|
{
|
||||||
|
return isSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelected(boolean selected)
|
||||||
|
{
|
||||||
|
isSelected = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assembleSelf(RenderTarget target) {
|
||||||
|
if (isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight(), getY(), getHeight(), getHeight(), 0xFFE5E5E5);
|
||||||
|
}
|
||||||
|
else if (isClicked() || isHovered() || isFocused())
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight(), getY(), getHeight(), getHeight(), 0xFF37A2E6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight(), getY(), getHeight(), getHeight(), 0xFFCBCBD0);
|
||||||
|
}
|
||||||
|
//Inside area
|
||||||
|
if (!isClicked() && isHovered() && !isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight()+2, getY()+2, getHeight()-4, getHeight()-4, 0xFFC3E4F7);
|
||||||
|
}
|
||||||
|
else if (!isClicked() || isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight()+2, getY()+2, getHeight()-4, getHeight()-4, Colors.WHITE);
|
||||||
|
}
|
||||||
|
if (isSelected())
|
||||||
|
{
|
||||||
|
if (!isDisabled())
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFF37A2E6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target.fill(getX()+getWidth()-getHeight()+4, getY()+4, getHeight()-8, getHeight()-8, 0xFFC3E4F7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target.pushTransform(new Mat4().identity().translate( getX(), getY(), 0));
|
||||||
|
label.assembleSelf(target);
|
||||||
|
target.popTransform();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package ru.windcorp.progressia.client.graphics.gui;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
public class RadioManager {
|
||||||
|
private List<RadioButton> options;
|
||||||
|
private int selectedOption;
|
||||||
|
|
||||||
|
public RadioManager()
|
||||||
|
{
|
||||||
|
options = Collections.synchronizedList(new CopyOnWriteArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOption(RadioButton option)
|
||||||
|
{
|
||||||
|
options.add(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSelected()
|
||||||
|
{
|
||||||
|
return selectedOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectSelf(RadioButton option)
|
||||||
|
{
|
||||||
|
if (!options.contains(option))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
options.get(selectedOption).setSelected(false);
|
||||||
|
selectedOption = options.indexOf(option);
|
||||||
|
option.takeFocus();
|
||||||
|
option.setSelected(true);
|
||||||
|
}
|
||||||
|
}
|
@ -38,10 +38,6 @@ public class StaticModel extends Model {
|
|||||||
this.transforms = transforms;
|
this.transforms = transforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StaticModel(Builder builder) {
|
|
||||||
this(builder.getParts(), builder.getTransforms());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Mat4 getTransform(int partIndex) {
|
protected Mat4 getTransform(int partIndex) {
|
||||||
return transforms[partIndex];
|
return transforms[partIndex];
|
||||||
@ -82,6 +78,10 @@ public class StaticModel extends Model {
|
|||||||
private Mat4[] getTransforms() {
|
private Mat4[] getTransforms() {
|
||||||
return transforms.toArray(new Mat4[transforms.size()]);
|
return transforms.toArray(new Mat4[transforms.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StaticModel build() {
|
||||||
|
return new StaticModel(getParts(), getTransforms());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ public class LayerWorld extends Layer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new StaticModel(b);
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final float FRICTION_COEFF = Units.get("1e-5f kg/s");
|
private static final float FRICTION_COEFF = Units.get("1e-5f kg/s");
|
||||||
|
@ -18,35 +18,20 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.world;
|
package ru.windcorp.progressia.client.world;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import glm.mat._4.Mat4;
|
|
||||||
import glm.vec._3.Vec3;
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Model;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.progressia.client.graphics.model.StaticModel;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.StaticModel.Builder;
|
|
||||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||||
import ru.windcorp.progressia.client.world.block.BlockRenderNone;
|
|
||||||
import ru.windcorp.progressia.client.world.block.BlockRenderRegistry;
|
import ru.windcorp.progressia.client.world.block.BlockRenderRegistry;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSupplier;
|
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizers;
|
|
||||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||||
import ru.windcorp.progressia.client.world.tile.TileRenderRegistry;
|
import ru.windcorp.progressia.client.world.tile.TileRenderRegistry;
|
||||||
import ru.windcorp.progressia.client.world.tile.TileRenderStack;
|
import ru.windcorp.progressia.client.world.tile.TileRenderStack;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
|
||||||
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
||||||
|
|
||||||
public class ChunkRender
|
public class ChunkRender
|
||||||
@ -55,7 +40,7 @@ public class ChunkRender
|
|||||||
private final WorldRender world;
|
private final WorldRender world;
|
||||||
private final ChunkData data;
|
private final ChunkData data;
|
||||||
|
|
||||||
private Model model = null;
|
private final ChunkRenderModel model;
|
||||||
|
|
||||||
private final Map<TileDataStack, TileRenderStackImpl> tileRenderLists = Collections
|
private final Map<TileDataStack, TileRenderStackImpl> tileRenderLists = Collections
|
||||||
.synchronizedMap(new WeakHashMap<>());
|
.synchronizedMap(new WeakHashMap<>());
|
||||||
@ -63,6 +48,7 @@ public class ChunkRender
|
|||||||
public ChunkRender(WorldRender world, ChunkData data) {
|
public ChunkRender(WorldRender world, ChunkData data) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
this.model = new ChunkRenderModel(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -107,165 +93,11 @@ public class ChunkRender
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void render(ShapeRenderHelper renderer) {
|
public synchronized void render(ShapeRenderHelper renderer) {
|
||||||
if (model == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.pushTransform().translate(
|
|
||||||
data.getX() * ChunkData.BLOCKS_PER_CHUNK,
|
|
||||||
data.getY() * ChunkData.BLOCKS_PER_CHUNK,
|
|
||||||
data.getZ() * ChunkData.BLOCKS_PER_CHUNK
|
|
||||||
);
|
|
||||||
|
|
||||||
model.render(renderer);
|
model.render(renderer);
|
||||||
|
|
||||||
renderer.popTransform();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void update() {
|
public synchronized void update() {
|
||||||
Collection<ChunkRenderOptimizer> optimizers = ChunkRenderOptimizers.getAllSuppliers().stream()
|
model.update();
|
||||||
.map(ChunkRenderOptimizerSupplier::createOptimizer)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
optimizers.forEach(bro -> bro.startRender(this));
|
|
||||||
|
|
||||||
StaticModel.Builder builder = StaticModel.builder();
|
|
||||||
|
|
||||||
Vec3i cursor = new Vec3i();
|
|
||||||
for (int x = 0; x < ChunkData.BLOCKS_PER_CHUNK; ++x) {
|
|
||||||
for (int y = 0; y < ChunkData.BLOCKS_PER_CHUNK; ++y) {
|
|
||||||
for (int z = 0; z < ChunkData.BLOCKS_PER_CHUNK; ++z) {
|
|
||||||
cursor.set(x, y, z);
|
|
||||||
|
|
||||||
buildBlock(cursor, optimizers, builder);
|
|
||||||
buildBlockTiles(cursor, optimizers, builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
optimizers.stream()
|
|
||||||
.map(ChunkRenderOptimizer::endRender)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(builder::addPart);
|
|
||||||
|
|
||||||
model = new StaticModel(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildBlock(
|
|
||||||
Vec3i cursor,
|
|
||||||
Collection<ChunkRenderOptimizer> optimizers,
|
|
||||||
Builder builder
|
|
||||||
) {
|
|
||||||
BlockRender block = getBlock(cursor);
|
|
||||||
|
|
||||||
if (block instanceof BlockRenderNone) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
forwardBlockToOptimizers(block, cursor, optimizers);
|
|
||||||
|
|
||||||
if (!block.needsOwnRenderable()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
addBlockRenderable(block, cursor, builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void forwardBlockToOptimizers(
|
|
||||||
BlockRender block,
|
|
||||||
Vec3i cursor,
|
|
||||||
Collection<ChunkRenderOptimizer> optimizers
|
|
||||||
) {
|
|
||||||
optimizers.forEach(cro -> cro.processBlock(block, cursor));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addBlockRenderable(
|
|
||||||
BlockRender block,
|
|
||||||
Vec3i cursor,
|
|
||||||
Builder builder
|
|
||||||
) {
|
|
||||||
Renderable renderable = block.createRenderable();
|
|
||||||
|
|
||||||
if (renderable == null) {
|
|
||||||
renderable = block::render;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.addPart(
|
|
||||||
renderable,
|
|
||||||
new Mat4().identity().translate(cursor.x, cursor.y, cursor.z)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildBlockTiles(
|
|
||||||
Vec3i cursor,
|
|
||||||
Collection<ChunkRenderOptimizer> optimizers,
|
|
||||||
Builder builder
|
|
||||||
) {
|
|
||||||
for (BlockFace face : BlockFace.getFaces()) {
|
|
||||||
buildFaceTiles(cursor, face, optimizers, builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildFaceTiles(
|
|
||||||
Vec3i cursor,
|
|
||||||
BlockFace face,
|
|
||||||
Collection<ChunkRenderOptimizer> optimizers,
|
|
||||||
Builder builder
|
|
||||||
) {
|
|
||||||
List<TileData> tiles = getData().getTilesOrNull(cursor, face);
|
|
||||||
|
|
||||||
if (tiles == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int layer = 0; layer < tiles.size(); ++layer) {
|
|
||||||
|
|
||||||
if (tiles.get(layer) == null) {
|
|
||||||
System.out.println(tiles.get(layer).getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTile(
|
|
||||||
cursor,
|
|
||||||
face,
|
|
||||||
TileRenderRegistry.getInstance().get(
|
|
||||||
tiles.get(layer).getId()
|
|
||||||
),
|
|
||||||
layer,
|
|
||||||
optimizers,
|
|
||||||
builder
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void buildTile(
|
|
||||||
Vec3i cursor,
|
|
||||||
BlockFace face,
|
|
||||||
TileRender tile,
|
|
||||||
int layer,
|
|
||||||
Collection<ChunkRenderOptimizer> optimizers,
|
|
||||||
Builder builder
|
|
||||||
) {
|
|
||||||
// TODO implement
|
|
||||||
|
|
||||||
Vec3 pos = new Vec3(cursor.x, cursor.y, cursor.z);
|
|
||||||
|
|
||||||
optimizers.forEach(cro -> cro.processTile(tile, cursor, face));
|
|
||||||
|
|
||||||
if (!tile.needsOwnRenderable())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Vec3 offset = new Vec3(
|
|
||||||
face.getVector().x,
|
|
||||||
face.getVector().y,
|
|
||||||
face.getVector().z
|
|
||||||
);
|
|
||||||
|
|
||||||
pos.add(offset.mul(1f / 64));
|
|
||||||
|
|
||||||
builder.addPart(
|
|
||||||
tile.createRenderable(face),
|
|
||||||
new Mat4().identity().translate(pos)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TileRenderStackImpl extends TileRenderStack {
|
private class TileRenderStackImpl extends TileRenderStack {
|
||||||
|
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.client.world;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import glm.mat._4.Mat4;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Model;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.StaticModel;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.StaticModel.Builder;
|
||||||
|
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||||
|
import ru.windcorp.progressia.client.world.block.BlockRenderNone;
|
||||||
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
||||||
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
||||||
|
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||||
|
import ru.windcorp.progressia.client.world.tile.TileRenderNone;
|
||||||
|
import ru.windcorp.progressia.client.world.tile.TileRenderStack;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
|
public class ChunkRenderModel implements Renderable {
|
||||||
|
|
||||||
|
private final ChunkRender chunk;
|
||||||
|
|
||||||
|
private final Collection<ChunkRenderOptimizer> optimizers = new ArrayList<>();
|
||||||
|
private Model model = null;
|
||||||
|
|
||||||
|
public ChunkRenderModel(ChunkRender chunk) {
|
||||||
|
this.chunk = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(ShapeRenderHelper renderer) {
|
||||||
|
if (model == null) return;
|
||||||
|
|
||||||
|
renderer.pushTransform().translate(
|
||||||
|
chunk.getX() * ChunkData.BLOCKS_PER_CHUNK,
|
||||||
|
chunk.getY() * ChunkData.BLOCKS_PER_CHUNK,
|
||||||
|
chunk.getZ() * ChunkData.BLOCKS_PER_CHUNK
|
||||||
|
);
|
||||||
|
|
||||||
|
model.render(renderer);
|
||||||
|
|
||||||
|
renderer.popTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
setupCROs();
|
||||||
|
|
||||||
|
StaticModel.Builder sink = StaticModel.builder();
|
||||||
|
|
||||||
|
optimizers.forEach(ChunkRenderOptimizer::startRender);
|
||||||
|
|
||||||
|
chunk.forEachBiC(blockInChunk -> {
|
||||||
|
processBlockAndTiles(blockInChunk, sink);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (ChunkRenderOptimizer optimizer : optimizers) {
|
||||||
|
Renderable renderable = optimizer.endRender();
|
||||||
|
if (renderable != null) {
|
||||||
|
sink.addPart(renderable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model = sink.build();
|
||||||
|
this.optimizers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCROs() {
|
||||||
|
Set<String> ids = ChunkRenderOptimizerRegistry.getInstance().keySet();
|
||||||
|
|
||||||
|
for (String id : ids) {
|
||||||
|
ChunkRenderOptimizer optimizer = ChunkRenderOptimizerRegistry.getInstance().create(id);
|
||||||
|
optimizer.setup(chunk);
|
||||||
|
this.optimizers.add(optimizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processBlockAndTiles(Vec3i blockInChunk, Builder sink) {
|
||||||
|
processBlock(blockInChunk, sink);
|
||||||
|
|
||||||
|
for (BlockFace face : BlockFace.getFaces()) {
|
||||||
|
processTileStack(blockInChunk, face, sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processBlock(Vec3i blockInChunk, Builder sink) {
|
||||||
|
BlockRender block = chunk.getBlock(blockInChunk);
|
||||||
|
|
||||||
|
if (block instanceof BlockRenderNone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.needsOwnRenderable()) {
|
||||||
|
sink.addPart(
|
||||||
|
block.createRenderable(chunk.getData(), blockInChunk),
|
||||||
|
new Mat4().identity().translate(blockInChunk.x, blockInChunk.y, blockInChunk.z)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
processBlockWithCROs(block, blockInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processBlockWithCROs(BlockRender block, Vec3i blockInChunk) {
|
||||||
|
for (ChunkRenderOptimizer optimizer : optimizers) {
|
||||||
|
optimizer.addBlock(block, blockInChunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processTileStack(Vec3i blockInChunk, BlockFace face, Builder sink) {
|
||||||
|
TileRenderStack trs = chunk.getTilesOrNull(blockInChunk, face);
|
||||||
|
|
||||||
|
if (trs == null || trs.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trs.forEach(tile -> processTile(tile, blockInChunk, face, sink));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processTile(TileRender tile, Vec3i blockInChunk, BlockFace face, Builder sink) {
|
||||||
|
if (tile instanceof TileRenderNone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile.needsOwnRenderable()) {
|
||||||
|
sink.addPart(
|
||||||
|
tile.createRenderable(chunk.getData(), blockInChunk, face),
|
||||||
|
new Mat4().identity().translate(blockInChunk.x, blockInChunk.y, blockInChunk.z)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
processTileWithCROs(tile, blockInChunk, face);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processTileWithCROs(TileRender tile, Vec3i blockInChunk, BlockFace face) {
|
||||||
|
for (ChunkRenderOptimizer optimizer : optimizers) {
|
||||||
|
optimizer.addTile(tile, blockInChunk, face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,8 +18,14 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.world;
|
package ru.windcorp.progressia.client.world;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.VectorUtil;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
|
|
||||||
class ChunkUpdateListener implements ChunkDataListener {
|
class ChunkUpdateListener implements ChunkDataListener {
|
||||||
|
|
||||||
@ -33,5 +39,61 @@ class ChunkUpdateListener implements ChunkDataListener {
|
|||||||
public void onChunkChanged(ChunkData chunk) {
|
public void onChunkChanged(ChunkData chunk) {
|
||||||
world.getChunk(chunk).markForUpdate();
|
world.getChunk(chunk).markForUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChunkLoaded(ChunkData chunk) {
|
||||||
|
Vec3i cursor = new Vec3i();
|
||||||
|
for (BlockFace face : BlockFace.getFaces()) {
|
||||||
|
cursor.set(chunk.getX(), chunk.getY(), chunk.getZ());
|
||||||
|
cursor.add(face.getVector());
|
||||||
|
world.markChunkForUpdate(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChunkBlockChanged(ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current) {
|
||||||
|
onLocationChanged(chunk, blockInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChunkTilesChanged(
|
||||||
|
ChunkData chunk,
|
||||||
|
Vec3i blockInChunk,
|
||||||
|
BlockFace face,
|
||||||
|
TileData tile,
|
||||||
|
boolean wasAdded
|
||||||
|
) {
|
||||||
|
onLocationChanged(chunk, blockInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onLocationChanged(ChunkData chunk, Vec3i blockInChunk) {
|
||||||
|
Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ());
|
||||||
|
|
||||||
|
checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.X);
|
||||||
|
checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.Y);
|
||||||
|
checkCoordinate(blockInChunk, chunkPos, VectorUtil.Axis.Z);
|
||||||
|
|
||||||
|
Vectors.release(chunkPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCoordinate(Vec3i blockInChunk, Vec3i chunkPos, VectorUtil.Axis axis) {
|
||||||
|
int block = VectorUtil.get(blockInChunk, axis);
|
||||||
|
int diff = 0;
|
||||||
|
|
||||||
|
if (block == 0) {
|
||||||
|
diff = -1;
|
||||||
|
} else if (block == ChunkData.BLOCKS_PER_CHUNK - 1) {
|
||||||
|
diff = +1;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int previousChunkPos = VectorUtil.get(chunkPos, axis);
|
||||||
|
VectorUtil.set(chunkPos, axis, previousChunkPos + diff);
|
||||||
|
|
||||||
|
world.markChunkForUpdate(chunkPos);
|
||||||
|
|
||||||
|
VectorUtil.set(chunkPos, axis, previousChunkPos);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ package ru.windcorp.progressia.client.world.block;
|
|||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.generic.GenericBlock;
|
import ru.windcorp.progressia.common.world.generic.GenericBlock;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
|
||||||
public abstract class BlockRender extends Namespaced implements GenericBlock {
|
public abstract class BlockRender extends Namespaced implements GenericBlock {
|
||||||
@ -35,7 +37,7 @@ public abstract class BlockRender extends Namespaced implements GenericBlock {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Renderable createRenderable() {
|
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,10 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.world.block;
|
package ru.windcorp.progressia.client.world.block;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.graphics.model.EmptyModel;
|
import ru.windcorp.progressia.client.graphics.model.EmptyModel;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
|
||||||
public class BlockRenderNone extends BlockRender {
|
public class BlockRenderNone extends BlockRender {
|
||||||
|
|
||||||
@ -28,7 +30,7 @@ public class BlockRenderNone extends BlockRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Renderable createRenderable() {
|
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
|
||||||
return EmptyModel.getInstance();
|
return EmptyModel.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +65,4 @@ public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsOwnRenderable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,17 +22,27 @@ import static ru.windcorp.progressia.common.world.block.BlockFace.*;
|
|||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Shapes;
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import glm.vec._4.Vec4;
|
||||||
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Face;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Faces;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerCube.OpaqueCube;
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.BlockOptimizedSurface;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
public abstract class BlockRenderTexturedCube
|
public abstract class BlockRenderTexturedCube
|
||||||
extends BlockRender
|
extends BlockRender
|
||||||
implements OpaqueCube {
|
implements BlockOptimizedSurface {
|
||||||
|
|
||||||
private final Map<BlockFace, Texture> textures = new HashMap<>();
|
private final Map<BlockFace, Texture> textures = new HashMap<>();
|
||||||
|
|
||||||
@ -55,22 +65,61 @@ public abstract class BlockRenderTexturedCube
|
|||||||
textures.put(WEST, westTexture);
|
textures.put(WEST, westTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Texture getTexture(BlockFace blockFace) {
|
||||||
public Texture getTexture(BlockFace face) {
|
return textures.get(blockFace);
|
||||||
return textures.get(face);
|
}
|
||||||
|
|
||||||
|
public Vec4 getColorMultiplier(BlockFace blockFace) {
|
||||||
|
return Colors.WHITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Renderable createRenderable() {
|
public final void getFaces(
|
||||||
return new Shapes.PppBuilder(
|
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||||
|
boolean inner,
|
||||||
|
Consumer<Face> output,
|
||||||
|
Vec3 offset
|
||||||
|
) {
|
||||||
|
output.accept(createFace(chunk, blockInChunk, blockFace, inner, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Face createFace(
|
||||||
|
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||||
|
boolean inner,
|
||||||
|
Vec3 offset
|
||||||
|
) {
|
||||||
|
return Faces.createBlockFace(
|
||||||
WorldRenderProgram.getDefault(),
|
WorldRenderProgram.getDefault(),
|
||||||
getTexture(TOP),
|
getTexture(blockFace),
|
||||||
getTexture(BOTTOM),
|
getColorMultiplier(blockFace),
|
||||||
getTexture(NORTH),
|
offset,
|
||||||
getTexture(SOUTH),
|
blockFace,
|
||||||
getTexture(EAST),
|
inner
|
||||||
getTexture(WEST)
|
);
|
||||||
).create();
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
|
||||||
|
boolean opaque = isBlockOpaque();
|
||||||
|
|
||||||
|
Face[] faces = new Face[BLOCK_FACE_COUNT + (opaque ? BLOCK_FACE_COUNT : 0)];
|
||||||
|
|
||||||
|
for (int i = 0; i < BLOCK_FACE_COUNT; ++i) {
|
||||||
|
faces[i] = createFace(chunk, blockInChunk, BlockFace.getFaces().get(i), false, Vectors.ZERO_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opaque) {
|
||||||
|
for (int i = 0; i < BLOCK_FACE_COUNT; ++i) {
|
||||||
|
faces[i + BLOCK_FACE_COUNT] = createFace(chunk, blockInChunk, BlockFace.getFaces().get(i), true, Vectors.ZERO_3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Shape(Usage.STATIC, WorldRenderProgram.getDefault(), faces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsOwnRenderable() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -65,9 +65,4 @@ public class BlockRenderTransparentCube extends BlockRenderTexturedCube {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsOwnRenderable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,31 +15,115 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ru.windcorp.progressia.client.world.cro;
|
package ru.windcorp.progressia.client.world.cro;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||||
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
public abstract class ChunkRenderOptimizer {
|
/**
|
||||||
|
* Chunk render optimizer (CRO) is an object that produces optimized models for
|
||||||
|
* chunks. CROs are sequentially given information about the blocks and tiles of
|
||||||
|
* a particular chunk, after which they are expected to produce a set of
|
||||||
|
* {@link Renderable}s. As the name suggests, CROs are primarily expected to
|
||||||
|
* output models that are optimized compared to models of individual blocks and
|
||||||
|
* tiles. An example of a CRO is {@link ChunkRenderOptimizerSurface}: it removes
|
||||||
|
* block surfaces and tiles that it knows cannot be seen, thus significantly
|
||||||
|
* reducing total polygon count.
|
||||||
|
* <h3>CRO lifecycle</h3>
|
||||||
|
* A CRO instance is created by {@link ChunkRenderOptimizerRegistry}. It may
|
||||||
|
* then be used to work on multiple chunks sequentially. Each chunk is processed
|
||||||
|
* in the following way:
|
||||||
|
* <ol>
|
||||||
|
* <li>{@link #setup(ChunkRender)} is invoked to provide the {@link ChunkRender}
|
||||||
|
* instance.</li>
|
||||||
|
* <li>{@link #startRender()} is invoked. The CRO must reset its state.</li>
|
||||||
|
* <li>{@link #addBlock(BlockRender, Vec3i)} and
|
||||||
|
* {@link #addTile(TileRender, Vec3i, BlockFace)} are invoked for each block and
|
||||||
|
* tile that this CRO should optimize. {@code addTile} specifies tiles in order
|
||||||
|
* of ascension within a tile stack.</li>
|
||||||
|
* <li>{@link #endRender()} is invoked. The CRO may perform any pending
|
||||||
|
* calculations. The result of the optimization is returned.</li>
|
||||||
|
* </ol>
|
||||||
|
* <p>
|
||||||
|
* Each CRO instance is accessed by a single thread.
|
||||||
|
*/
|
||||||
|
public abstract class ChunkRenderOptimizer extends Namespaced {
|
||||||
|
|
||||||
public abstract void startRender(ChunkRender chunk);
|
/**
|
||||||
|
* The chunk that this CRO is currently working on.
|
||||||
|
*/
|
||||||
|
protected ChunkRender chunk = null;
|
||||||
|
|
||||||
public abstract void processBlock(
|
/**
|
||||||
BlockRender block,
|
* Creates a new CRO instance with the specified ID.
|
||||||
Vec3i posInChunk
|
*
|
||||||
);
|
* @param id the ID of this CRO
|
||||||
|
*/
|
||||||
|
public ChunkRenderOptimizer(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void processTile(
|
/**
|
||||||
TileRender tile,
|
* This method is invoked before a new chunk processing cycle begins to
|
||||||
Vec3i posInChunk,
|
* specify the chunk. When overriding, {@code super.setup(chunk)} must be
|
||||||
BlockFace face
|
* invoked.
|
||||||
);
|
*
|
||||||
|
* @param chunk the chunk that will be processed next
|
||||||
|
*/
|
||||||
|
public void setup(ChunkRender chunk) {
|
||||||
|
this.chunk = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract Shape endRender();
|
/**
|
||||||
|
* @return the chunk that this CRO is currently working on
|
||||||
|
*/
|
||||||
|
public ChunkRender getChunk() {
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets this CRO to a state in which a new chunk may be processed.
|
||||||
|
*/
|
||||||
|
public abstract void startRender();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that this CRO processes the provided block. This method may only
|
||||||
|
* be invoked between {@link #startRender()} and {@link #endRender()}. This
|
||||||
|
* method is only invoked once per block. This method is not necessarily
|
||||||
|
* invoked for each block.
|
||||||
|
*
|
||||||
|
* @param block a {@link BlockRender} instance describing the block.
|
||||||
|
* It corresponds to
|
||||||
|
* {@code getChunk().getBlock(blockInChunk)}.
|
||||||
|
* @param blockInChunk the position of the block
|
||||||
|
*/
|
||||||
|
public abstract void addBlock(BlockRender block, Vec3i blockInChunk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that this CRO processes the provided tile. This method may only
|
||||||
|
* be invoked between {@link #startRender()} and {@link #endRender()}. This
|
||||||
|
* method is only invoked once per tile. This method is not necessarily
|
||||||
|
* invoked for each tile. When multiple tiles in a tile stack are requested,
|
||||||
|
* this method is invoked for lower tiles first.
|
||||||
|
*
|
||||||
|
* @param tile a {@link BlockRender} instance describing the tile
|
||||||
|
* @param blockInChunk the position of the block that the tile belongs to
|
||||||
|
* @param blockFace the face that the tile belongs to
|
||||||
|
*/
|
||||||
|
public abstract void addTile(TileRender tile, Vec3i blockInChunk, BlockFace blockFace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the CRO assembles and outputs its model. This method may
|
||||||
|
* only be invoked after {@link #startRender()}.
|
||||||
|
*
|
||||||
|
* @return the assembled {@link Renderable}.
|
||||||
|
*/
|
||||||
|
public abstract Renderable endRender();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,283 +0,0 @@
|
|||||||
/*
|
|
||||||
* Progressia
|
|
||||||
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package ru.windcorp.progressia.client.world.cro;
|
|
||||||
|
|
||||||
import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK;
|
|
||||||
import static ru.windcorp.progressia.common.world.block.BlockFace.BLOCK_FACE_COUNT;
|
|
||||||
import static ru.windcorp.progressia.common.world.generic.GenericTileStack.TILES_PER_FACE;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
|
||||||
import glm.vec._3.i.Vec3i;
|
|
||||||
import ru.windcorp.progressia.client.graphics.Colors;
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Face;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Faces;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
|
||||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
|
||||||
import ru.windcorp.progressia.client.world.ChunkRender;
|
|
||||||
import ru.windcorp.progressia.client.world.block.BlockRender;
|
|
||||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
|
||||||
|
|
||||||
public class ChunkRenderOptimizerCube extends ChunkRenderOptimizer {
|
|
||||||
|
|
||||||
public static interface OpaqueCube {
|
|
||||||
public Texture getTexture(BlockFace face);
|
|
||||||
|
|
||||||
public boolean isOpaque(BlockFace face);
|
|
||||||
|
|
||||||
public boolean isBlockOpaque();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface OpaqueSurface {
|
|
||||||
public Texture getTexture(BlockFace face);
|
|
||||||
|
|
||||||
public boolean isOpaque(BlockFace face);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BlockInfo {
|
|
||||||
OpaqueCube block;
|
|
||||||
final FaceInfo[] faces = new FaceInfo[BLOCK_FACE_COUNT];
|
|
||||||
|
|
||||||
{
|
|
||||||
for (int i = 0; i < faces.length; ++i) {
|
|
||||||
faces[i] = new FaceInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class FaceInfo {
|
|
||||||
static final int NO_OPAQUE_TILES = -1;
|
|
||||||
|
|
||||||
int topOpaqueTile = NO_OPAQUE_TILES;
|
|
||||||
final OpaqueSurface[] tiles = new OpaqueSurface[TILES_PER_FACE];
|
|
||||||
int tileCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final BlockInfo[][][] data = new BlockInfo[BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK];
|
|
||||||
|
|
||||||
{
|
|
||||||
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
|
|
||||||
for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) {
|
|
||||||
for (int z = 0; z < BLOCKS_PER_CHUNK; ++z) {
|
|
||||||
data[x][y][z] = new BlockInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startRender(ChunkRender chunk) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void processBlock(BlockRender block, Vec3i pos) {
|
|
||||||
if (!(block instanceof OpaqueCube))
|
|
||||||
return;
|
|
||||||
OpaqueCube opaqueCube = (OpaqueCube) block;
|
|
||||||
addBlock(pos, opaqueCube);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void processTile(TileRender tile, Vec3i pos, BlockFace face) {
|
|
||||||
if (!(tile instanceof OpaqueSurface))
|
|
||||||
return;
|
|
||||||
OpaqueSurface opaqueTile = (OpaqueSurface) tile;
|
|
||||||
addTile(pos, face, opaqueTile);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addBlock(Vec3i pos, OpaqueCube cube) {
|
|
||||||
getBlock(pos).block = cube;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTile(Vec3i pos, BlockFace face, OpaqueSurface opaqueTile) {
|
|
||||||
FaceInfo faceInfo = getFace(pos, face);
|
|
||||||
|
|
||||||
int index = faceInfo.tileCount;
|
|
||||||
faceInfo.tileCount++;
|
|
||||||
|
|
||||||
faceInfo.tiles[index] = opaqueTile;
|
|
||||||
|
|
||||||
if (opaqueTile.isOpaque(face)) {
|
|
||||||
faceInfo.topOpaqueTile = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BlockInfo getBlock(Vec3i cursor) {
|
|
||||||
return data[cursor.x][cursor.y][cursor.z];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FaceInfo getFace(Vec3i cursor, BlockFace face) {
|
|
||||||
return getBlock(cursor).faces[face.getId()];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Shape endRender() {
|
|
||||||
|
|
||||||
Collection<Face> shapeFaces = new ArrayList<>(
|
|
||||||
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3
|
|
||||||
);
|
|
||||||
|
|
||||||
Vec3i cursor = new Vec3i();
|
|
||||||
|
|
||||||
for (cursor.x = 0; cursor.x < BLOCKS_PER_CHUNK; ++cursor.x) {
|
|
||||||
for (cursor.y = 0; cursor.y < BLOCKS_PER_CHUNK; ++cursor.y) {
|
|
||||||
for (cursor.z = 0; cursor.z < BLOCKS_PER_CHUNK; ++cursor.z) {
|
|
||||||
processInnerFaces(cursor, shapeFaces::add);
|
|
||||||
processOuterFaces(cursor, shapeFaces::add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Shape(
|
|
||||||
Usage.STATIC,
|
|
||||||
WorldRenderProgram.getDefault(),
|
|
||||||
shapeFaces.toArray(new Face[shapeFaces.size()])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processOuterFaces(
|
|
||||||
Vec3i cursor,
|
|
||||||
Consumer<Face> output
|
|
||||||
) {
|
|
||||||
for (BlockFace face : BlockFace.getFaces()) {
|
|
||||||
if (!shouldRenderOuterFace(cursor, face))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vec3 faceOrigin = new Vec3(cursor.x, cursor.y, cursor.z);
|
|
||||||
Vec3 offset = new Vec3(face.getVector().x, face.getVector().y, face.getVector().z).mul(1f / 128);
|
|
||||||
|
|
||||||
FaceInfo info = getFace(cursor, face);
|
|
||||||
|
|
||||||
if (info.topOpaqueTile == FaceInfo.NO_OPAQUE_TILES) {
|
|
||||||
OpaqueCube block = getBlock(cursor).block;
|
|
||||||
|
|
||||||
if (block != null) {
|
|
||||||
addFace(
|
|
||||||
faceOrigin,
|
|
||||||
face,
|
|
||||||
getBlock(cursor).block.getTexture(face),
|
|
||||||
output
|
|
||||||
);
|
|
||||||
|
|
||||||
faceOrigin.add(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int startLayer = info.topOpaqueTile;
|
|
||||||
if (startLayer == FaceInfo.NO_OPAQUE_TILES) {
|
|
||||||
startLayer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int layer = startLayer; layer < info.tileCount; ++layer) {
|
|
||||||
addFace(
|
|
||||||
faceOrigin,
|
|
||||||
face,
|
|
||||||
info.tiles[layer].getTexture(face),
|
|
||||||
output
|
|
||||||
);
|
|
||||||
|
|
||||||
faceOrigin.add(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addFace(
|
|
||||||
Vec3 cursor,
|
|
||||||
BlockFace face,
|
|
||||||
Texture texture,
|
|
||||||
Consumer<Face> output
|
|
||||||
) {
|
|
||||||
if (texture == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
output.accept(
|
|
||||||
Faces.createBlockFace(
|
|
||||||
WorldRenderProgram.getDefault(),
|
|
||||||
texture,
|
|
||||||
Colors.WHITE,
|
|
||||||
new Vec3(cursor),
|
|
||||||
face,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldRenderOuterFace(Vec3i cursor, BlockFace face) {
|
|
||||||
cursor.add(face.getVector());
|
|
||||||
try {
|
|
||||||
|
|
||||||
// TODO handle neighboring chunks properly
|
|
||||||
if (!isInBounds(cursor))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
OpaqueCube adjacent = getBlock(cursor).block;
|
|
||||||
|
|
||||||
if (adjacent == null)
|
|
||||||
return true;
|
|
||||||
if (adjacent.isOpaque(face))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
cursor.sub(face.getVector());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processInnerFaces(
|
|
||||||
Vec3i cursor,
|
|
||||||
Consumer<Face> output
|
|
||||||
) {
|
|
||||||
// if (block.isBlockOpaque()) return;
|
|
||||||
//
|
|
||||||
// for (BlockFace face : BlockFace.getFaces()) {
|
|
||||||
//
|
|
||||||
// Texture texture = block.getTexture(face);
|
|
||||||
// if (texture == null) continue;
|
|
||||||
//
|
|
||||||
// output.accept(Faces.createBlockFace(
|
|
||||||
// WorldRenderProgram.getDefault(),
|
|
||||||
// texture,
|
|
||||||
// COLOR_MULTIPLIER,
|
|
||||||
// new Vec3(cursor.x, cursor.y, cursor.z),
|
|
||||||
// face,
|
|
||||||
// true
|
|
||||||
// ));
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInBounds(Vec3i cursor) {
|
|
||||||
return isInBounds(cursor.x) &&
|
|
||||||
isInBounds(cursor.y) &&
|
|
||||||
isInBounds(cursor.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInBounds(int c) {
|
|
||||||
return c >= 0 && c < BLOCKS_PER_CHUNK;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -18,28 +18,17 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.world.cro;
|
package ru.windcorp.progressia.client.world.cro;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import ru.windcorp.progressia.common.util.namespaces.NamespacedFactoryRegistry;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
public class ChunkRenderOptimizerRegistry extends NamespacedFactoryRegistry<ChunkRenderOptimizer> {
|
||||||
|
|
||||||
public abstract class ChunkRenderOptimizerSupplier extends Namespaced {
|
private static final ChunkRenderOptimizerRegistry INSTANCE = new ChunkRenderOptimizerRegistry();
|
||||||
|
|
||||||
public ChunkRenderOptimizerSupplier(String id) {
|
/**
|
||||||
super(id);
|
* @return the instance
|
||||||
}
|
*/
|
||||||
|
public static ChunkRenderOptimizerRegistry getInstance() {
|
||||||
public abstract ChunkRenderOptimizer createOptimizer();
|
return INSTANCE;
|
||||||
|
|
||||||
public static ChunkRenderOptimizerSupplier of(
|
|
||||||
String id,
|
|
||||||
Supplier<ChunkRenderOptimizer> supplier
|
|
||||||
) {
|
|
||||||
return new ChunkRenderOptimizerSupplier(id) {
|
|
||||||
@Override
|
|
||||||
public ChunkRenderOptimizer createOptimizer() {
|
|
||||||
return supplier.get();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,396 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.windcorp.progressia.client.world.cro;
|
||||||
|
|
||||||
|
import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK;
|
||||||
|
import static ru.windcorp.progressia.common.world.block.BlockFace.BLOCK_FACE_COUNT;
|
||||||
|
import static ru.windcorp.progressia.common.world.generic.GenericTileStack.TILES_PER_FACE;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Face;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||||
|
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||||
|
import ru.windcorp.progressia.client.world.ChunkRender;
|
||||||
|
import ru.windcorp.progressia.client.world.block.BlockRender;
|
||||||
|
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
|
public class ChunkRenderOptimizerSurface extends ChunkRenderOptimizer {
|
||||||
|
|
||||||
|
private static final float OVERLAY_OFFSET = 1 / 128f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common interface to objects that can provide optimizeable surfaces.
|
||||||
|
* This is an internal interface; use {@link BlockOptimizedSurface} or
|
||||||
|
* {@link TileOptimizedSurface} instead.
|
||||||
|
*/
|
||||||
|
private static interface OptimizedSurface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and outputs a set of faces that correspond to this surface.
|
||||||
|
* The coordinates of the face vertices must be in chunk coordinate
|
||||||
|
* system.
|
||||||
|
*
|
||||||
|
* @param chunk the chunk that contains the requested face
|
||||||
|
* @param blockInChunk the block in chunk
|
||||||
|
* @param blockFace the requested face
|
||||||
|
* @param inner whether this face should be visible from inside
|
||||||
|
* ({@code true}) or outside ({@code false})
|
||||||
|
* @param output a consumer that the created faces must be given
|
||||||
|
* to
|
||||||
|
* @param offset an additional offset that must be applied to all
|
||||||
|
* vertices
|
||||||
|
*/
|
||||||
|
void getFaces(
|
||||||
|
ChunkData chunk,
|
||||||
|
Vec3i blockInChunk,
|
||||||
|
BlockFace blockFace,
|
||||||
|
boolean inner,
|
||||||
|
Consumer<Face> output,
|
||||||
|
Vec3 offset /* kostyl 156% */
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the opacity of the surface identified by the provided
|
||||||
|
* {@link BlockFace}.
|
||||||
|
* Opaque surfaces prevent surfaces behind them from being included in
|
||||||
|
* chunk models.
|
||||||
|
*
|
||||||
|
* @param blockFace the face to query
|
||||||
|
* @return {@code true} iff the surface is opaque.
|
||||||
|
*/
|
||||||
|
boolean isOpaque(BlockFace blockFace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A block that can be optimized by {@link ChunkRenderOptimizerSurface}.
|
||||||
|
*/
|
||||||
|
public static interface BlockOptimizedSurface extends OptimizedSurface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the opacity of the block. Opaque blocks do not expect that
|
||||||
|
* the camera can be inside them. Opaque blocks prevent surfaces that
|
||||||
|
* face them
|
||||||
|
* from being included in chunk models.
|
||||||
|
*
|
||||||
|
* @return {@code true} iff the block is opaque.
|
||||||
|
*/
|
||||||
|
boolean isBlockOpaque();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tile that can be optimized by {@link ChunkRenderOptimizerSurface}.
|
||||||
|
*/
|
||||||
|
public static interface TileOptimizedSurface extends OptimizedSurface {
|
||||||
|
// Empty for now
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BlockInfo {
|
||||||
|
BlockOptimizedSurface block;
|
||||||
|
final FaceInfo[] faces = new FaceInfo[BLOCK_FACE_COUNT];
|
||||||
|
|
||||||
|
{
|
||||||
|
for (int i = 0; i < faces.length; ++i) {
|
||||||
|
faces[i] = new FaceInfo(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FaceInfo {
|
||||||
|
static final int BLOCK_LAYER = -1;
|
||||||
|
|
||||||
|
final BlockInfo block;
|
||||||
|
|
||||||
|
int topOpaqueSurface = BLOCK_LAYER;
|
||||||
|
int bottomOpaqueSurface = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
final TileOptimizedSurface[] tiles = new TileOptimizedSurface[TILES_PER_FACE];
|
||||||
|
int tileCount = 0;
|
||||||
|
|
||||||
|
FaceInfo(BlockInfo block) {
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
OptimizedSurface getSurface(int layer) {
|
||||||
|
return layer == BLOCK_LAYER ? block.block : tiles[layer];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BlockInfo[][][] data = new BlockInfo[BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK][BLOCKS_PER_CHUNK];
|
||||||
|
|
||||||
|
public ChunkRenderOptimizerSurface(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startRender() {
|
||||||
|
for (int x = 0; x < BLOCKS_PER_CHUNK; ++x) {
|
||||||
|
for (int y = 0; y < BLOCKS_PER_CHUNK; ++y) {
|
||||||
|
for (int z = 0; z < BLOCKS_PER_CHUNK; ++z) {
|
||||||
|
data[x][y][z] = new BlockInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addBlock(BlockRender block, Vec3i pos) {
|
||||||
|
if (!(block instanceof BlockOptimizedSurface))
|
||||||
|
return;
|
||||||
|
|
||||||
|
BlockOptimizedSurface bos = (BlockOptimizedSurface) block;
|
||||||
|
addBlock(pos, bos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTile(TileRender tile, Vec3i pos, BlockFace face) {
|
||||||
|
if (!(tile instanceof TileOptimizedSurface))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TileOptimizedSurface tos = (TileOptimizedSurface) tile;
|
||||||
|
addTile(pos, face, tos);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addBlock(Vec3i pos, BlockOptimizedSurface block) {
|
||||||
|
getBlock(pos).block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTile(Vec3i pos, BlockFace face, TileOptimizedSurface tile) {
|
||||||
|
FaceInfo faceInfo = getFace(pos, face);
|
||||||
|
|
||||||
|
int index = faceInfo.tileCount;
|
||||||
|
faceInfo.tileCount++;
|
||||||
|
|
||||||
|
faceInfo.tiles[index] = tile;
|
||||||
|
|
||||||
|
if (tile.isOpaque(face)) {
|
||||||
|
faceInfo.topOpaqueSurface = index;
|
||||||
|
|
||||||
|
if (faceInfo.bottomOpaqueSurface == FaceInfo.BLOCK_LAYER) {
|
||||||
|
faceInfo.bottomOpaqueSurface = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BlockInfo getBlock(Vec3i cursor) {
|
||||||
|
return data[cursor.x][cursor.y][cursor.z];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FaceInfo getFace(Vec3i cursor, BlockFace face) {
|
||||||
|
return getBlock(cursor).faces[face.getId()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Renderable endRender() {
|
||||||
|
Collection<Face> shapeFaces = new ArrayList<>(
|
||||||
|
BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * BLOCKS_PER_CHUNK * 3
|
||||||
|
);
|
||||||
|
|
||||||
|
Vec3i cursor = new Vec3i();
|
||||||
|
Consumer<Face> consumer = shapeFaces::add;
|
||||||
|
|
||||||
|
for (cursor.x = 0; cursor.x < BLOCKS_PER_CHUNK; ++cursor.x) {
|
||||||
|
for (cursor.y = 0; cursor.y < BLOCKS_PER_CHUNK; ++cursor.y) {
|
||||||
|
for (cursor.z = 0; cursor.z < BLOCKS_PER_CHUNK; ++cursor.z) {
|
||||||
|
processInnerFaces(cursor, consumer);
|
||||||
|
processOuterFaces(cursor, consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shapeFaces.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Shape(
|
||||||
|
Usage.STATIC,
|
||||||
|
WorldRenderProgram.getDefault(),
|
||||||
|
shapeFaces.toArray(new Face[shapeFaces.size()])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processOuterFaces(
|
||||||
|
Vec3i blockInChunk,
|
||||||
|
Consumer<Face> output
|
||||||
|
) {
|
||||||
|
for (BlockFace blockFace : BlockFace.getFaces()) {
|
||||||
|
processOuterFace(blockInChunk, blockFace, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processOuterFace(Vec3i blockInChunk, BlockFace blockFace, Consumer<Face> output) {
|
||||||
|
if (!shouldRenderOuterFace(blockInChunk, blockFace))
|
||||||
|
return;
|
||||||
|
|
||||||
|
FaceInfo info = getFace(blockInChunk, blockFace);
|
||||||
|
|
||||||
|
if (info.tileCount == 0 && info.block.block == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vec3 faceOrigin = new Vec3(blockInChunk.x, blockInChunk.y, blockInChunk.z);
|
||||||
|
Vec3 offset = new Vec3(blockFace.getFloatVector()).mul(OVERLAY_OFFSET);
|
||||||
|
|
||||||
|
for (
|
||||||
|
int layer = info.topOpaqueSurface;
|
||||||
|
layer < info.tileCount;
|
||||||
|
++layer
|
||||||
|
) {
|
||||||
|
OptimizedSurface surface = info.getSurface(layer);
|
||||||
|
if (surface == null)
|
||||||
|
continue; // layer may be BLOCK_LAYER, then block may be null
|
||||||
|
|
||||||
|
surface.getFaces(chunk.getData(), blockInChunk, blockFace, false, output, faceOrigin);
|
||||||
|
|
||||||
|
faceOrigin.add(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processInnerFaces(
|
||||||
|
Vec3i blockInChunk,
|
||||||
|
Consumer<Face> output
|
||||||
|
) {
|
||||||
|
for (BlockFace blockFace : BlockFace.getFaces()) {
|
||||||
|
processInnerFace(blockInChunk, blockFace, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processInnerFace(Vec3i blockInChunk, BlockFace blockFace, Consumer<Face> output) {
|
||||||
|
if (!shouldRenderInnerFace(blockInChunk, blockFace))
|
||||||
|
return;
|
||||||
|
|
||||||
|
FaceInfo info = getFace(blockInChunk, blockFace);
|
||||||
|
|
||||||
|
if (info.tileCount == 0 && info.block.block == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vec3 faceOrigin = new Vec3(blockInChunk.x, blockInChunk.y, blockInChunk.z);
|
||||||
|
Vec3 offset = new Vec3(blockFace.getFloatVector()).mul(OVERLAY_OFFSET);
|
||||||
|
|
||||||
|
for (
|
||||||
|
int layer = FaceInfo.BLOCK_LAYER;
|
||||||
|
layer <= info.bottomOpaqueSurface && layer < info.tileCount;
|
||||||
|
++layer
|
||||||
|
) {
|
||||||
|
OptimizedSurface surface = info.getSurface(layer);
|
||||||
|
if (surface == null)
|
||||||
|
continue; // layer may be BLOCK_LAYER, then block may be null
|
||||||
|
|
||||||
|
surface.getFaces(chunk.getData(), blockInChunk, blockFace, true, output, faceOrigin);
|
||||||
|
|
||||||
|
faceOrigin.add(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRenderOuterFace(Vec3i blockInChunk, BlockFace face) {
|
||||||
|
blockInChunk.add(face.getVector());
|
||||||
|
try {
|
||||||
|
return shouldRenderWhenFacing(blockInChunk, face);
|
||||||
|
} finally {
|
||||||
|
blockInChunk.sub(face.getVector());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRenderInnerFace(Vec3i blockInChunk, BlockFace face) {
|
||||||
|
return shouldRenderWhenFacing(blockInChunk, face);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRenderWhenFacing(Vec3i blockInChunk, BlockFace face) {
|
||||||
|
if (chunk.containsBiC(blockInChunk)) {
|
||||||
|
return shouldRenderWhenFacingLocal(blockInChunk, face);
|
||||||
|
} else {
|
||||||
|
return shouldRenderWhenFacingNeighbor(blockInChunk, face);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRenderWhenFacingLocal(Vec3i blockInChunk, BlockFace face) {
|
||||||
|
BlockOptimizedSurface block = getBlock(blockInChunk).block;
|
||||||
|
|
||||||
|
if (block == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (block.isOpaque(face)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRenderWhenFacingNeighbor(Vec3i blockInLocalChunk, BlockFace face) {
|
||||||
|
Vec3i blockInChunk = Vectors.grab3i().set(blockInLocalChunk.x, blockInLocalChunk.y, blockInLocalChunk.z);
|
||||||
|
Vec3i chunkPos = Vectors.grab3i().set(chunk.getX(), chunk.getY(), chunk.getZ());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Determine blockInChunk and chunkPos
|
||||||
|
if (blockInLocalChunk.x == -1) {
|
||||||
|
blockInChunk.x = BLOCKS_PER_CHUNK - 1;
|
||||||
|
chunkPos.x -= 1;
|
||||||
|
} else if (blockInLocalChunk.x == BLOCKS_PER_CHUNK) {
|
||||||
|
blockInChunk.x = 0;
|
||||||
|
chunkPos.x += 1;
|
||||||
|
} else if (blockInLocalChunk.y == -1) {
|
||||||
|
blockInChunk.y = BLOCKS_PER_CHUNK - 1;
|
||||||
|
chunkPos.y -= 1;
|
||||||
|
} else if (blockInLocalChunk.y == BLOCKS_PER_CHUNK) {
|
||||||
|
blockInChunk.y = 0;
|
||||||
|
chunkPos.y += 1;
|
||||||
|
} else if (blockInLocalChunk.z == -1) {
|
||||||
|
blockInChunk.z = BLOCKS_PER_CHUNK - 1;
|
||||||
|
chunkPos.z -= 1;
|
||||||
|
} else if (blockInLocalChunk.z == BLOCKS_PER_CHUNK) {
|
||||||
|
blockInChunk.z = 0;
|
||||||
|
chunkPos.z += 1;
|
||||||
|
} else {
|
||||||
|
throw new AssertionError(
|
||||||
|
"Requested incorrent neighbor ("
|
||||||
|
+ blockInLocalChunk.x + "; "
|
||||||
|
+ blockInLocalChunk.y + "; "
|
||||||
|
+ blockInLocalChunk.z + ")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkRender chunk = this.chunk.getWorld().getChunk(chunkPos);
|
||||||
|
if (chunk == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BlockRender block = chunk.getBlock(blockInChunk);
|
||||||
|
if (!(block instanceof BlockOptimizedSurface))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
BlockOptimizedSurface bos = (BlockOptimizedSurface) block;
|
||||||
|
if (!bos.isOpaque(face))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
Vectors.release(chunkPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -19,9 +19,11 @@
|
|||||||
package ru.windcorp.progressia.client.world.tile;
|
package ru.windcorp.progressia.client.world.tile;
|
||||||
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizer;
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
import ru.windcorp.progressia.common.world.generic.GenericTile;
|
import ru.windcorp.progressia.common.world.generic.GenericTile;
|
||||||
|
|
||||||
@ -37,7 +39,7 @@ public class TileRender extends Namespaced implements GenericTile {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Renderable createRenderable(BlockFace face) {
|
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace face) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,19 +18,10 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.world.tile;
|
package ru.windcorp.progressia.client.world.tile;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
|
||||||
import ru.windcorp.progressia.client.graphics.Colors;
|
|
||||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Faces;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerCube.OpaqueSurface;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
public class TileRenderGrass extends TileRender implements OpaqueSurface {
|
public class TileRenderGrass extends TileRenderSurface {
|
||||||
|
|
||||||
private final Texture topTexture;
|
private final Texture topTexture;
|
||||||
private final Texture sideTexture;
|
private final Texture sideTexture;
|
||||||
@ -55,27 +46,4 @@ public class TileRenderGrass extends TileRender implements OpaqueSurface {
|
|||||||
return face == BlockFace.TOP;
|
return face == BlockFace.TOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Renderable createRenderable(BlockFace face) {
|
|
||||||
ShapeRenderProgram program = WorldRenderProgram.getDefault();
|
|
||||||
|
|
||||||
return new Shape(
|
|
||||||
Usage.STATIC,
|
|
||||||
WorldRenderProgram.getDefault(),
|
|
||||||
Faces.createBlockFace(
|
|
||||||
program,
|
|
||||||
getTexture(face),
|
|
||||||
Colors.WHITE,
|
|
||||||
new Vec3(0, 0, 0),
|
|
||||||
face,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsOwnRenderable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.client.world.tile;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.EmptyModel;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
|
public class TileRenderNone extends TileRender {
|
||||||
|
|
||||||
|
public TileRenderNone(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace face) {
|
||||||
|
return EmptyModel.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsOwnRenderable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -18,19 +18,25 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.client.world.tile;
|
package ru.windcorp.progressia.client.world.tile;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import glm.vec._4.Vec4;
|
||||||
import ru.windcorp.progressia.client.graphics.Colors;
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
import ru.windcorp.progressia.client.graphics.backend.Usage;
|
||||||
|
import ru.windcorp.progressia.client.graphics.model.Face;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Faces;
|
import ru.windcorp.progressia.client.graphics.model.Faces;
|
||||||
import ru.windcorp.progressia.client.graphics.model.Shape;
|
import ru.windcorp.progressia.client.graphics.model.Shape;
|
||||||
import ru.windcorp.progressia.client.graphics.model.ShapeRenderProgram;
|
|
||||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||||
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram;
|
||||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerCube.OpaqueSurface;
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface.TileOptimizedSurface;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
public abstract class TileRenderSurface extends TileRender implements OpaqueSurface {
|
public abstract class TileRenderSurface extends TileRender implements TileOptimizedSurface {
|
||||||
|
|
||||||
private final Texture texture;
|
private final Texture texture;
|
||||||
|
|
||||||
@ -38,27 +44,52 @@ public abstract class TileRenderSurface extends TileRender implements OpaqueSurf
|
|||||||
super(id);
|
super(id);
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TileRenderSurface(String id) {
|
||||||
|
this(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public Texture getTexture(BlockFace blockFace) {
|
||||||
public Texture getTexture(BlockFace face) {
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vec4 getColorMultiplier(BlockFace blockFace) {
|
||||||
|
return Colors.WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void getFaces(
|
||||||
|
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||||
|
boolean inner,
|
||||||
|
Consumer<Face> output,
|
||||||
|
Vec3 offset
|
||||||
|
) {
|
||||||
|
output.accept(createFace(chunk, blockInChunk, blockFace, inner, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Face createFace(
|
||||||
|
ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace,
|
||||||
|
boolean inner,
|
||||||
|
Vec3 offset
|
||||||
|
) {
|
||||||
|
return Faces.createBlockFace(
|
||||||
|
WorldRenderProgram.getDefault(),
|
||||||
|
getTexture(blockFace),
|
||||||
|
getColorMultiplier(blockFace),
|
||||||
|
offset,
|
||||||
|
blockFace,
|
||||||
|
inner
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Renderable createRenderable(BlockFace face) {
|
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace) {
|
||||||
ShapeRenderProgram program = WorldRenderProgram.getDefault();
|
|
||||||
|
|
||||||
return new Shape(
|
return new Shape(
|
||||||
Usage.STATIC,
|
Usage.STATIC,
|
||||||
WorldRenderProgram.getDefault(),
|
WorldRenderProgram.getDefault(),
|
||||||
Faces.createBlockFace(
|
|
||||||
program,
|
createFace(chunk, blockInChunk, blockFace, false, Vectors.ZERO_3),
|
||||||
getTexture(face),
|
createFace(chunk, blockInChunk, blockFace, true, Vectors.ZERO_3)
|
||||||
Colors.WHITE,
|
|
||||||
new Vec3(0, 0, 0),
|
|
||||||
face,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.common.resource;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.Progressia;
|
||||||
|
|
||||||
|
public class ClasspathResourceReader implements ResourceReader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream read(String name) {
|
||||||
|
return Progressia.class.getClassLoader().getResourceAsStream(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.common.resource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class FilesystemResourceReader implements ResourceReader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream read(String name) {
|
||||||
|
try {
|
||||||
|
return Files.newInputStream(Paths.get(name));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,19 +30,24 @@ import org.lwjgl.BufferUtils;
|
|||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.common.io.CharStreams;
|
||||||
|
|
||||||
import ru.windcorp.progressia.Progressia;
|
|
||||||
import ru.windcorp.progressia.common.util.Named;
|
import ru.windcorp.progressia.common.util.Named;
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
public class Resource extends Named {
|
public class Resource extends Named {
|
||||||
|
|
||||||
|
private final ResourceReader resourceReader;
|
||||||
|
|
||||||
public Resource(String name) {
|
public Resource(String name, ResourceReader resourceReader) {
|
||||||
super(name);
|
super(name);
|
||||||
|
this.resourceReader = resourceReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
// TODO Do proper resource lookup
|
return getResourceReader().read(getName());
|
||||||
return Progressia.class.getClassLoader().getResourceAsStream(getName());
|
}
|
||||||
|
|
||||||
|
public ResourceReader getResourceReader() {
|
||||||
|
return resourceReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reader getReader() {
|
public Reader getReader() {
|
||||||
|
@ -19,9 +19,16 @@
|
|||||||
package ru.windcorp.progressia.common.resource;
|
package ru.windcorp.progressia.common.resource;
|
||||||
|
|
||||||
public class ResourceManager {
|
public class ResourceManager {
|
||||||
|
|
||||||
|
private static final ResourceReader CLASSPATH_READER = new ClasspathResourceReader();
|
||||||
|
private static final ResourceReader FILESYSTEM_READER = new FilesystemResourceReader();
|
||||||
|
|
||||||
public static Resource getResource(String name) {
|
public static Resource getResource(String name) {
|
||||||
return new Resource(name);
|
return new Resource(name, CLASSPATH_READER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Resource getFileResource(String name) {
|
||||||
|
return new Resource(name, FILESYSTEM_READER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Resource getTextureResource(String name) {
|
public static Resource getTextureResource(String name) {
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.common.resource;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface ResourceReader {
|
||||||
|
|
||||||
|
InputStream read(String name);
|
||||||
|
|
||||||
|
}
|
@ -203,13 +203,12 @@ public class CrashReports {
|
|||||||
if (provider == null)
|
if (provider == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
addSeparator(output);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, String> buf = new HashMap<>();
|
Map<String, String> buf = new HashMap<>();
|
||||||
provider.provideContext(buf);
|
provider.provideContext(buf);
|
||||||
|
|
||||||
if (!buf.isEmpty()) {
|
if (!buf.isEmpty()) {
|
||||||
|
addSeparator(output);
|
||||||
output.append(StringUtil.center(provider.getName(), 80)).append("\n");
|
output.append(StringUtil.center(provider.getName(), 80)).append("\n");
|
||||||
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
||||||
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||||
|
@ -26,12 +26,12 @@ public class RAMContextProvider implements ContextProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void provideContext(Map<String, String> output) {
|
public void provideContext(Map<String, String> output) {
|
||||||
output.put("Max Memory", Long.toString(Runtime.getRuntime().maxMemory() / 1024 / 1024) + " MB");
|
output.put("Max Memory", Runtime.getRuntime().maxMemory() / 1024 / 1024 + " MB");
|
||||||
output.put("Total Memory", Long.toString(Runtime.getRuntime().totalMemory() / 1024 / 1024) + " MB");
|
output.put("Total Memory", Runtime.getRuntime().totalMemory() / 1024 / 1024 + " MB");
|
||||||
output.put("Free Memory", Long.toString(Runtime.getRuntime().freeMemory() / 1024 / 1024) + " MB");
|
output.put("Free Memory", Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
|
||||||
output.put(
|
output.put(
|
||||||
"Used Memory",
|
"Used Memory",
|
||||||
Long.toString((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024)
|
(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024
|
||||||
+ " MB"
|
+ " MB"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -15,39 +15,27 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ru.windcorp.progressia.client.world.cro;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
package ru.windcorp.progressia.common.util.crash.providers;
|
||||||
import java.util.HashMap;
|
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.ContextProvider;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ChunkRenderOptimizers {
|
public class ScreenContextProvider implements ContextProvider {
|
||||||
|
|
||||||
private ChunkRenderOptimizers() {
|
@Override
|
||||||
|
public void provideContext(Map<String, String> output) {
|
||||||
|
if (GraphicsBackend.isGLFWInitialized()) {
|
||||||
|
output.put("Refresh rate", GraphicsBackend.getRefreshRate() + " Hz");
|
||||||
|
output.put("Size", GraphicsBackend.getFrameWidth() + "x" + GraphicsBackend.getFrameHeight());
|
||||||
|
output.put("Fullscreen", GraphicsBackend.isFullscreen() ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<String, ChunkRenderOptimizerSupplier> SUPPLIERS = new HashMap<>();
|
@Override
|
||||||
|
public String getName() {
|
||||||
static {
|
return "Screen Context Provider";
|
||||||
register(
|
|
||||||
ChunkRenderOptimizerSupplier.of(
|
|
||||||
"Default:OpaqueCube",
|
|
||||||
ChunkRenderOptimizerCube::new
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ChunkRenderOptimizerSupplier getSupplier(String id) {
|
|
||||||
return SUPPLIERS.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void register(ChunkRenderOptimizerSupplier supplier) {
|
|
||||||
SUPPLIERS.put(supplier.getId(), supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Collection<ChunkRenderOptimizerSupplier> getAllSuppliers() {
|
|
||||||
return SUPPLIERS.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -158,5 +158,9 @@ public class Coordinates {
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isOnChunkBorder(int blockInChunk) {
|
||||||
|
return blockInChunk == 0 || blockInChunk == BLOCKS_PER_CHUNK - 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,12 @@ import glm.vec._3.i.Vec3i;
|
|||||||
public class BlockRelation {
|
public class BlockRelation {
|
||||||
|
|
||||||
private final Vec3i vector = new Vec3i();
|
private final Vec3i vector = new Vec3i();
|
||||||
|
private final Vec3 floatVector = new Vec3();
|
||||||
private final Vec3 normalized = new Vec3();
|
private final Vec3 normalized = new Vec3();
|
||||||
|
|
||||||
public BlockRelation(int x, int y, int z) {
|
public BlockRelation(int x, int y, int z) {
|
||||||
vector.set(x, y, z);
|
vector.set(x, y, z);
|
||||||
|
floatVector.set(x, y, z);
|
||||||
normalized.set(x, y, z).normalize();
|
normalized.set(x, y, z).normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +43,10 @@ public class BlockRelation {
|
|||||||
public Vec3i getVector() {
|
public Vec3i getVector() {
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vec3 getFloatVector() {
|
||||||
|
return floatVector;
|
||||||
|
}
|
||||||
|
|
||||||
public Vec3 getNormalized() {
|
public Vec3 getNormalized() {
|
||||||
return normalized;
|
return normalized;
|
||||||
|
@ -91,6 +91,72 @@ public interface GenericChunk<Self extends GenericChunk<Self, B, T, TS>, B exten
|
|||||||
Vectors.release(v);
|
Vectors.release(v);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean isSurfaceBiC(Vec3i blockInChunk) {
|
||||||
|
int hits = 0;
|
||||||
|
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.x)) hits++;
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.y)) hits++;
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.z)) hits++;
|
||||||
|
|
||||||
|
return hits >= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isSurfaceBiW(Vec3i blockInWorld) {
|
||||||
|
Vec3i v = Vectors.grab3i();
|
||||||
|
|
||||||
|
v = Coordinates.getInWorld(getPosition(), Vectors.ZERO_3i, v);
|
||||||
|
v = blockInWorld.sub(v, v);
|
||||||
|
|
||||||
|
boolean result = isSurfaceBiC(v);
|
||||||
|
|
||||||
|
Vectors.release(v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isEdgeBiC(Vec3i blockInChunk) {
|
||||||
|
int hits = 0;
|
||||||
|
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.x)) hits++;
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.y)) hits++;
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.z)) hits++;
|
||||||
|
|
||||||
|
return hits >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isEdgeBiW(Vec3i blockInWorld) {
|
||||||
|
Vec3i v = Vectors.grab3i();
|
||||||
|
|
||||||
|
v = Coordinates.getInWorld(getPosition(), Vectors.ZERO_3i, v);
|
||||||
|
v = blockInWorld.sub(v, v);
|
||||||
|
|
||||||
|
boolean result = isEdgeBiC(v);
|
||||||
|
|
||||||
|
Vectors.release(v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isVertexBiC(Vec3i blockInChunk) {
|
||||||
|
int hits = 0;
|
||||||
|
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.x)) hits++;
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.y)) hits++;
|
||||||
|
if (Coordinates.isOnChunkBorder(blockInChunk.z)) hits++;
|
||||||
|
|
||||||
|
return hits == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isVertexBiW(Vec3i blockInWorld) {
|
||||||
|
Vec3i v = Vectors.grab3i();
|
||||||
|
|
||||||
|
v = Coordinates.getInWorld(getPosition(), Vectors.ZERO_3i, v);
|
||||||
|
v = blockInWorld.sub(v, v);
|
||||||
|
|
||||||
|
boolean result = isVertexBiC(v);
|
||||||
|
|
||||||
|
Vectors.release(v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
default void forEachBiC(Consumer<? super Vec3i> action) {
|
default void forEachBiC(Consumer<? super Vec3i> action) {
|
||||||
VectorUtil.iterateCuboid(
|
VectorUtil.iterateCuboid(
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ru.windcorp.progressia.server;
|
package ru.windcorp.progressia.server;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -183,6 +183,18 @@ public class Server {
|
|||||||
return this.serverThread.getTicker().getTPS();
|
return this.serverThread.getTicker().getTPS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the amount of ticks performed since the server has started. This
|
||||||
|
* value resets on shutdowns. The counter is incremented at the end of a
|
||||||
|
* tick.
|
||||||
|
*
|
||||||
|
* @return the number of times the world has finished a tick since the
|
||||||
|
* server has started.
|
||||||
|
*/
|
||||||
|
public long getUptimeTicks() {
|
||||||
|
return this.serverThread.getTicker().getUptimeTicks();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link WorldAccessor} object for this server. Use the
|
* Returns the {@link WorldAccessor} object for this server. Use the
|
||||||
* provided accessor to
|
* provided accessor to
|
||||||
|
@ -82,6 +82,7 @@ public class TickerCoordinator {
|
|||||||
private boolean isTickStartSet = false;
|
private boolean isTickStartSet = false;
|
||||||
private long tickStart = -1;
|
private long tickStart = -1;
|
||||||
private double tickLength = 1.0 / 20; // Do something about it
|
private double tickLength = 1.0 / 20; // Do something about it
|
||||||
|
private long ticks = 0;
|
||||||
|
|
||||||
private final Logger logger = LogManager.getLogger("Ticker Coordinator");
|
private final Logger logger = LogManager.getLogger("Ticker Coordinator");
|
||||||
|
|
||||||
@ -151,6 +152,10 @@ public class TickerCoordinator {
|
|||||||
public double getTPS() {
|
public double getTPS() {
|
||||||
return 1 / tickLength;
|
return 1 / tickLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getUptimeTicks() {
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
|
|
||||||
private void onTickStart() {
|
private void onTickStart() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@ -163,6 +168,10 @@ public class TickerCoordinator {
|
|||||||
|
|
||||||
tickStart = System.currentTimeMillis();
|
tickStart = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onTickEnd() {
|
||||||
|
ticks++;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* runOneTick & Friends
|
* runOneTick & Friends
|
||||||
@ -182,6 +191,8 @@ public class TickerCoordinator {
|
|||||||
logger.debug("Pass complete");
|
logger.debug("Pass complete");
|
||||||
passes++;
|
passes++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTickEnd();
|
||||||
|
|
||||||
logger.debug("Tick complete; run {} passes", passes);
|
logger.debug("Tick complete; run {} passes", passes);
|
||||||
|
|
||||||
@ -191,7 +202,7 @@ public class TickerCoordinator {
|
|||||||
// ...or almost silently
|
// ...or almost silently
|
||||||
logger.debug("Tick interrupted. WTF?");
|
logger.debug("Tick interrupted. WTF?");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
crash(e, "Coordinator");
|
throw CrashReports.report(e, "Coordinator");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public class LayerAbout extends GUILayer {
|
|||||||
new Label(
|
new Label(
|
||||||
"Version",
|
"Version",
|
||||||
font,
|
font,
|
||||||
new MutableStringLocalized("LayerAbout.Version").format("TechDemo")
|
new MutableStringLocalized("LayerAbout.Version").format("pre-alpha 1")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -18,22 +18,23 @@
|
|||||||
|
|
||||||
package ru.windcorp.progressia.test;
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
import glm.vec._4.Vec4;
|
import glm.vec._4.Vec4;
|
||||||
import ru.windcorp.progressia.client.Client;
|
import ru.windcorp.progressia.client.Client;
|
||||||
import ru.windcorp.progressia.client.ClientState;
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
import ru.windcorp.progressia.client.graphics.Colors;
|
import ru.windcorp.progressia.client.graphics.Colors;
|
||||||
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
|
||||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface;
|
||||||
import ru.windcorp.progressia.client.graphics.font.Font;
|
import ru.windcorp.progressia.client.graphics.font.Font;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Button;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Checkbox;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.DynamicLabel;
|
import ru.windcorp.progressia.client.graphics.gui.DynamicLabel;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
import ru.windcorp.progressia.client.graphics.gui.GUILayer;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.Button;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Label;
|
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
import ru.windcorp.progressia.client.graphics.gui.Panel;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.RadioButton;
|
||||||
|
import ru.windcorp.progressia.client.graphics.gui.RadioManager;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign;
|
||||||
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
import ru.windcorp.progressia.client.graphics.gui.layout.LayoutVertical;
|
||||||
import ru.windcorp.progressia.client.localization.Localizer;
|
import ru.windcorp.progressia.client.localization.Localizer;
|
||||||
@ -44,6 +45,11 @@ import ru.windcorp.progressia.common.util.dynstr.DynamicStrings;
|
|||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.ServerState;
|
import ru.windcorp.progressia.server.ServerState;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class LayerTestGUI extends GUILayer {
|
public class LayerTestGUI extends GUILayer {
|
||||||
|
|
||||||
public LayerTestGUI() {
|
public LayerTestGUI() {
|
||||||
@ -55,6 +61,54 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
Font font = new Font().withColor(color).deriveOutlined();
|
Font font = new Font().withColor(color).deriveOutlined();
|
||||||
|
|
||||||
TestPlayerControls tpc = TestPlayerControls.getInstance();
|
TestPlayerControls tpc = TestPlayerControls.getInstance();
|
||||||
|
|
||||||
|
Button disableButton = new Button("TestButton",
|
||||||
|
new Label("TestButtonLabel", new Font().withColor(Colors.BLACK), "I'm in TestGUI"),
|
||||||
|
b -> {b.setDisable(!b.isDisabled());}
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.addChild(disableButton);
|
||||||
|
|
||||||
|
panel.addChild(
|
||||||
|
new Button(
|
||||||
|
"TestButton2",
|
||||||
|
new Label("TestButtonLabel2", new Font().withColor(Colors.BLACK), "I enable the above button"),
|
||||||
|
b -> {disableButton.setDisable(false);}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.addChild(
|
||||||
|
new Checkbox(
|
||||||
|
"Checkbox1",
|
||||||
|
new Label("CheckboxLabel", font,"Reset"),
|
||||||
|
c -> {c.setText(
|
||||||
|
new Label("CheckboxLabel", font, "Set")
|
||||||
|
);},
|
||||||
|
c -> {c.setText(
|
||||||
|
new Label("CheckboxLabel", font, "Reset")
|
||||||
|
);}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
RadioManager manager = new RadioManager();
|
||||||
|
|
||||||
|
panel.addChild(
|
||||||
|
new RadioButton(
|
||||||
|
"Radio1,1",
|
||||||
|
new Label("RadioLabel1,1",font,"Option 1"),
|
||||||
|
rb -> {},
|
||||||
|
manager
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.addChild(
|
||||||
|
new RadioButton(
|
||||||
|
"Radio1,2",
|
||||||
|
new Label("RadioLabel1,2",font,"Option 2"),
|
||||||
|
rb -> {},
|
||||||
|
manager
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
panel.addChild(
|
panel.addChild(
|
||||||
new Label(
|
new Label(
|
||||||
@ -110,6 +164,22 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
panel.addChild(
|
||||||
|
new Label(
|
||||||
|
"FullscreenDisplay",
|
||||||
|
font,
|
||||||
|
tmp_dynFormat("LayerTestGUI.IsFullscreen", GraphicsBackend::isFullscreen)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
panel.addChild(
|
||||||
|
new Label(
|
||||||
|
"VSyncDisplay",
|
||||||
|
font,
|
||||||
|
tmp_dynFormat("LayerTestGUI.IsVSync", GraphicsBackend::isVSyncEnabled)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
panel.addChild(
|
panel.addChild(
|
||||||
new DynamicLabel(
|
new DynamicLabel(
|
||||||
"FPSDisplay",
|
"FPSDisplay",
|
||||||
@ -243,13 +313,127 @@ public class LayerTestGUI extends GUILayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class Counter {
|
||||||
|
|
||||||
|
private int DISPLAY_INERTIA = 200;
|
||||||
|
private long AVERAGE_TIME = 10000;
|
||||||
|
private long first_time;
|
||||||
|
|
||||||
|
private final long[] values;
|
||||||
|
private int size;
|
||||||
|
private int head;
|
||||||
|
|
||||||
|
private long lastUpdate;
|
||||||
|
|
||||||
|
public Counter(long averageTime, int maxTPS)
|
||||||
|
{
|
||||||
|
DISPLAY_INERTIA = (int) averageTime*maxTPS/1000;
|
||||||
|
AVERAGE_TIME = averageTime;
|
||||||
|
first_time = -1;
|
||||||
|
values = new long[DISPLAY_INERTIA];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(long value) {
|
||||||
|
if (first_time==-1)
|
||||||
|
{
|
||||||
|
first_time=System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
if (size == values.length) {
|
||||||
|
values[head] = value;
|
||||||
|
head++;
|
||||||
|
if (head == values.length)
|
||||||
|
head = 0;
|
||||||
|
} else {
|
||||||
|
values[size] = value;
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double average() {
|
||||||
|
double count=0;
|
||||||
|
long ctime = System.currentTimeMillis();
|
||||||
|
for (int i=0;i<size;i++)
|
||||||
|
{
|
||||||
|
if ((ctime-values[i])<AVERAGE_TIME)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((ctime-first_time)<AVERAGE_TIME)
|
||||||
|
{
|
||||||
|
if ((ctime-first_time)<10)
|
||||||
|
{
|
||||||
|
return 20.0;
|
||||||
|
}
|
||||||
|
return count/(ctime-first_time)*1000;
|
||||||
|
}
|
||||||
|
return count/AVERAGE_TIME*1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double update() {
|
||||||
|
long now = (long) (GraphicsInterface.getTime() / .05);
|
||||||
|
if (lastUpdate != now) {
|
||||||
|
lastUpdate = now;
|
||||||
|
add(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
return average();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Queue {
|
||||||
|
|
||||||
|
private static final int DISPLAY_INERTIA = 32;
|
||||||
|
private static final double UPDATE_INTERVAL = Units.get(50.0, "ms");
|
||||||
|
|
||||||
|
private final double[] values = new double[DISPLAY_INERTIA];
|
||||||
|
private int size;
|
||||||
|
private int head;
|
||||||
|
|
||||||
|
private long lastUpdate;
|
||||||
|
|
||||||
|
public void add(double value) {
|
||||||
|
if (size == values.length) {
|
||||||
|
values[head] = value;
|
||||||
|
head++;
|
||||||
|
if (head == values.length)
|
||||||
|
head = 0;
|
||||||
|
} else {
|
||||||
|
values[size] = value;
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double average() {
|
||||||
|
if (size == values.length && head!=0) {
|
||||||
|
return (values[head-1]-values[head])/DISPLAY_INERTIA*20;
|
||||||
|
} else if (head==0) {
|
||||||
|
return (values[size-1]-values[0])/DISPLAY_INERTIA*20;
|
||||||
|
} else {
|
||||||
|
return values[head-1]/size*20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double update(double value) {
|
||||||
|
long now = (long) (GraphicsInterface.getTime() / UPDATE_INTERVAL);
|
||||||
|
if (lastUpdate != now) {
|
||||||
|
lastUpdate = now;
|
||||||
|
add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return average();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static final Averager FPS_RECORD = new Averager();
|
private static final Averager FPS_RECORD = new Averager();
|
||||||
private static final Averager TPS_RECORD = new Averager();
|
private static final Queue TPS_RECORD = new Queue();
|
||||||
|
|
||||||
private static final Supplier<CharSequence> TPS_STRING = DynamicStrings.builder()
|
private static final Supplier<CharSequence> TPS_STRING = DynamicStrings.builder()
|
||||||
.addDyn(new MutableStringLocalized("LayerTestGUI.TPSDisplay"))
|
.addDyn(new MutableStringLocalized("LayerTestGUI.TPSDisplay"))
|
||||||
.addDyn(() -> TPS_RECORD.update(ServerState.getInstance().getTPS()), 5, 1)
|
.addDyn(() -> TPS_RECORD.update(ServerState.getInstance().getUptimeTicks()), 5, 1)
|
||||||
.buildSupplier();
|
.buildSupplier();
|
||||||
|
|
||||||
private static final Supplier<CharSequence> POS_STRING = DynamicStrings.builder()
|
private static final Supplier<CharSequence> POS_STRING = DynamicStrings.builder()
|
||||||
|
@ -33,12 +33,14 @@ import org.lwjgl.glfw.GLFW;
|
|||||||
import glm.vec._3.Vec3;
|
import glm.vec._3.Vec3;
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.ClientState;
|
import ru.windcorp.progressia.client.ClientState;
|
||||||
import ru.windcorp.progressia.client.audio.SoundEffect;
|
import ru.windcorp.progressia.client.audio.Sound;
|
||||||
import ru.windcorp.progressia.client.comms.controls.*;
|
import ru.windcorp.progressia.client.comms.controls.*;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
import ru.windcorp.progressia.client.graphics.input.KeyEvent;
|
||||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||||
import ru.windcorp.progressia.client.graphics.world.Selection;
|
import ru.windcorp.progressia.client.graphics.world.Selection;
|
||||||
import ru.windcorp.progressia.client.world.block.*;
|
import ru.windcorp.progressia.client.world.block.*;
|
||||||
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerRegistry;
|
||||||
|
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerSurface;
|
||||||
import ru.windcorp.progressia.client.world.entity.*;
|
import ru.windcorp.progressia.client.world.entity.*;
|
||||||
import ru.windcorp.progressia.client.world.tile.*;
|
import ru.windcorp.progressia.client.world.tile.*;
|
||||||
import ru.windcorp.progressia.common.collision.AABB;
|
import ru.windcorp.progressia.common.collision.AABB;
|
||||||
@ -285,6 +287,15 @@ public class TestContent {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
logic.register(ControlLogic.of("Test:PlaceTile", TestContent::onTilePlaceReceived));
|
logic.register(ControlLogic.of("Test:PlaceTile", TestContent::onTilePlaceReceived));
|
||||||
|
|
||||||
|
triggers.register(
|
||||||
|
ControlTriggers.localOf(
|
||||||
|
"Test:StartNextMusic",
|
||||||
|
KeyEvent.class,
|
||||||
|
TestMusicPlayer::startNextNow,
|
||||||
|
KeyMatcher.of(GLFW.GLFW_KEY_M).matcher()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void register(BlockData x) {
|
private static void register(BlockData x) {
|
||||||
@ -358,7 +369,7 @@ public class TestContent {
|
|||||||
|
|
||||||
private static void onBlockBreakTrigger(ControlData control) {
|
private static void onBlockBreakTrigger(ControlData control) {
|
||||||
((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock());
|
((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock());
|
||||||
SoundEffect sfx = new SoundEffect("Progressia:BlockDestroy");
|
Sound sfx = new Sound("Progressia:BlockDestroy");
|
||||||
sfx.setPosition(getSelection().getPoint());
|
sfx.setPosition(getSelection().getPoint());
|
||||||
sfx.setPitch((float) (Math.random() + 1 * 0.5));
|
sfx.setPitch((float) (Math.random() + 1 * 0.5));
|
||||||
sfx.play(false);
|
sfx.play(false);
|
||||||
@ -420,6 +431,7 @@ public class TestContent {
|
|||||||
|
|
||||||
private static void registerMisc() {
|
private static void registerMisc() {
|
||||||
ChunkIO.registerCodec(new TestChunkCodec());
|
ChunkIO.registerCodec(new TestChunkCodec());
|
||||||
|
ChunkRenderOptimizerRegistry.getInstance().register("Core:SurfaceOptimizer", ChunkRenderOptimizerSurface::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ public class TestEntityRenderHuman extends EntityRender {
|
|||||||
).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create()
|
).setOrigin(ox, oy, oz).setSize(sx, sy, sz).create()
|
||||||
);
|
);
|
||||||
|
|
||||||
return new StaticModel(b);
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -386,7 +386,7 @@ public class TestEntityRenderJavapony extends EntityRender {
|
|||||||
).setOrigin(16, -4, 8).setSize(4, 8, 4).create()
|
).setOrigin(16, -4, 8).setSize(4, 8, 4).create()
|
||||||
);
|
);
|
||||||
|
|
||||||
return new StaticModel(b);
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Renderable createLeg(
|
private static Renderable createLeg(
|
||||||
@ -427,7 +427,7 @@ public class TestEntityRenderJavapony extends EntityRender {
|
|||||||
).setOrigin(-8, -8, -32).setSize(16, 16, 32).create()
|
).setOrigin(-8, -8, -32).setSize(16, 16, 32).create()
|
||||||
);
|
);
|
||||||
|
|
||||||
return new StaticModel(b);
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
152
src/main/java/ru/windcorp/progressia/test/TestMusicPlayer.java
Normal file
152
src/main/java/ru/windcorp/progressia/test/TestMusicPlayer.java
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Progressia
|
||||||
|
* Copyright (C) 2020-2021 Wind Corporation and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.client.audio.AudioFormat;
|
||||||
|
import ru.windcorp.progressia.client.audio.AudioManager;
|
||||||
|
import ru.windcorp.progressia.client.audio.AudioRegistry;
|
||||||
|
import ru.windcorp.progressia.client.audio.Music;
|
||||||
|
import ru.windcorp.progressia.client.audio.Sound;
|
||||||
|
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
||||||
|
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
|
||||||
|
public class TestMusicPlayer implements Runnable {
|
||||||
|
|
||||||
|
private static final int MIN_SILENCE = 15 * 1000; // 15 seconds
|
||||||
|
private static final int MAX_SILENCE = 60 * 1000; // one minute
|
||||||
|
|
||||||
|
private static TestMusicPlayer instance = null;
|
||||||
|
|
||||||
|
private final List<SoundType> compositions = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Random random = new Random();
|
||||||
|
private long nextStart;
|
||||||
|
private Sound lastStarted = null;
|
||||||
|
|
||||||
|
public TestMusicPlayer() {
|
||||||
|
this.nextStart = System.currentTimeMillis();
|
||||||
|
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void start() {
|
||||||
|
Thread thread = new Thread(new TestMusicPlayer(), "Music Thread");
|
||||||
|
thread.setDaemon(true);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
loadCompositions();
|
||||||
|
|
||||||
|
if (compositions.isEmpty()) {
|
||||||
|
LogManager.getLogger().warn("No music found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
synchronized (this) {
|
||||||
|
while (true) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (nextStart > now) {
|
||||||
|
wait(nextStart - now);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LogManager.getLogger().warn("Received interrupt in music thread, terminating thread...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startNextComposition();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCompositions() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Path directory = Paths.get("music");
|
||||||
|
|
||||||
|
if (!Files.isDirectory(directory)) {
|
||||||
|
Files.createDirectories(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Path> it = Files.walk(directory).filter(Files::isRegularFile).iterator();
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
String file = it.next().toString();
|
||||||
|
if (!file.endsWith(".ogg") && !file.endsWith(".oga")) {
|
||||||
|
LogManager.getLogger().warn("Skipping " + file + ": not .ogg nor .oga");
|
||||||
|
}
|
||||||
|
|
||||||
|
String id = "Progressia:Music" + (i++);
|
||||||
|
|
||||||
|
AudioManager.loadSound(ResourceManager.getFileResource(file.toString()), id, AudioFormat.STEREO);
|
||||||
|
SoundType composition = AudioRegistry.getInstance().get(id);
|
||||||
|
compositions.add(composition);
|
||||||
|
|
||||||
|
LogManager.getLogger().info("Loaded " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw CrashReports.report(e, "Could not load music");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void startNextComposition() {
|
||||||
|
int index = random.nextInt(compositions.size());
|
||||||
|
SoundType composition = compositions.get(index);
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long durationInMs = (long) (composition.getDuration() * 1000);
|
||||||
|
long silence = random.nextInt(MAX_SILENCE - MIN_SILENCE) + MIN_SILENCE;
|
||||||
|
|
||||||
|
nextStart = now + durationInMs + silence;
|
||||||
|
|
||||||
|
lastStarted = new Music(composition);
|
||||||
|
lastStarted.play(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startNextNow() {
|
||||||
|
if (instance == null) return;
|
||||||
|
|
||||||
|
synchronized (instance) {
|
||||||
|
instance.nextStart = System.currentTimeMillis();
|
||||||
|
instance.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -187,6 +187,20 @@ public class TestPlayerControls {
|
|||||||
handleEscape();
|
handleEscape();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GLFW.GLFW_KEY_F11:
|
||||||
|
if (!event.isPress())
|
||||||
|
return false;
|
||||||
|
GraphicsInterface.makeFullscreen(!GraphicsBackend.isFullscreen());
|
||||||
|
updateGUI();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GLFW.GLFW_KEY_F12:
|
||||||
|
if (!event.isPress())
|
||||||
|
return false;
|
||||||
|
GraphicsBackend.setVSyncEnabled(!GraphicsBackend.isVSyncEnabled());
|
||||||
|
updateGUI();
|
||||||
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_KEY_F3:
|
case GLFW.GLFW_KEY_F3:
|
||||||
if (!event.isPress())
|
if (!event.isPress())
|
||||||
return false;
|
return false;
|
||||||
|
@ -19,4 +19,6 @@ LayerTestGUI.PosDisplay.NA.Client = Pos: client n/a
|
|||||||
LayerTestGUI.PosDisplay.NA.Entity = Pos: entity n/a
|
LayerTestGUI.PosDisplay.NA.Entity = Pos: entity n/a
|
||||||
LayerTestGUI.SelectedBlockDisplay = %s Block: %s
|
LayerTestGUI.SelectedBlockDisplay = %s Block: %s
|
||||||
LayerTestGUI.SelectedTileDisplay = %s Tile: %s
|
LayerTestGUI.SelectedTileDisplay = %s Tile: %s
|
||||||
LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel)
|
LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel)
|
||||||
|
LayerTestGUI.IsFullscreen = Fullscreen: %5s (F11)
|
||||||
|
LayerTestGUI.IsVSync = VSync: %5s (F12)
|
@ -19,4 +19,6 @@ LayerTestGUI.PosDisplay.NA.Client = Поз: клиент н/д
|
|||||||
LayerTestGUI.PosDisplay.NA.Entity = Поз: сущность н/д
|
LayerTestGUI.PosDisplay.NA.Entity = Поз: сущность н/д
|
||||||
LayerTestGUI.SelectedBlockDisplay = %s Блок: %s
|
LayerTestGUI.SelectedBlockDisplay = %s Блок: %s
|
||||||
LayerTestGUI.SelectedTileDisplay = %s Плитка: %s
|
LayerTestGUI.SelectedTileDisplay = %s Плитка: %s
|
||||||
LayerTestGUI.PlacementModeHint = (Блок %s плитки: Ctrl + прокрутка)
|
LayerTestGUI.PlacementModeHint = (Блок %s плитки: Ctrl + прокрутка)
|
||||||
|
LayerTestGUI.IsFullscreen = Полный экран: %5s (F11)
|
||||||
|
LayerTestGUI.IsVSync = Верт. синхр.: %5s (F12)
|
BIN
src/main/resources/assets/textures/all_buttons.png
Normal file
BIN
src/main/resources/assets/textures/all_buttons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
Reference in New Issue
Block a user