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/ProgressiaInstaller.nsi
|
||||
!build_packages/NSIS/logo.ico
|
||||
!build_packages/NSIS/left_side.bmp
|
||||
|
||||
# ... and except build_packages/DEB/template
|
||||
!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)
|
||||
- Java 8 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
|
||||
|
||||
See [Build Guide](docs/building/BuildGuide.md) for compilation requirements.
|
||||
|
@ -128,6 +128,7 @@ buildWindowsInstaller() {
|
||||
{
|
||||
cp -r 'build/libs/lib' 'build_packages/NSIS/lib' &&
|
||||
cp 'build/libs/Progressia.jar' 'build_packages/NSIS/Progressia.jar' &&
|
||||
cp 'LICENSE' 'build_packages/NSIS/LICENSE.txt' &&
|
||||
echo "------ NSIS ------" &&
|
||||
makensis "$configurationFile" &&
|
||||
echo "---- NSIS END ----" &&
|
||||
@ -144,6 +145,9 @@ buildWindowsInstaller() {
|
||||
if [ -e 'build_packages/NSIS/Progressia.jar' ]; then
|
||||
rm 'build_packages/NSIS/Progressia.jar'
|
||||
fi
|
||||
if [ -e 'build_packages/NSIS/LICENSE.txt' ]; then
|
||||
rm 'build_packages/NSIS/LICENSE.txt'
|
||||
fi
|
||||
echo "Cleaned up"
|
||||
} || {
|
||||
echoerr "Could not clean up after building Windows installer"
|
||||
|
@ -10,16 +10,32 @@
|
||||
;--------------------------------
|
||||
;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 "Progressia"
|
||||
OutFile "ProgressiaInstaller.exe"
|
||||
Name "${PROJECT_NAME}"
|
||||
OutFile "${PROJECT_NAME}Installer.exe"
|
||||
Unicode True
|
||||
|
||||
;Default installation folder
|
||||
InstallDir "$PROGRAMFILES\Progressia"
|
||||
InstallDir "$PROGRAMFILES\${PROJECT_NAME}"
|
||||
|
||||
;Get installation folder from registry if available
|
||||
InstallDirRegKey HKLM "Software\Progressia" "Install_Dir"
|
||||
InstallDirRegKey HKLM "Software\${PROJECT_NAME}" ""
|
||||
|
||||
;Request application privileges for Windows Vista
|
||||
RequestExecutionLevel admin
|
||||
@ -33,14 +49,18 @@
|
||||
;Pages
|
||||
|
||||
!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_DIRECTORY
|
||||
!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_UNPAGE_WELCOME
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_COMPONENTS
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
!insertmacro MUI_UNPAGE_FINISH
|
||||
|
||||
@ -52,12 +72,15 @@
|
||||
;--------------------------------
|
||||
;Installer Sections
|
||||
|
||||
Section "Install Progressia" SecDummy
|
||||
Section "Install ${PROJECT_NAME}" SEC0000
|
||||
|
||||
SectionIn RO ;Make it read-only
|
||||
SetOutPath "$INSTDIR"
|
||||
SetOverwrite on
|
||||
|
||||
;Files
|
||||
File Progressia.jar
|
||||
File logo.ico
|
||||
File /r lib
|
||||
|
||||
;Store installation folder
|
||||
@ -65,22 +88,24 @@ Section "Install Progressia" SecDummy
|
||||
|
||||
;Create uninstaller
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Progressia" "DisplayName" "Progressia (remove only)"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Progressia" "UninstallString" "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "DisplayName" "${PROJECT_NAME} (remove only)"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PROJECT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
|
||||
SectionEnd
|
||||
|
||||
;--------------------------------
|
||||
;Descriptions
|
||||
Section "Create Desktop Shortcut" SEC0001
|
||||
SetOutPath "$APPDATA\${PROJECT_NAME}"
|
||||
CreateShortCut "$DESKTOP\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
|
||||
SectionEnd
|
||||
|
||||
;Language strings
|
||||
LangString DESC_SecDummy ${LANG_ENGLISH} "A test section."
|
||||
Section "Start Menu Shortcuts" SEC0002
|
||||
|
||||
;Assign language strings to sections
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SecDummy} $(DESC_SecDummy)
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
CreateDirectory "$SMPROGRAMS\${PROJECT_NAME}"
|
||||
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||
CreateShortcut "$SMPROGRAMS\${PROJECT_NAME}\${PROJECT_NAME}.lnk" "$INSTDIR\${PROJECT_NAME}.jar" "" "$INSTDIR\logo.ico"
|
||||
|
||||
SectionEnd
|
||||
|
||||
;--------------------------------
|
||||
;Uninstaller Section
|
||||
@ -92,9 +117,45 @@ Section "Uninstall"
|
||||
Delete $INSTDIR\Uninstall.exe
|
||||
Delete $INSTDIR\Progressia.jar
|
||||
Delete $INSTDIR\lib\*.*
|
||||
Delete $INSTDIR\logo.ico
|
||||
|
||||
RMDir /r "$INSTDIR"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Progressia"
|
||||
DeleteRegKey HKLM "Software\Progressia"
|
||||
RMDir $INSTDIR\lib
|
||||
|
||||
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
|
||||
|
||||
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 ArgsContextProvider());
|
||||
CrashReports.registerProvider(new LanguageContextProvider());
|
||||
CrashReports.registerProvider(new ScreenContextProvider());
|
||||
// Analyzers
|
||||
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.server.ServerState;
|
||||
import ru.windcorp.progressia.test.TestContent;
|
||||
import ru.windcorp.progressia.test.TestMusicPlayer;
|
||||
|
||||
public class ClientProxy implements Proxy {
|
||||
|
||||
@ -59,6 +60,8 @@ public class ClientProxy implements Proxy {
|
||||
|
||||
ServerState.startServer();
|
||||
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.SoundType;
|
||||
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.ALC10.*;
|
||||
@ -40,7 +41,6 @@ public class AudioManager {
|
||||
|
||||
private static List<Speaker> soundSpeakers = new ArrayList<>(SOUNDS_NUM);
|
||||
private static Speaker musicSpeaker;
|
||||
private static ArrayList<SoundType> soundsBuffer = new ArrayList<>();
|
||||
|
||||
public static void initAL() {
|
||||
String defaultDeviceName = alcGetString(
|
||||
@ -82,31 +82,19 @@ public class AudioManager {
|
||||
return speaker;
|
||||
}
|
||||
|
||||
private static SoundType findSoundType(String soundID) throws Exception {
|
||||
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) {
|
||||
public static Speaker initSpeaker(SoundType st) {
|
||||
Speaker speaker = getLastSpeaker();
|
||||
try {
|
||||
findSoundType(soundID).initSpeaker(speaker);
|
||||
st.initSpeaker(speaker);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return speaker;
|
||||
}
|
||||
|
||||
public static Speaker initMusicSpeaker(String soundID) {
|
||||
public static Speaker initMusicSpeaker(SoundType st) {
|
||||
try {
|
||||
findSoundType(soundID).initSpeaker(musicSpeaker);
|
||||
st.initSpeaker(musicSpeaker);
|
||||
} catch (Exception ex) {
|
||||
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) {
|
||||
soundsBuffer.add(AudioReader.readAsMono(path, id));
|
||||
AudioRegistry.getInstance().register(AudioReader.readAsMono(resource, id));
|
||||
} 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;
|
||||
|
||||
import ru.windcorp.progressia.common.resource.ResourceManager;
|
||||
|
||||
public class AudioSystem {
|
||||
static public void initialize() {
|
||||
AudioManager.initAL();
|
||||
@ -28,7 +30,7 @@ public class AudioSystem {
|
||||
|
||||
static void loadAudioData() {
|
||||
AudioManager.loadSound(
|
||||
"assets/sounds/block_destroy_clap.ogg",
|
||||
ResourceManager.getResource("assets/sounds/block_destroy_clap.ogg"),
|
||||
"Progressia:BlockDestroy",
|
||||
AudioFormat.MONO
|
||||
);
|
||||
|
@ -19,72 +19,37 @@
|
||||
package ru.windcorp.progressia.client.audio;
|
||||
|
||||
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.common.util.namespaces.Namespaced;
|
||||
|
||||
public class Music extends Namespaced {
|
||||
private Vec3 position = new Vec3();
|
||||
private Vec3 velocity = new Vec3();
|
||||
private float pitch = 1.0f;
|
||||
private float gain = 1.0f;
|
||||
public class Music
|
||||
extends Sound {
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public Music(
|
||||
String id,
|
||||
Vec3 position,
|
||||
Vec3 velocity,
|
||||
float pitch,
|
||||
float gain
|
||||
) {
|
||||
this(id);
|
||||
this.position = position;
|
||||
this.velocity = velocity;
|
||||
this.pitch = pitch;
|
||||
this.gain = gain;
|
||||
@Override
|
||||
protected Speaker initSpeaker() {
|
||||
return AudioManager.initMusicSpeaker(soundType);
|
||||
}
|
||||
|
||||
public void play(boolean loop) {
|
||||
Speaker speaker = AudioManager.initMusicSpeaker(this.getId());
|
||||
speaker.setGain(gain);
|
||||
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;
|
||||
@Override
|
||||
public void setPosition(Vec3 position) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
@ -19,23 +19,30 @@
|
||||
package ru.windcorp.progressia.client.audio;
|
||||
|
||||
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.common.util.namespaces.Namespaced;
|
||||
|
||||
public class SoundEffect
|
||||
extends Namespaced {
|
||||
public class Sound {
|
||||
|
||||
private Vec3 position = new Vec3();
|
||||
private Vec3 velocity = new Vec3();
|
||||
private float pitch = 1.0f;
|
||||
private float gain = 1.0f;
|
||||
protected Vec3 position = new Vec3(0f, 0f, 0f);
|
||||
protected Vec3 velocity = new Vec3(0f, 0f, 0f);
|
||||
protected float pitch = 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,
|
||||
int timeLength,
|
||||
Vec3 position,
|
||||
Vec3 velocity,
|
||||
float pitch,
|
||||
@ -48,8 +55,27 @@ public class SoundEffect
|
||||
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) {
|
||||
Speaker speaker = AudioManager.initSpeaker(this.getId());
|
||||
Speaker speaker = initSpeaker();
|
||||
speaker.setGain(gain);
|
||||
speaker.setPitch(pitch);
|
||||
speaker.setPosition(position);
|
||||
@ -93,4 +119,9 @@ public class SoundEffect
|
||||
public float getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public double getDuration() {
|
||||
return soundType.getDuration();
|
||||
}
|
||||
|
||||
}
|
@ -33,13 +33,11 @@ public class AudioReader {
|
||||
}
|
||||
|
||||
// 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 rateBuffer = BufferUtils.createIntBuffer(1);
|
||||
|
||||
Resource res = ResourceManager.getResource(path);
|
||||
|
||||
ShortBuffer rawAudio = decodeVorbis(res, channelBuffer, rateBuffer);
|
||||
ShortBuffer rawAudio = decodeVorbis(resource, channelBuffer, rateBuffer);
|
||||
|
||||
return new SoundType(
|
||||
id,
|
||||
@ -49,12 +47,12 @@ public class AudioReader {
|
||||
);
|
||||
}
|
||||
|
||||
public static SoundType readAsMono(String path, String id) {
|
||||
return readAsSpecified(path, id, AL_FORMAT_MONO16);
|
||||
public static SoundType readAsMono(Resource resource, String id) {
|
||||
return readAsSpecified(resource, id, AL_FORMAT_MONO16);
|
||||
}
|
||||
|
||||
public static SoundType readAsStereo(String path, String id) {
|
||||
return readAsSpecified(path, id, AL_FORMAT_STEREO16);
|
||||
public static SoundType readAsStereo(Resource resource, String id) {
|
||||
return readAsSpecified(resource, id, AL_FORMAT_STEREO16);
|
||||
}
|
||||
|
||||
private static ShortBuffer decodeVorbis(
|
||||
|
@ -21,6 +21,9 @@ package ru.windcorp.progressia.client.audio.backend;
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
import static org.lwjgl.openal.AL11.*;
|
||||
|
||||
public class SoundType extends Namespaced {
|
||||
@ -29,6 +32,7 @@ public class SoundType extends Namespaced {
|
||||
private int sampleRate;
|
||||
private int format;
|
||||
private int audioBuffer;
|
||||
private double duration;
|
||||
|
||||
public SoundType(
|
||||
String id,
|
||||
@ -46,9 +50,14 @@ public class SoundType extends Namespaced {
|
||||
private void createAudioBuffer() {
|
||||
this.audioBuffer = alGenBuffers();
|
||||
alBufferData(audioBuffer, format, rawAudio, sampleRate);
|
||||
duration = rawAudio.limit() / (double) sampleRate / (format == AL10.AL_FORMAT_STEREO16 ? 2 : 1);
|
||||
}
|
||||
|
||||
public void initSpeaker(Speaker speaker) {
|
||||
speaker.setAudioData(audioBuffer);
|
||||
}
|
||||
|
||||
public double getDuration() {
|
||||
return duration;
|
||||
}
|
||||
}
|
@ -120,6 +120,7 @@ public class Speaker {
|
||||
}
|
||||
|
||||
public void setAudioData(int audioData) {
|
||||
stop();
|
||||
this.audioData = 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;
|
||||
}
|
||||
|
||||
}
|
@ -143,6 +143,96 @@ public class ControlTriggers {
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
///
|
||||
///
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
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(
|
||||
Class<I> inputType,
|
||||
BiConsumer<I, ControlData> dataWriter
|
||||
@ -150,6 +240,13 @@ public class ControlTriggers {
|
||||
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(
|
||||
Class<I> inputType,
|
||||
Predicate<I>[] predicates
|
||||
|
@ -18,11 +18,11 @@
|
||||
|
||||
package ru.windcorp.progressia.client.graphics.backend;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import glm.vec._2.i.Vec2i;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
public class GraphicsBackend {
|
||||
|
||||
@ -38,9 +38,30 @@ public class GraphicsBackend {
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
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() {
|
||||
startRenderThread();
|
||||
}
|
||||
@ -128,4 +149,47 @@ public class GraphicsBackend {
|
||||
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();
|
||||
}
|
||||
|
||||
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() {
|
||||
// TODO Do GLFW error handling: check glfwInit, setup error callback
|
||||
glfwInit();
|
||||
GraphicsBackend.setGLFWInitialized(true);
|
||||
}
|
||||
|
||||
private static void createWindow() {
|
||||
@ -67,7 +68,7 @@ class LWJGLInitializer {
|
||||
glfwSetInputMode(handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
|
||||
glfwMakeContextCurrent(handle);
|
||||
glfwSwapInterval(0);
|
||||
glfwSwapInterval(0); // TODO: remove after config system is added
|
||||
}
|
||||
|
||||
private static void positionWindow() {
|
||||
@ -87,6 +88,7 @@ class LWJGLInitializer {
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
RenderTaskQueue.schedule(OpenGLObjectTracker::deleteEnqueuedObjects);
|
||||
GraphicsBackend.setOpenGLInitialized(true);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public StaticModel(Builder builder) {
|
||||
this(builder.getParts(), builder.getTransforms());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mat4 getTransform(int partIndex) {
|
||||
return transforms[partIndex];
|
||||
@ -83,6 +79,10 @@ public class StaticModel extends Model {
|
||||
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");
|
||||
|
@ -18,35 +18,20 @@
|
||||
|
||||
package ru.windcorp.progressia.client.world;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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 ru.windcorp.progressia.client.graphics.model.Model;
|
||||
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.BlockRenderNone;
|
||||
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.TileRenderRegistry;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRenderStack;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||
import ru.windcorp.progressia.common.world.generic.GenericChunk;
|
||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||
import ru.windcorp.progressia.common.world.tile.TileDataStack;
|
||||
|
||||
public class ChunkRender
|
||||
@ -55,7 +40,7 @@ public class ChunkRender
|
||||
private final WorldRender world;
|
||||
private final ChunkData data;
|
||||
|
||||
private Model model = null;
|
||||
private final ChunkRenderModel model;
|
||||
|
||||
private final Map<TileDataStack, TileRenderStackImpl> tileRenderLists = Collections
|
||||
.synchronizedMap(new WeakHashMap<>());
|
||||
@ -63,6 +48,7 @@ public class ChunkRender
|
||||
public ChunkRender(WorldRender world, ChunkData data) {
|
||||
this.world = world;
|
||||
this.data = data;
|
||||
this.model = new ChunkRenderModel(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -107,165 +93,11 @@ public class ChunkRender
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
renderer.popTransform();
|
||||
}
|
||||
|
||||
public synchronized void update() {
|
||||
Collection<ChunkRenderOptimizer> optimizers = ChunkRenderOptimizers.getAllSuppliers().stream()
|
||||
.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)
|
||||
);
|
||||
model.update();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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.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 {
|
||||
|
||||
@ -34,4 +40,60 @@ class ChunkUpdateListener implements ChunkDataListener {
|
||||
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.common.util.namespaces.Namespaced;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
import ru.windcorp.progressia.common.world.generic.GenericBlock;
|
||||
import glm.vec._3.i.Vec3i;
|
||||
import ru.windcorp.progressia.client.graphics.model.Renderable;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,10 @@
|
||||
|
||||
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.Renderable;
|
||||
import ru.windcorp.progressia.common.world.ChunkData;
|
||||
|
||||
public class BlockRenderNone extends BlockRender {
|
||||
|
||||
@ -28,7 +30,7 @@ public class BlockRenderNone extends BlockRender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable() {
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk) {
|
||||
return EmptyModel.getInstance();
|
||||
}
|
||||
|
||||
|
@ -65,9 +65,4 @@ public class BlockRenderOpaqueCube extends BlockRenderTexturedCube {
|
||||
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.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.Shape;
|
||||
import ru.windcorp.progressia.client.graphics.texture.Texture;
|
||||
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;
|
||||
|
||||
public abstract class BlockRenderTexturedCube
|
||||
extends BlockRender
|
||||
implements OpaqueCube {
|
||||
implements BlockOptimizedSurface {
|
||||
|
||||
private final Map<BlockFace, Texture> textures = new HashMap<>();
|
||||
|
||||
@ -55,22 +65,61 @@ public abstract class BlockRenderTexturedCube
|
||||
textures.put(WEST, westTexture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture(BlockFace face) {
|
||||
return textures.get(face);
|
||||
public Texture getTexture(BlockFace blockFace) {
|
||||
return textures.get(blockFace);
|
||||
}
|
||||
|
||||
public Vec4 getColorMultiplier(BlockFace blockFace) {
|
||||
return Colors.WHITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable() {
|
||||
return new Shapes.PppBuilder(
|
||||
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(TOP),
|
||||
getTexture(BOTTOM),
|
||||
getTexture(NORTH),
|
||||
getTexture(SOUTH),
|
||||
getTexture(EAST),
|
||||
getTexture(WEST)
|
||||
).create();
|
||||
getTexture(blockFace),
|
||||
getColorMultiplier(blockFace),
|
||||
offset,
|
||||
blockFace,
|
||||
inner
|
||||
);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsOwnRenderable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,27 +19,111 @@
|
||||
package ru.windcorp.progressia.client.world.cro;
|
||||
|
||||
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.block.BlockRender;
|
||||
import ru.windcorp.progressia.client.world.tile.TileRender;
|
||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||
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,
|
||||
Vec3i posInChunk
|
||||
);
|
||||
/**
|
||||
* Creates a new CRO instance with the specified ID.
|
||||
*
|
||||
* @param id the ID of this CRO
|
||||
*/
|
||||
public ChunkRenderOptimizer(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
public abstract void processTile(
|
||||
TileRender tile,
|
||||
Vec3i posInChunk,
|
||||
BlockFace face
|
||||
);
|
||||
/**
|
||||
* This method is invoked before a new chunk processing cycle begins to
|
||||
* specify the chunk. When overriding, {@code super.setup(chunk)} must be
|
||||
* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public abstract ChunkRenderOptimizer createOptimizer();
|
||||
|
||||
public static ChunkRenderOptimizerSupplier of(
|
||||
String id,
|
||||
Supplier<ChunkRenderOptimizer> supplier
|
||||
) {
|
||||
return new ChunkRenderOptimizerSupplier(id) {
|
||||
@Override
|
||||
public ChunkRenderOptimizer createOptimizer() {
|
||||
return supplier.get();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @return the instance
|
||||
*/
|
||||
public static ChunkRenderOptimizerRegistry getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
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.world.cro.ChunkRenderOptimizer;
|
||||
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.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;
|
||||
}
|
||||
|
||||
|
@ -18,19 +18,10 @@
|
||||
|
||||
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.world.WorldRenderProgram;
|
||||
import ru.windcorp.progressia.client.world.cro.ChunkRenderOptimizerCube.OpaqueSurface;
|
||||
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 sideTexture;
|
||||
@ -55,27 +46,4 @@ public class TileRenderGrass extends TileRender implements OpaqueSurface {
|
||||
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;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
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.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.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;
|
||||
|
||||
public abstract class TileRenderSurface extends TileRender implements OpaqueSurface {
|
||||
public abstract class TileRenderSurface extends TileRender implements TileOptimizedSurface {
|
||||
|
||||
private final Texture texture;
|
||||
|
||||
@ -39,26 +45,51 @@ public abstract class TileRenderSurface extends TileRender implements OpaqueSurf
|
||||
this.texture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Texture getTexture(BlockFace face) {
|
||||
public TileRenderSurface(String id) {
|
||||
this(id, null);
|
||||
}
|
||||
|
||||
public Texture getTexture(BlockFace blockFace) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderable createRenderable(BlockFace face) {
|
||||
ShapeRenderProgram program = WorldRenderProgram.getDefault();
|
||||
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
|
||||
public Renderable createRenderable(ChunkData chunk, Vec3i blockInChunk, BlockFace blockFace) {
|
||||
return new Shape(
|
||||
Usage.STATIC,
|
||||
WorldRenderProgram.getDefault(),
|
||||
Faces.createBlockFace(
|
||||
program,
|
||||
getTexture(face),
|
||||
Colors.WHITE,
|
||||
new Vec3(0, 0, 0),
|
||||
face,
|
||||
false
|
||||
)
|
||||
|
||||
createFace(chunk, blockInChunk, blockFace, false, Vectors.ZERO_3),
|
||||
createFace(chunk, blockInChunk, blockFace, true, Vectors.ZERO_3)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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.CharStreams;
|
||||
|
||||
import ru.windcorp.progressia.Progressia;
|
||||
import ru.windcorp.progressia.common.util.Named;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class Resource extends Named {
|
||||
|
||||
public Resource(String name) {
|
||||
private final ResourceReader resourceReader;
|
||||
|
||||
public Resource(String name, ResourceReader resourceReader) {
|
||||
super(name);
|
||||
this.resourceReader = resourceReader;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
// TODO Do proper resource lookup
|
||||
return Progressia.class.getClassLoader().getResourceAsStream(getName());
|
||||
return getResourceReader().read(getName());
|
||||
}
|
||||
|
||||
public ResourceReader getResourceReader() {
|
||||
return resourceReader;
|
||||
}
|
||||
|
||||
public Reader getReader() {
|
||||
|
@ -20,8 +20,15 @@ package ru.windcorp.progressia.common.resource;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
|
@ -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)
|
||||
continue;
|
||||
|
||||
addSeparator(output);
|
||||
|
||||
try {
|
||||
Map<String, String> buf = new HashMap<>();
|
||||
provider.provideContext(buf);
|
||||
|
||||
if (!buf.isEmpty()) {
|
||||
addSeparator(output);
|
||||
output.append(StringUtil.center(provider.getName(), 80)).append("\n");
|
||||
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
||||
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||
|
@ -26,12 +26,12 @@ public class RAMContextProvider implements ContextProvider {
|
||||
|
||||
@Override
|
||||
public void provideContext(Map<String, String> output) {
|
||||
output.put("Max Memory", Long.toString(Runtime.getRuntime().maxMemory() / 1024 / 1024) + " MB");
|
||||
output.put("Total Memory", Long.toString(Runtime.getRuntime().totalMemory() / 1024 / 1024) + " MB");
|
||||
output.put("Free Memory", Long.toString(Runtime.getRuntime().freeMemory() / 1024 / 1024) + " MB");
|
||||
output.put("Max Memory", Runtime.getRuntime().maxMemory() / 1024 / 1024 + " MB");
|
||||
output.put("Total Memory", Runtime.getRuntime().totalMemory() / 1024 / 1024 + " MB");
|
||||
output.put("Free Memory", Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");
|
||||
output.put(
|
||||
"Used Memory",
|
||||
Long.toString((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024)
|
||||
(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024
|
||||
+ " MB"
|
||||
);
|
||||
}
|
||||
|
@ -16,38 +16,26 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.windcorp.progressia.client.world.cro;
|
||||
package ru.windcorp.progressia.common.util.crash.providers;
|
||||
|
||||
import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend;
|
||||
import ru.windcorp.progressia.common.util.crash.ContextProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
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<>();
|
||||
|
||||
static {
|
||||
register(
|
||||
ChunkRenderOptimizerSupplier.of(
|
||||
"Default:OpaqueCube",
|
||||
ChunkRenderOptimizerCube::new
|
||||
)
|
||||
);
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Screen Context Provider";
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -159,4 +159,8 @@ public class Coordinates {
|
||||
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 {
|
||||
|
||||
private final Vec3i vector = new Vec3i();
|
||||
private final Vec3 floatVector = new Vec3();
|
||||
private final Vec3 normalized = new Vec3();
|
||||
|
||||
public BlockRelation(int x, int y, int z) {
|
||||
vector.set(x, y, z);
|
||||
floatVector.set(x, y, z);
|
||||
normalized.set(x, y, z).normalize();
|
||||
}
|
||||
|
||||
@ -42,6 +44,10 @@ public class BlockRelation {
|
||||
return vector;
|
||||
}
|
||||
|
||||
public Vec3 getFloatVector() {
|
||||
return floatVector;
|
||||
}
|
||||
|
||||
public Vec3 getNormalized() {
|
||||
return normalized;
|
||||
}
|
||||
|
@ -92,6 +92,72 @@ public interface GenericChunk<Self extends GenericChunk<Self, B, T, TS>, B exten
|
||||
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) {
|
||||
VectorUtil.iterateCuboid(
|
||||
0,
|
||||
|
@ -183,6 +183,18 @@ public class Server {
|
||||
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
|
||||
* provided accessor to
|
||||
|
@ -82,6 +82,7 @@ public class TickerCoordinator {
|
||||
private boolean isTickStartSet = false;
|
||||
private long tickStart = -1;
|
||||
private double tickLength = 1.0 / 20; // Do something about it
|
||||
private long ticks = 0;
|
||||
|
||||
private final Logger logger = LogManager.getLogger("Ticker Coordinator");
|
||||
|
||||
@ -152,6 +153,10 @@ public class TickerCoordinator {
|
||||
return 1 / tickLength;
|
||||
}
|
||||
|
||||
public long getUptimeTicks() {
|
||||
return ticks;
|
||||
}
|
||||
|
||||
private void onTickStart() {
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
@ -164,6 +169,10 @@ public class TickerCoordinator {
|
||||
tickStart = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void onTickEnd() {
|
||||
ticks++;
|
||||
}
|
||||
|
||||
/*
|
||||
* runOneTick & Friends
|
||||
*/
|
||||
@ -183,6 +192,8 @@ public class TickerCoordinator {
|
||||
passes++;
|
||||
}
|
||||
|
||||
onTickEnd();
|
||||
|
||||
logger.debug("Tick complete; run {} passes", passes);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
@ -191,7 +202,7 @@ public class TickerCoordinator {
|
||||
// ...or almost silently
|
||||
logger.debug("Tick interrupted. WTF?");
|
||||
} catch (Exception e) {
|
||||
crash(e, "Coordinator");
|
||||
throw CrashReports.report(e, "Coordinator");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class LayerAbout extends GUILayer {
|
||||
new Label(
|
||||
"Version",
|
||||
font,
|
||||
new MutableStringLocalized("LayerAbout.Version").format("TechDemo")
|
||||
new MutableStringLocalized("LayerAbout.Version").format("pre-alpha 1")
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -18,22 +18,23 @@
|
||||
|
||||
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._4.Vec4;
|
||||
import ru.windcorp.progressia.client.Client;
|
||||
import ru.windcorp.progressia.client.ClientState;
|
||||
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.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.GUILayer;
|
||||
import ru.windcorp.progressia.client.graphics.gui.Button;
|
||||
import ru.windcorp.progressia.client.graphics.gui.Label;
|
||||
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.LayoutVertical;
|
||||
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.ServerState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LayerTestGUI extends GUILayer {
|
||||
|
||||
public LayerTestGUI() {
|
||||
@ -56,6 +62,54 @@ public class LayerTestGUI extends GUILayer {
|
||||
|
||||
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(
|
||||
new Label(
|
||||
"IsFlyingDisplay",
|
||||
@ -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(
|
||||
new DynamicLabel(
|
||||
"FPSDisplay",
|
||||
@ -244,12 +314,126 @@ 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 TPS_RECORD = new Averager();
|
||||
private static final Queue TPS_RECORD = new Queue();
|
||||
|
||||
private static final Supplier<CharSequence> TPS_STRING = DynamicStrings.builder()
|
||||
.addDyn(new MutableStringLocalized("LayerTestGUI.TPSDisplay"))
|
||||
.addDyn(() -> TPS_RECORD.update(ServerState.getInstance().getTPS()), 5, 1)
|
||||
.addDyn(() -> TPS_RECORD.update(ServerState.getInstance().getUptimeTicks()), 5, 1)
|
||||
.buildSupplier();
|
||||
|
||||
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.i.Vec3i;
|
||||
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.graphics.input.KeyEvent;
|
||||
import ru.windcorp.progressia.client.graphics.input.KeyMatcher;
|
||||
import ru.windcorp.progressia.client.graphics.world.Selection;
|
||||
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.tile.*;
|
||||
import ru.windcorp.progressia.common.collision.AABB;
|
||||
@ -285,6 +287,15 @@ public class TestContent {
|
||||
)
|
||||
);
|
||||
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) {
|
||||
@ -358,7 +369,7 @@ public class TestContent {
|
||||
|
||||
private static void onBlockBreakTrigger(ControlData control) {
|
||||
((ControlBreakBlockData) control).setBlockInWorld(getSelection().getBlock());
|
||||
SoundEffect sfx = new SoundEffect("Progressia:BlockDestroy");
|
||||
Sound sfx = new Sound("Progressia:BlockDestroy");
|
||||
sfx.setPosition(getSelection().getPoint());
|
||||
sfx.setPitch((float) (Math.random() + 1 * 0.5));
|
||||
sfx.play(false);
|
||||
@ -420,6 +431,7 @@ public class TestContent {
|
||||
|
||||
private static void registerMisc() {
|
||||
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()
|
||||
);
|
||||
|
||||
return new StaticModel(b);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -386,7 +386,7 @@ public class TestEntityRenderJavapony extends EntityRender {
|
||||
).setOrigin(16, -4, 8).setSize(4, 8, 4).create()
|
||||
);
|
||||
|
||||
return new StaticModel(b);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
private static Renderable createLeg(
|
||||
@ -427,7 +427,7 @@ public class TestEntityRenderJavapony extends EntityRender {
|
||||
).setOrigin(-8, -8, -32).setSize(16, 16, 32).create()
|
||||
);
|
||||
|
||||
return new StaticModel(b);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
@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();
|
||||
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:
|
||||
if (!event.isPress())
|
||||
return false;
|
||||
|
@ -20,3 +20,5 @@ LayerTestGUI.PosDisplay.NA.Entity = Pos: entity n/a
|
||||
LayerTestGUI.SelectedBlockDisplay = %s Block: %s
|
||||
LayerTestGUI.SelectedTileDisplay = %s Tile: %s
|
||||
LayerTestGUI.PlacementModeHint = (Blocks %s Tiles: Ctrl + Mouse Wheel)
|
||||
LayerTestGUI.IsFullscreen = Fullscreen: %5s (F11)
|
||||
LayerTestGUI.IsVSync = VSync: %5s (F12)
|
@ -20,3 +20,5 @@ LayerTestGUI.PosDisplay.NA.Entity = Поз: сущность н/д
|
||||
LayerTestGUI.SelectedBlockDisplay = %s Блок: %s
|
||||
LayerTestGUI.SelectedTileDisplay = %s Плитка: %s
|
||||
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