├── client
├── options.txt
├── pom.xml
├── resources
│ ├── assets
│ │ ├── audio
│ │ │ ├── dig.wav
│ │ │ ├── place.wav
│ │ │ ├── pop.wav
│ │ │ ├── snap.wav
│ │ │ └── throw.wav
│ │ ├── fonts
│ │ │ └── default
│ │ │ │ ├── info.txt
│ │ │ │ └── sheet01.png
│ │ ├── models
│ │ │ └── test.glb
│ │ └── textures
│ │ │ ├── blueSquare.png
│ │ │ ├── clouds.png
│ │ │ ├── clouds_online.png
│ │ │ ├── cursors
│ │ │ ├── cursor.png
│ │ │ ├── mallet.png
│ │ │ ├── mallet32.png
│ │ │ ├── pickaxe.png
│ │ │ ├── pickaxe32.png
│ │ │ ├── pickaxeBorderless.png
│ │ │ └── pickaxeBorderless32.png
│ │ │ ├── dirt.png
│ │ │ ├── glass.png
│ │ │ ├── grass.png
│ │ │ ├── greenArm.png
│ │ │ ├── greenFace.png
│ │ │ ├── night.png
│ │ │ ├── redSquare.png
│ │ │ ├── stickman.png
│ │ │ └── stickman_crouch.png
│ └── shaders
│ │ ├── new
│ │ ├── blocks.frag
│ │ ├── blocks.vert
│ │ ├── fullscreen-blend.frag
│ │ ├── fullscreen.frag
│ │ ├── fullscreen.geom
│ │ ├── fullscreen.vert
│ │ ├── light.frag
│ │ ├── light.geom
│ │ ├── light.vert
│ │ ├── model.frag
│ │ ├── model.vert
│ │ ├── shading.comp
│ │ ├── shading_apply.frag
│ │ └── shading_apply.geom
│ │ └── old
│ │ ├── model.frag
│ │ └── model.vert
├── src
│ ├── de
│ │ └── matthiasmann
│ │ │ └── twl
│ │ │ └── utils
│ │ │ └── PNGDecoder.java
│ ├── module-info.java
│ └── ritzow
│ │ └── sandbox
│ │ └── client
│ │ ├── GameLoop.java
│ │ ├── GameState.java
│ │ ├── InWorldContext.java
│ │ ├── MainMenuContext.java
│ │ ├── StartClient.java
│ │ ├── audio
│ │ ├── AudioData.java
│ │ ├── AudioSystem.java
│ │ ├── JavaxAudioSystem.java
│ │ ├── OpenALAudioSystem.java
│ │ ├── OpenALException.java
│ │ ├── Sound.java
│ │ └── WAVEDecoder.java
│ │ ├── build
│ │ └── CompileShaders.java
│ │ ├── data
│ │ ├── ClientOptions.java
│ │ ├── StandardClientOptions.java
│ │ └── StandardClientProperties.java
│ │ ├── graphics
│ │ ├── BlockRenderProgram.java
│ │ ├── Camera.java
│ │ ├── Display.java
│ │ ├── Framebuffer.java
│ │ ├── FullscreenQuadProgram.java
│ │ ├── GLTFModelLoader.java
│ │ ├── GameModels.java
│ │ ├── Graphics.java
│ │ ├── GraphicsUtility.java
│ │ ├── Light.java
│ │ ├── LightRenderProgram.java
│ │ ├── LightingApplyProgram.java
│ │ ├── Lit.java
│ │ ├── Model.java
│ │ ├── ModelDestination.java
│ │ ├── ModelRenderProgramBase.java
│ │ ├── ModelRenderProgramEnhanced.java
│ │ ├── ModelRenderProgramOld.java
│ │ ├── ModelRenderer.java
│ │ ├── ModelStorage.java
│ │ ├── MutableGraphics.java
│ │ ├── OpenGLByteTexture.java
│ │ ├── OpenGLException.java
│ │ ├── OpenGLTexture.java
│ │ ├── RenderManager.java
│ │ ├── Renderable.java
│ │ ├── Shader.java
│ │ ├── ShaderProgram.java
│ │ ├── ShadingApplyProgram.java
│ │ ├── ShadingComputeProgram.java
│ │ ├── StaticLight.java
│ │ ├── TextureAtlas.java
│ │ ├── TextureData.java
│ │ └── Textures.java
│ │ ├── input
│ │ ├── Button.java
│ │ ├── Control.java
│ │ ├── ControlsContext.java
│ │ ├── ControlsQuery.java
│ │ ├── InputProvider.java
│ │ └── controller
│ │ │ ├── InteractionController.java
│ │ │ └── TrackingCameraController.java
│ │ ├── network
│ │ ├── Client.java
│ │ ├── GameTalker.java
│ │ └── ServerBadDataException.java
│ │ ├── ui
│ │ ├── Animation.java
│ │ ├── Circle.java
│ │ ├── Font.java
│ │ ├── GuiElement.java
│ │ ├── GuiRenderer.java
│ │ ├── Position.java
│ │ ├── Rectangle.java
│ │ ├── Shape.java
│ │ ├── StandardGuiRenderer.java
│ │ └── element
│ │ │ ├── AbsoluteGuiPositioner.java
│ │ │ ├── BorderAnchor.java
│ │ │ ├── Button.java
│ │ │ ├── EditableText.java
│ │ │ ├── HBox.java
│ │ │ ├── HBoxDynamic.java
│ │ │ ├── Holder.java
│ │ │ ├── Icon.java
│ │ │ ├── RotationAnimation.java
│ │ │ ├── Scaler.java
│ │ │ ├── Text.java
│ │ │ ├── TextField.java
│ │ │ ├── VBox.java
│ │ │ └── VBoxDynamic.java
│ │ ├── util
│ │ ├── ClientUtility.java
│ │ └── SerializationProvider.java
│ │ └── world
│ │ ├── ClientWorldRendererLightmap.java
│ │ ├── ClientWorldRendererModelOnly.java
│ │ ├── ClientWorldRendererTraced.java
│ │ ├── block
│ │ ├── ClientBlockProperties.java
│ │ ├── ClientDirtBlock.java
│ │ ├── ClientGlassBlock.java
│ │ └── ClientGrassBlock.java
│ │ ├── entity
│ │ ├── ClientItemEntity.java
│ │ └── ClientPlayerEntity.java
│ │ └── item
│ │ └── ClientBlockItem.java
└── test
│ └── ritzow
│ └── sandbox
│ └── client
│ └── test
│ └── RunTest.java
├── clientlauncher
├── Linux
│ ├── Sandbox2DLinuxLauncher.vcxproj
│ ├── Sandbox2DLinuxLauncher
│ │ └── Sandbox2DLinuxLauncher.vcxproj
│ └── main.cpp
├── Shared
│ ├── shared.cpp
│ └── src
│ │ └── ritzow
│ │ └── sandbox
│ │ └── build
│ │ └── Build.java
├── Windows
│ ├── Sandbox2DLauncherWindows.vcxproj
│ ├── launch.cpp
│ ├── launch_static.cpp
│ └── resources
│ │ ├── greenFace.ico
│ │ ├── resource.h
│ │ └── resource.rc
├── build.ps1
├── macOS
│ ├── prepare-build.bat
│ └── src
│ │ ├── launch.cpp
│ │ └── run.command
└── pom.xml
├── pom.xml
├── readme.md
├── server
├── pom.xml
├── src
│ ├── module-info.java
│ └── ritzow
│ │ └── sandbox
│ │ └── server
│ │ ├── CommandParser.java
│ │ ├── SerializationProvider.java
│ │ ├── StartServer.java
│ │ ├── network
│ │ ├── ClientBadDataException.java
│ │ ├── ClientNetworkInfo.java
│ │ ├── ClientState.java
│ │ ├── GameServer.java
│ │ └── Server.java
│ │ └── world
│ │ └── entity
│ │ └── ServerPlayerEntity.java
└── test
│ └── ritzow
│ └── sandbox
│ └── server
│ └── test
│ └── RunTest.java
├── serverlauncher
├── pom.xml
├── prepare_build.bat
├── resources
│ ├── greenFace.png
│ └── ui
│ │ ├── launcher_main.fxml
│ │ └── style.css
├── run_server.bat
└── src
│ ├── module-info.java
│ └── ritzow
│ └── sandbox
│ └── server
│ └── launcher
│ ├── LauncherController.java
│ └── StartLauncher.java
├── shared
├── pom.xml
└── src
│ ├── module-info.java
│ └── ritzow
│ └── sandbox
│ ├── data
│ ├── Bytes.java
│ ├── DataReader.java
│ ├── Deserializer.java
│ ├── SerializationException.java
│ ├── Serializer.java
│ ├── SerializerReaderWriter.java
│ ├── Transportable.java
│ ├── TransportableDataReader.java
│ └── TypeNotRegisteredException.java
│ ├── network
│ ├── NetworkUtility.java
│ ├── Protocol.java
│ ├── ReceivePacket.java
│ ├── SendPacket.java
│ └── TimeoutException.java
│ ├── util
│ ├── Optimized.java
│ └── Utility.java
│ └── world
│ ├── BlockGrid.java
│ ├── World.java
│ ├── block
│ ├── Block.java
│ ├── DirtBlock.java
│ ├── GlassBlock.java
│ └── GrassBlock.java
│ ├── component
│ ├── Inventory.java
│ ├── Living.java
│ ├── Luminous.java
│ ├── Physical.java
│ └── Positional.java
│ ├── entity
│ ├── Entity.java
│ ├── ItemEntity.java
│ └── PlayerEntity.java
│ ├── generator
│ ├── SinusoidWorldGenerator.java
│ └── WorldGenerator.java
│ └── item
│ ├── BlockItem.java
│ └── Item.java
└── to-do.txt
/client/options.txt:
--------------------------------------------------------------------------------
1 | #Options file
2 |
3 | use_new_opengl = true
4 | vsync = false
5 | fps_print =
6 | fps_limit =
7 | use_internet = false
8 | gui_scale = 500
9 | debug = false
10 | debug_opengl = true
11 | lefty = true
--------------------------------------------------------------------------------
/client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | net.ritzow
6 | sandbox2d-parent
7 | ${revision}
8 |
9 |
10 | sandbox2d-client
11 |
12 |
13 |
14 |
15 | org.lwjgl
16 | lwjgl-bom
17 | 3.3.1
18 | import
19 | pom
20 |
21 |
22 |
23 |
24 |
25 | src
26 | test
27 |
28 |
29 | resources
30 |
31 |
32 |
33 |
34 | org.apache.maven.plugins
35 | maven-surefire-plugin
36 |
37 |
38 | true
39 | true
40 | true
41 | true
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | net.ritzow
51 | sandbox2d-shared
52 |
53 |
54 | org.lwjgl
55 | lwjgl
56 |
57 |
58 | org.lwjgl
59 | lwjgl-glfw
60 |
61 |
62 | org.lwjgl
63 | lwjgl-openal
64 |
65 |
66 | org.lwjgl
67 | lwjgl-opengl
68 |
69 |
70 | org.lwjgl
71 | lwjgl
72 | natives-windows
73 |
74 |
75 | org.lwjgl
76 | lwjgl-glfw
77 | natives-windows
78 |
79 |
80 | org.lwjgl
81 | lwjgl-openal
82 | natives-windows
83 |
84 |
85 | org.lwjgl
86 | lwjgl-opengl
87 | natives-windows
88 |
89 |
90 | org.junit.jupiter
91 | junit-jupiter-api
92 | test
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/client/resources/assets/audio/dig.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/audio/dig.wav
--------------------------------------------------------------------------------
/client/resources/assets/audio/place.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/audio/place.wav
--------------------------------------------------------------------------------
/client/resources/assets/audio/pop.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/audio/pop.wav
--------------------------------------------------------------------------------
/client/resources/assets/audio/snap.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/audio/snap.wav
--------------------------------------------------------------------------------
/client/resources/assets/audio/throw.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/audio/throw.wav
--------------------------------------------------------------------------------
/client/resources/assets/fonts/default/info.txt:
--------------------------------------------------------------------------------
1 | Default Font
--------------------------------------------------------------------------------
/client/resources/assets/fonts/default/sheet01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/fonts/default/sheet01.png
--------------------------------------------------------------------------------
/client/resources/assets/models/test.glb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/models/test.glb
--------------------------------------------------------------------------------
/client/resources/assets/textures/blueSquare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/blueSquare.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/clouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/clouds.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/clouds_online.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/clouds_online.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/cursors/cursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/cursors/cursor.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/cursors/mallet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/cursors/mallet.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/cursors/mallet32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/cursors/mallet32.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/cursors/pickaxe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/cursors/pickaxe.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/cursors/pickaxe32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/cursors/pickaxe32.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/cursors/pickaxeBorderless.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/cursors/pickaxeBorderless.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/cursors/pickaxeBorderless32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/cursors/pickaxeBorderless32.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/dirt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/dirt.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/glass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/glass.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/grass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/grass.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/greenArm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/greenArm.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/greenFace.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/greenFace.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/night.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/redSquare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/redSquare.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/stickman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/stickman.png
--------------------------------------------------------------------------------
/client/resources/assets/textures/stickman_crouch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ritzow/Sandbox2D/1e8d2e2dd6d76ffe5f10b304362d767e67923154/client/resources/assets/textures/stickman_crouch.png
--------------------------------------------------------------------------------
/client/resources/shaders/new/blocks.frag:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | //If I ever want multiple outputs:
4 | //https://stackoverflow.com/questions/30025009/in-opengl-is-it-possible-for-one-shader-program-in-one-draw-call-to-render-to?rq=1
5 |
6 | layout(location = 0) in smooth vec2 textureCoord;
7 |
8 | layout(location = 0) out vec4 color; //color attachment 0
9 |
10 | layout(location = 2, binding = 0) uniform sampler2D atlasTexture;
11 | layout(location = 3) uniform float exposure;
12 |
13 | void main() {
14 | vec4 texel = texture(atlasTexture, textureCoord);
15 | color = vec4(texel.rgb * exposure, texel.a);
16 | }
--------------------------------------------------------------------------------
/client/resources/shaders/new/blocks.vert:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0) in vec2 position;
4 | layout(location = 1) in vec2 textureCoord;
5 |
6 | layout(location = 0) out smooth vec2 outTextureCoord;
7 |
8 | layout(location = 0) uniform mat4 view;
9 | layout(location = 1) uniform uvec3 blockGrid; //left block X, bottom blockY, blocks width
10 |
11 | const int MAX_RENDER_COUNT = 100;
12 |
13 | layout(binding = 1, std140) uniform instance_data {
14 | int offsets[MAX_RENDER_COUNT];
15 | };
16 |
17 | void main() {
18 | //get the index into the viewport block-grid
19 | //of the current series of the same block, then add the index of the current block
20 | int index = offsets[gl_DrawID] + gl_InstanceID;
21 | uint blockX = index % blockGrid[2] + blockGrid[0];
22 | uint blockY = index / blockGrid[2] + blockGrid[1];
23 | gl_Position = view * vec4(position + vec2(float(blockX), float(blockY)), 0.0, 1.0);
24 | outTextureCoord = textureCoord;
25 | }
--------------------------------------------------------------------------------
/client/resources/shaders/new/fullscreen-blend.frag:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0) in smooth vec2 textureCoord;
4 | layout(location = 0) out vec4 fragColor;//color attachment 0
5 |
6 | layout(location = 0, binding = 0) uniform sampler2D diffuse;
7 | layout(location = 1, binding = 1) uniform sampler2D lighting;
8 | //TODO layout(location = 2, binding = 2) uniform sampler2D shadow;
9 |
10 | const float pi2 = 6.28318530718;// Pi*2
11 |
12 | // GAUSSIAN BLUR SETTINGS {{{
13 | const float DIRECTIONS = 16.0;// BLUR DIRECTIONS (Default 16.0 - More is better but slower)
14 | const float QUALITY = 4.0;// BLUR QUALITY (Default 4.0 - More is better but slower)
15 | const float SIZE = 8.0;// BLUR SIZE (Radius) default 8
16 | // GAUSSIAN BLUR SETTINGS }}}
17 |
18 | const float DIVISOR = QUALITY * DIRECTIONS - 15.0;
19 |
20 | void main() {
21 |
22 | //Gaussian blur from https://www.shadertoy.com/view/Xltfzj
23 | // vec2 radius = SIZE/vec2(1920, 1080);//Size/iResolution.xy;
24 | //
25 | // // Pixel colour
26 | // vec4 color = texture(diffuse, textureCoord);
27 | // float alpha = color.a;
28 | //
29 | // // Blur calculations (TODO this needs to eat into the diffuse/shadow textureand make edges more transparent)
30 | // for(float d = 0.0; d < pi2; d += pi2/DIRECTIONS) {
31 | // for(float i = 1.0/QUALITY; i <= 1.0; i += 1.0/QUALITY) {
32 | // //TODO if is completely transparent, don't use
33 | // color += texture(diffuse, textureCoord + vec2(cos(d), sin(d)) * radius * i);
34 | // }
35 | // }
36 | //
37 | // color /= DIVISOR;
38 | // color.a = alpha; //removes soft edges that extend past opaque parts of texture
39 |
40 | //Output to screen
41 | fragColor = texture(diffuse, textureCoord) * texture(lighting, textureCoord); //color * texture(lighting, textureCoord);
42 | }
43 |
--------------------------------------------------------------------------------
/client/resources/shaders/new/fullscreen.frag:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0) in smooth vec2 textureCoord;
4 | layout(location = 0) out vec4 fragColor; //color attachment 0
5 | layout(location = 0) uniform sampler2D source; //TODO maybe use an image instead of sampler to be more efficient
6 |
7 | void main() {
8 | fragColor = texture(source, textureCoord);
9 | }
10 |
--------------------------------------------------------------------------------
/client/resources/shaders/new/fullscreen.geom:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(points) in;
4 | layout(triangle_strip, max_vertices = 4) out;
5 |
6 | layout(location = 0) out smooth vec2 textureCoord;
7 |
8 | void main() {
9 | textureCoord = vec2(0, 1);
10 | gl_Position = vec4(-1, 1, 0, 1);
11 | EmitVertex();
12 |
13 | textureCoord = vec2(0, 0);
14 | gl_Position = vec4(-1, -1, 0, 1);
15 | EmitVertex();
16 |
17 | textureCoord = vec2(1, 1);
18 | gl_Position = vec4(1, 1, 0, 1);
19 | EmitVertex();
20 |
21 | textureCoord = vec2(1, 0);
22 | gl_Position = vec4(1, -1, 0, 1);
23 | EmitVertex();
24 |
25 | EndPrimitive();
26 | }
27 |
--------------------------------------------------------------------------------
/client/resources/shaders/new/fullscreen.vert:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | void main() {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/client/resources/shaders/new/light.frag:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0) in smooth vec2 inPosition;
4 | layout(location = 2) in smooth vec2 solidTextureCoord;
5 |
6 | layout(location = 1) in flat vec3 inColor;
7 | layout(location = 3) in flat vec2 textureCenter;
8 | layout(location = 4) in flat float sampleScale;
9 |
10 | layout(location = 0) out vec4 fragColor; //color attachment 0
11 |
12 | layout(location = 1, binding = 0) uniform sampler2D solidTexture;
13 |
14 | const float SAMPLE_DISTANCE = 0.001;
15 | const float DISSIPATION_FACTOR = 0.0025;
16 |
17 | void main() {
18 | //TODO still need to deal with aspect ratio issues
19 | //texture coords are fewer in one direction based on aspect ratio (usually fewer horizontally since window is wide)
20 | vec2 increment = normalize(solidTextureCoord - textureCenter) * SAMPLE_DISTANCE; //texture coord distance to increment
21 | int samples = int(distance(solidTextureCoord, textureCenter)/length(increment));
22 | float alpha = 1 - length(inPosition); //standard alpha without occlusion
23 |
24 | if(alpha <= 0)
25 | discard;
26 |
27 | for(vec2 texCoord = textureCenter; alpha > 0 && samples > 0; texCoord += increment, samples--) {
28 | alpha -= texture(solidTexture, texCoord).a * DISSIPATION_FACTOR / sampleScale;
29 | }
30 |
31 | fragColor = vec4(inColor, alpha);
32 | }
33 |
--------------------------------------------------------------------------------
/client/resources/shaders/new/light.geom:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | const int NUM_SIDES = 10;
4 | const float PI = 3.1415926;
5 | const float INCREMENT = PI * 2.0 / NUM_SIDES;
6 |
7 | layout(points) in;
8 | layout(triangle_strip, max_vertices = NUM_SIDES * 3) out;
9 |
10 | layout(location = 0) in vec2 position[]; //calculated in Java
11 | layout(location = 1) in vec3 color[];
12 | layout(location = 2) in float intensity[];
13 |
14 | //interpolate relative position, nopserspective since its always a regular square
15 | layout(location = 0) out smooth vec2 outPosition;
16 | layout(location = 2) out smooth vec2 solidTextureCoord;
17 |
18 | layout(location = 1) out flat vec3 outColor;
19 | layout(location = 3) out flat vec2 textureCenter;
20 | layout(location = 4) out flat float sampleScale;
21 |
22 | layout(location = 3) uniform float scale;
23 | layout(location = 0) uniform mat4 view; //TODO dont use a matrix, because rotation is unnecessary and only one scale is needed
24 |
25 | void main() {
26 | //TODO could use polygons to compute fewer fragments https://open.gl/geometry
27 | //TODO could generate a polygon then do the raycasting in here to determine each vertex darkness then interpolate it in the fragment shader (fewer raycasts if done here)
28 | //Generate the actual geometry for the light from the position in world space and light properties
29 |
30 | //set flat values
31 | // outColor = color[0];
32 | // textureCenter = ((view * vec4(position[0], 0, 1)).xy + 1)/2;
33 | // sampleScale = scale; //length((view * vec4(0, 1, 0, 1)).y);
34 | //
35 | // outPosition = vec2(-1, 1);
36 | // gl_Position = view * vec4(position[0] + vec2(-intensity[0], intensity[0]), 0, 1);
37 | // solidTextureCoord = (gl_Position.xy + 1)/2;
38 | // EmitVertex();
39 | //
40 | // outPosition = vec2(-1, -1);
41 | // gl_Position = view * vec4(position[0] - vec2(intensity[0], intensity[0]), 0, 1);
42 | // solidTextureCoord = (gl_Position.xy + 1)/2;
43 | // EmitVertex();
44 | //
45 | // outPosition = vec2(1, 1);
46 | // gl_Position = view * vec4(position[0] + vec2(intensity[0], intensity[0]), 0, 1);
47 | // solidTextureCoord = (gl_Position.xy + 1)/2;
48 | // EmitVertex();
49 | //
50 | // outPosition = vec2(1, -1);
51 | // gl_Position = view * vec4(position[0] + vec2(intensity[0], -intensity[0]), 0, 1);
52 | // solidTextureCoord = (gl_Position.xy + 1)/2;
53 | // EmitVertex();
54 | //
55 | // EndPrimitive();
56 |
57 | vec2 pos = position[0];
58 | float length = intensity[0];
59 | vec4 center = view * vec4(pos, 0, 1);
60 | outColor = color[0];
61 | textureCenter = (center.xy + 1)/2;
62 | sampleScale = scale;
63 |
64 | outPosition = vec2(cos(0), -sin(0));
65 | gl_Position = view * vec4(pos + length * outPosition, 0, 1);
66 | solidTextureCoord = (gl_Position.xy + 1)/2;
67 | EmitVertex();
68 |
69 | float angle = INCREMENT;
70 | for(int i = 0; i <= NUM_SIDES; angle += INCREMENT, i++) {
71 | outPosition = vec2(0, 0);
72 | gl_Position = center;
73 | solidTextureCoord = textureCenter;
74 | EmitVertex();
75 |
76 | outPosition = vec2(cos(angle), -sin(angle));
77 | gl_Position = view * vec4(pos + length * outPosition, 0, 1);
78 | solidTextureCoord = (gl_Position.xy + 1)/2;
79 | EmitVertex();
80 |
81 | EndPrimitive(); //End triangle
82 |
83 | EmitVertex(); //Start new triangle
84 | }
85 | }
--------------------------------------------------------------------------------
/client/resources/shaders/new/light.vert:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0) in vec2 position;
4 | layout(location = 1) in vec3 color;
5 | layout(location = 2) in float intensity;
6 |
7 | layout(location = 0) out vec2 outPosition;
8 | layout(location = 1) out vec3 outColor;
9 | layout(location = 2) out float outIntensity;
10 |
11 | void main() {
12 | outPosition = position;
13 | outColor = color;
14 | outIntensity = intensity;
15 | }
16 |
--------------------------------------------------------------------------------
/client/resources/shaders/new/model.frag:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | //If I ever want multiple outputs:
4 | //https://stackoverflow.com/questions/30025009/in-opengl-is-it-possible-for-one-shader-program-in-one-draw-call-to-render-to?rq=1
5 |
6 | layout(location = 0) in smooth vec2 textureCoord;
7 | layout(location = 1) in flat float opacity;
8 | layout(location = 2) in flat float exposure;
9 |
10 | layout(location = 0) out vec4 color; //color attachment 0
11 |
12 | layout(location = 1, binding = 0) uniform sampler2D atlasTexture;
13 |
14 | void main() {
15 | vec4 texel = texture(atlasTexture, textureCoord);
16 | color = vec4(texel.rgb * exposure, texel.a * opacity);
17 | }
--------------------------------------------------------------------------------
/client/resources/shaders/new/model.vert:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0) in vec2 position;
4 | layout(location = 1) in vec2 textureCoord;
5 |
6 | layout(location = 0) out smooth vec2 outTextureCoord;
7 | layout(location = 1) out flat float opacity;
8 | layout(location = 2) out flat float exposure;
9 |
10 | layout(location = 0) uniform mat4 view;
11 |
12 | struct instance {
13 | float opacity;
14 | float exposure;
15 | mat4 transform;
16 | };
17 |
18 | const int MAX_RENDER_COUNT = 100;
19 |
20 | layout(binding = 1, std140) uniform instance_data { //TODO store the single view matrix in here as well?
21 | int offsets[MAX_RENDER_COUNT]; //TODO pack the offsets into vec4s for improved size (increasing upload speed)
22 | instance instances[MAX_RENDER_COUNT];
23 | };
24 |
25 | void main() {
26 | instance i = instances[offsets[gl_DrawID] + gl_InstanceID];
27 | gl_Position = view * i.transform * vec4(position, 0.0, 1.0);
28 | outTextureCoord = textureCoord;
29 | opacity = i.opacity;
30 | exposure = i.exposure;
31 | }
--------------------------------------------------------------------------------
/client/resources/shaders/new/shading.comp:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0, binding = 0, r8ui) readonly uniform uimage2D solidMap;
4 | layout(location = 1, binding = 1, r8ui) writeonly uniform uimage2D shadingMap;
5 | layout(location = 2) uniform uvec2 bounds;
6 |
7 | const int SUNLIGHT_RADIUS_BLOCKS = 1;
8 | const int DIMENSION = SUNLIGHT_RADIUS_BLOCKS * 2 + 1;
9 | const float MAX_DISTANCE = length(vec2(SUNLIGHT_RADIUS_BLOCKS, SUNLIGHT_RADIUS_BLOCKS));
10 | const float START_RATIO = 0.5f;
11 |
12 | void main() {
13 | ivec2 blockPos = ivec2(bounds.x + gl_WorkGroupID.x, bounds.y + gl_WorkGroupID.y);
14 | float exposure = 0;
15 | //TODO try with ONLY distance from nearest light block, or just weight all distances.??
16 | for(int row = blockPos.y - SUNLIGHT_RADIUS_BLOCKS; row <= blockPos.y + SUNLIGHT_RADIUS_BLOCKS; row++) {
17 | for(int column = blockPos.x - SUNLIGHT_RADIUS_BLOCKS; column <= blockPos.x + SUNLIGHT_RADIUS_BLOCKS; column++) {
18 | //exposure += imageLoad(solidMap, ivec2(column, row)).r;
19 | //TODO implement inverse square law
20 | uint blockLight = imageLoad(solidMap, ivec2(column, row)).r;
21 | float ratio = 1.0 + START_RATIO - distance(vec2(column, row), vec2(blockPos))/MAX_DISTANCE;
22 | float current = (blockLight * ratio)/255.0f;
23 | exposure = max(exposure, current * current);
24 | }
25 | }
26 | imageStore(shadingMap, ivec2(blockPos), uvec4(uint(exposure * 255), 0, 0, 0));
27 |
28 | //Iterate over circular group of points:
29 | //https://stackoverflow.com/questions/40779343/java-loop-through-all-pixels-in-a-2d-circle-with-center-x-y-and-radius
30 | // for (int i = y-r; i < y+r; i++) {
31 | // for (int j = x; (j-x)^2 + (i-y)^2 <= r^2; j--) {
32 | // //in the circle
33 | // }
34 | // for (int j = x+1; (j-x)*(j-x) + (i-y)*(i-y) <= r*r; j++) {
35 | // //in the circle
36 | // }
37 | // }
38 |
39 | //if imageLoad is outside boundary, will return 0
40 | //TODO invert lighting values, 0 should be fully lit
41 | //TODO instead of average, do a sum that weights by distance of light and clamps at fully lit
42 | //where one adjacent fully lit block is enough to outweigh all other blocks dark
43 | }
44 |
--------------------------------------------------------------------------------
/client/resources/shaders/new/shading_apply.frag:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(location = 0) in smooth vec2 worldCoord;
4 |
5 | layout(location = 0) out vec4 color;
6 |
7 | layout(location = 1, binding = 0) uniform usampler2D tileLighting;
8 |
9 | const bool SMOOTH_LIGHTING = true;
10 |
11 | float lighting(ivec2 position) {
12 | return texelFetch(tileLighting, position, 0).r/255.0f;
13 | }
14 |
15 | void main() {
16 | if(SMOOTH_LIGHTING) {
17 | //bilinear interpolation
18 | float ratioX = fract(worldCoord.x);
19 | float ratioY = fract(worldCoord.y);
20 | int left = int(floor(worldCoord.x));
21 | int right = int(ceil(worldCoord.x));
22 | int bottom = int(floor(worldCoord.y));
23 | int top = int(ceil(worldCoord.y));
24 | float lightBottom = mix(lighting(ivec2(left, bottom)), lighting(ivec2(right, bottom)), ratioX);
25 | float lightTop = mix(lighting(ivec2(left, top)), lighting(ivec2(right, top)), ratioX);
26 | color = vec4(0, 0, 0, 1.0 - mix(lightBottom, lightTop, ratioY));
27 | } else {
28 | color = vec4(0, 0, 0, 1.0 - lighting(ivec2(int(worldCoord.x + 0.5), int(worldCoord.y + 0.5))));
29 | }
30 | }
--------------------------------------------------------------------------------
/client/resources/shaders/new/shading_apply.geom:
--------------------------------------------------------------------------------
1 | #version 460 core
2 |
3 | layout(points) in;
4 | layout(triangle_strip, max_vertices = 4) out;
5 |
6 | layout(location = 0) out smooth vec2 worldCoord;
7 |
8 | layout(location = 0) uniform vec4 view; //left x, bottom y, right x, top y
9 |
10 | void main() {
11 | worldCoord = vec2(view[0], view[3]);
12 | gl_Position = vec4(-1, 1, 0, 1);
13 | EmitVertex();
14 |
15 | worldCoord = vec2(view[0], view[1]);
16 | gl_Position = vec4(-1, -1, 0, 1);
17 | EmitVertex();
18 |
19 | worldCoord = vec2(view[2], view[3]);
20 | gl_Position = vec4(1, 1, 0, 1);
21 | EmitVertex();
22 |
23 | worldCoord = vec2(view[2], view[1]);
24 | gl_Position = vec4(1, -1, 0, 1);
25 | EmitVertex();
26 |
27 | EndPrimitive();
28 | }
29 |
--------------------------------------------------------------------------------
/client/resources/shaders/old/model.frag:
--------------------------------------------------------------------------------
1 | #version 410 core
2 |
3 | in vec2 passTextureCoord;
4 | out vec4 pixelColor;
5 | uniform sampler2D atlasTexture;
6 | uniform float opacity, exposure;
7 |
8 | void main() {
9 | vec4 color = texture(atlasTexture, passTextureCoord);
10 | pixelColor = vec4(color.rgb * exposure, color.a * opacity);
11 | }
--------------------------------------------------------------------------------
/client/resources/shaders/old/model.vert:
--------------------------------------------------------------------------------
1 | #version 410 core
2 |
3 | in vec2 position, textureCoord;
4 | out vec2 passTextureCoord;
5 | uniform mat4 view, transform;
6 |
7 | void main() {
8 | gl_Position = view * transform * vec4(position, 0.0, 1.0);
9 | passTextureCoord = textureCoord;
10 | }
--------------------------------------------------------------------------------
/client/src/module-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Contains all Sandbox2D client code, but does not export anything. The client can be
3 | * run by using main class ritzow.sandbox.client.StartClient. TWL's PNGDecoder library
4 | * is required as an automatic module "PNGDecoder". LWJGL modules
5 | * org.lwjgl.opengl/openal/glfw are also required.
6 | * @author Solomon Ritzow
7 | */
8 | module ritzow.sandbox.client {
9 | requires ritzow.sandbox.shared;
10 | requires static java.desktop;
11 | requires java.logging;
12 | requires org.lwjgl;
13 | requires org.lwjgl.glfw;
14 | requires org.lwjgl.openal;
15 | requires org.lwjgl.opengl;
16 | requires org.json;
17 |
18 | exports ritzow.sandbox.client;
19 | }
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/GameLoop.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client;
2 |
3 | import java.util.Objects;
4 | import java.util.concurrent.locks.LockSupport;
5 | import ritzow.sandbox.util.Utility;
6 |
7 | import static ritzow.sandbox.client.data.StandardClientOptions.*;
8 |
9 | class GameLoop {
10 | public interface GameContext {
11 | void run(long deltaNanos);
12 | }
13 |
14 | private static GameContext current;
15 |
16 | private static long lastUpdate, lastUpdateTime;
17 |
18 | public static void start(GameContext initial) {
19 | if(current != null)
20 | throw new IllegalStateException("GameLoop already started");
21 | current = Objects.requireNonNull(initial);
22 | lastUpdate = System.nanoTime();
23 | while(current != null) {
24 | long frameStart = System.nanoTime();
25 | //long frameStartMS = System.currentTimeMillis();
26 | long delta = frameStart - lastUpdate;
27 | current.run(delta);
28 | lastUpdate = Math.max(frameStart, lastUpdate);
29 | lastUpdateTime = delta;
30 | if(LIMIT_FPS) Utility.limitFramerate(frameStart, FRAME_TIME_LIMIT);
31 | //LockSupport.parkUntil(frameStartMS + FRAME_TIME_LIMIT/1_000_000);
32 | if(PRINT_FPS) Utility.printFramerate(frameStart);
33 | }
34 | }
35 |
36 | public static long getLastUpdateTime() {
37 | return lastUpdateTime;
38 | }
39 |
40 | public static long updateDeltaTime() {
41 | long lastTime = lastUpdate;
42 | return (lastUpdate = System.nanoTime()) - lastTime;
43 | }
44 |
45 | public static void setContext(Runnable context) {
46 | current = delta -> context.run();
47 | }
48 |
49 | public static void setContext(GameContext context) {
50 | current = context;
51 | }
52 |
53 | public static void stop() {
54 | current = null;
55 | }
56 | }
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/GameState.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client;
2 |
3 | import ritzow.sandbox.client.graphics.Display;
4 | import ritzow.sandbox.client.graphics.LightRenderProgram;
5 | import ritzow.sandbox.client.graphics.ModelRenderer;
6 | import ritzow.sandbox.client.ui.StandardGuiRenderer;
7 |
8 | class GameState {
9 | private GameState() {}
10 |
11 | private static ModelRenderer modelRenderer;
12 | private static Display display;
13 | private static long cursorPick, cursorMallet;
14 | private static MainMenuContext menuContext;
15 | private static StandardGuiRenderer guiRenderer;
16 | private static LightRenderProgram lightRenderer;
17 |
18 | static Display display() {
19 | return display;
20 | }
21 |
22 | static ModelRenderer modelRenderer() {
23 | return modelRenderer;
24 | }
25 |
26 | static void modelRenderer(ModelRenderer program) {
27 | GameState.modelRenderer = program;
28 | }
29 |
30 | static void setDisplay(Display display) {
31 | GameState.display = display;
32 | }
33 |
34 | static long cursorPick() {
35 | return cursorPick;
36 | }
37 |
38 | static void setCursorPick(long cursorPick) {
39 | GameState.cursorPick = cursorPick;
40 | }
41 |
42 | static long cursorMallet() {
43 | return cursorMallet;
44 | }
45 |
46 | static void setCursorMallet(long cursorMallet) {
47 | GameState.cursorMallet = cursorMallet;
48 | }
49 |
50 | static MainMenuContext menuContext() {
51 | return menuContext;
52 | }
53 |
54 | static void setMenuContext(MainMenuContext menuContext) {
55 | GameState.menuContext = menuContext;
56 | }
57 |
58 | static StandardGuiRenderer guiRenderer() {
59 | return guiRenderer;
60 | }
61 |
62 | static void setGuiRenderer(StandardGuiRenderer guiRenderer) {
63 | GameState.guiRenderer = guiRenderer;
64 | }
65 |
66 | static void setLightRenderer(LightRenderProgram lightRenderer) {
67 | GameState.lightRenderer = lightRenderer;
68 | }
69 |
70 | static LightRenderProgram lightRenderer() {
71 | return lightRenderer;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/StartClient.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client;
2 |
3 | import java.io.IOException;
4 | import org.lwjgl.glfw.GLFWErrorCallback;
5 | import ritzow.sandbox.client.audio.AudioSystem;
6 | import ritzow.sandbox.client.data.StandardClientOptions;
7 | import ritzow.sandbox.client.graphics.Display;
8 | import ritzow.sandbox.client.graphics.RenderManager;
9 | import ritzow.sandbox.client.util.ClientUtility;
10 | import ritzow.sandbox.util.Utility;
11 |
12 | import static org.lwjgl.glfw.GLFW.*;
13 | import static ritzow.sandbox.client.data.StandardClientProperties.CURSORS_PATH;
14 | import static ritzow.sandbox.client.data.StandardClientProperties.TEXTURES_PATH;
15 | import static ritzow.sandbox.client.util.ClientUtility.log;
16 |
17 | /**
18 | * Entry point to Sandbox2D game client
19 | **/
20 | public class StartClient {
21 |
22 | /**
23 | * Command line entry point.
24 | * @param args command line arguments.
25 | * @throws IOException if the program encounters an error.
26 | **/
27 | public static void main(String... args) throws IOException {
28 | log().info("Starting game");
29 | long startupStart = System.nanoTime();
30 | AudioSystem.getDefault(); //load default audio system
31 | setupGLFW();
32 | RenderManager.setup();
33 | GameState.setLightRenderer(RenderManager.LIGHT_RENDERER);
34 | GameState.modelRenderer(RenderManager.MODEL_RENDERER);
35 | MainMenuContext mainMenu = new MainMenuContext();
36 | GameState.setMenuContext(mainMenu);
37 | log().info("Game startup took " + Utility.formatTime(Utility.nanosSince(startupStart)));
38 | GameState.display().show();
39 | GameLoop.start(mainMenu::update);
40 | }
41 |
42 | public static void shutdown() {
43 | log().info("Exiting game");
44 | GameState.modelRenderer().delete();
45 | RenderManager.closeContext();
46 | GameState.display().destroy();
47 | AudioSystem.getDefault().close();
48 | glfwDestroyCursor(GameState.cursorPick());
49 | glfwDestroyCursor(GameState.cursorMallet());
50 | glfwTerminate();
51 | log().info("Game exited");
52 | GameLoop.stop();
53 | }
54 |
55 | private static void setupGLFW() throws IOException {
56 | log().info("Loading GLFW and creating window");
57 | glfwSetErrorCallback(GLFWErrorCallback.createThrow());
58 | if(!glfwInit())
59 | throw new RuntimeException("GLFW failed to initialize");
60 | var appIcon = ClientUtility.loadGLFWImage(TEXTURES_PATH.resolve("redSquare.png"));
61 | var cursorPickaxe = ClientUtility.loadGLFWImage(CURSORS_PATH.resolve("pickaxe32.png"));
62 | var cursorMallet = ClientUtility.loadGLFWImage(CURSORS_PATH.resolve("mallet32.png"));
63 | GameState.setCursorPick(ClientUtility.loadGLFWCursor(cursorPickaxe, 0, 0.66f));
64 | GameState.setCursorMallet(ClientUtility.loadGLFWCursor(cursorMallet, 0, 0.66f));
65 | GameState.setDisplay(new Display(4, StandardClientOptions.USE_OPENGL_4_6 ? 6 : 3, "Sandbox2D", appIcon));
66 | RenderManager.OPENGL_CAPS = GameState.display().setGraphicsContextOnThread();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/audio/AudioData.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client.audio;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | public interface AudioData {
6 | short getBitsPerSample();
7 | boolean isSigned();
8 | short getChannels();
9 | int getSampleRate();
10 | ByteBuffer getData();
11 | }
12 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/audio/AudioSystem.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client.audio;
2 |
3 | import java.io.IOException;
4 | import ritzow.sandbox.client.audio.Sound.StandardSound;
5 |
6 | import static ritzow.sandbox.client.data.StandardClientProperties.AUDIO_PATH;
7 | import static ritzow.sandbox.client.util.ClientUtility.log;
8 |
9 | //TODO audio system should know about camera location and other things
10 | public interface AudioSystem {
11 |
12 | default void playSound(Sound sound, float x, float y, float velocityX, float velocityY) {
13 | playSound(sound, x, y, velocityX, velocityY, 1.0f, 1.0f);
14 | }
15 |
16 | void playSound(Sound sound, float x, float y, float velocityX, float velocityY, float gain, float pitch);
17 | void playSoundGlobal(Sound sound, float gain, float pitch);
18 | void registerSound(Sound sound, AudioData data);
19 | void setVolume(float gain);
20 | void setPosition(float x, float y);
21 | void close();
22 |
23 | static AudioSystem getDefault() {
24 | return Default.DEFAULT;
25 | }
26 |
27 | final class Default {
28 | private Default() {}
29 | private static final AudioSystem DEFAULT = loadDefault();
30 | private static AudioSystem loadDefault() {
31 | try {
32 | log().info("Loading audio system");
33 | OpenALAudioSystem.initOpenAL();
34 | AudioSystem audio = OpenALAudioSystem.create();
35 | audio.setVolume(1.0f);
36 | for(var sound : StandardSound.values()) {
37 | audio.registerSound(sound,
38 | WAVEDecoder.decode(AUDIO_PATH.resolve(sound.fileName())));
39 | }
40 | return audio;
41 | } catch (IOException e) {
42 | throw new RuntimeException(e);
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/audio/JavaxAudioSystem.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client.audio;
2 |
3 | import java.io.IOException;
4 | import java.util.Collection;
5 | import java.util.Map;
6 | import java.util.concurrent.ConcurrentHashMap;
7 | import java.util.concurrent.ConcurrentLinkedQueue;
8 | import java.util.concurrent.ExecutorService;
9 | import java.util.concurrent.Executors;
10 | import javax.sound.sampled.AudioFormat;
11 | import javax.sound.sampled.AudioSystem;
12 | import javax.sound.sampled.DataLine;
13 | import javax.sound.sampled.FloatControl;
14 | import javax.sound.sampled.FloatControl.Type;
15 | import javax.sound.sampled.LineUnavailableException;
16 | import javax.sound.sampled.Mixer;
17 | import javax.sound.sampled.SourceDataLine;
18 |
19 | public final class JavaxAudioSystem implements ritzow.sandbox.client.audio.AudioSystem {
20 | private final Collection lines;
21 | private final DataLine.Info info;
22 | private final Mixer mixer;
23 | private final Map sounds;
24 |
25 | public static JavaxAudioSystem create(AudioData init) throws IOException {
26 | return new JavaxAudioSystem(init);
27 | }
28 |
29 | public JavaxAudioSystem(AudioData init) throws IOException {
30 | try {
31 | mixer = AudioSystem.getMixer(null); //get default mixer
32 | mixer.open();
33 | info = new DataLine.Info(SourceDataLine.class, getFormat(init));
34 | lines = new ConcurrentLinkedQueue<>();
35 | sounds = new ConcurrentHashMap<>();
36 | for(int i = 0; i < 5; i++) {
37 | createLine();
38 | }
39 | } catch (LineUnavailableException e) {
40 | throw new IOException(e);
41 | }
42 | }
43 |
44 | @Override
45 | public void registerSound(Sound id, AudioData data) {
46 | if(sounds.containsKey(id))
47 | throw new IllegalStateException(id + " already registered");
48 | sounds.put(id, getData(data));
49 | }
50 |
51 | private static byte[] getData(AudioData info) {
52 | byte[] data = new byte[info.getData().capacity()];
53 | info.getData().get(data);
54 | return data;
55 | }
56 |
57 | private static AudioFormat getFormat(AudioData info) {
58 | return new AudioFormat(
59 | info.getSampleRate(),
60 | info.getBitsPerSample(),
61 | info.getChannels(),
62 | info.isSigned(),
63 | false
64 | );
65 | }
66 |
67 | private final ExecutorService worker = Executors.newCachedThreadPool();
68 |
69 | @Override
70 | public void playSound(Sound sound, float x, float y, float velocityX, float velocityY, float gain, float pitch) {
71 | worker.execute(()-> {
72 | byte[] data = sounds.get(sound);
73 | if(data == null)
74 | throw new IllegalArgumentException("no such sound");
75 | selectLine().write(data, 0, data.length);
76 | });
77 | }
78 |
79 | private SourceDataLine selectLine() {
80 | synchronized(lines) {
81 | for(var line : lines) {
82 | if(!line.isActive()) {
83 | return line;
84 | }
85 | }
86 | }
87 |
88 | if(!reachedMaxLines()) {
89 | try {
90 | return createLine();
91 | } catch (LineUnavailableException e) {
92 | throw new RuntimeException(e);
93 | }
94 | }
95 | return null;
96 | }
97 |
98 | private boolean reachedMaxLines() {
99 | int max = mixer.getMaxLines(info);
100 | return max != -1 && max > lines.size();
101 | }
102 |
103 | private SourceDataLine createLine() throws LineUnavailableException {
104 | var line = (SourceDataLine)mixer.getLine(info);
105 | line.open(); line.start();
106 | lines.add(line);
107 | return line;
108 | }
109 |
110 | @Override
111 | public void playSoundGlobal(Sound sound, float gain, float pitch) {
112 | throw new UnsupportedOperationException("not implemented");
113 | }
114 |
115 | @Override
116 | public void setVolume(float gain) {
117 | synchronized(lines) {
118 | for(var line : lines) {
119 | ((FloatControl)line.getControl(Type.MASTER_GAIN)).setValue(gain);
120 | }
121 | }
122 | }
123 |
124 | @Override
125 | public void setPosition(float x, float y) {
126 |
127 | }
128 |
129 | @Override
130 | public void close() {
131 | for(var line : lines) {
132 | line.close();
133 | }
134 | mixer.close();
135 | worker.shutdown();
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/audio/OpenALException.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client.audio;
2 |
3 | public class OpenALException extends RuntimeException {
4 |
5 | public OpenALException() {
6 | }
7 |
8 | public OpenALException(String message) {
9 | super(message);
10 | }
11 |
12 | public OpenALException(Throwable cause) {
13 | super(cause);
14 | }
15 |
16 | public OpenALException(String message, Throwable cause) {
17 | super(message, cause);
18 | }
19 |
20 | public OpenALException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
21 | super(message, cause, enableSuppression, writableStackTrace);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/audio/Sound.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client.audio;
2 |
3 | import java.nio.file.Path;
4 |
5 | public interface Sound {
6 |
7 | enum StandardSound implements Sound {
8 | BLOCK_BREAK(Path.of("dig.wav")),
9 | BLOCK_PLACE(Path.of("place.wav")),
10 | POP(Path.of("pop.wav")),
11 | SNAP(Path.of("snap.wav")),
12 | THROW(Path.of("throw.wav"));
13 |
14 | private final Path fileName;
15 |
16 | StandardSound(Path fileName) {
17 | this.fileName = fileName;
18 | }
19 |
20 | public Path fileName() {
21 | return fileName;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/build/CompileShaders.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client.build;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Path;
6 | import java.util.ArrayDeque;
7 | import java.util.Queue;
8 |
9 | public class CompileShaders {
10 | private static final Path DEST = Path.of("resources/shaders/new/out");
11 |
12 | private static final boolean DEBUG = true;
13 |
14 | private static record CompileTask(Process process, String name) {}
15 |
16 | public static void main(String... args) throws IOException, InterruptedException {
17 | Queue processes = new ArrayDeque<>();
18 | for(Path file : Files.newDirectoryStream(Path.of("resources/shaders/new"), Files::isRegularFile)) {
19 | Path destFile = DEST.resolve(file.getFileName() + ".spv");
20 | if(Files.notExists(destFile) || Files.getLastModifiedTime(file).compareTo(Files.getLastModifiedTime(destFile)) > 0) {
21 | processes.add(build(file, destFile));
22 | }
23 | }
24 | while(!processes.isEmpty()) {
25 | CompileTask next = processes.poll();
26 | System.out.printf("%30s compilation exited with value %d%n", next.name, next.process.waitFor());
27 | }
28 | }
29 |
30 | private static CompileTask build(Path file, Path dest) throws IOException {
31 | //https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/
32 | //TODO look into -l option "link all input files together to form a single module" to validate glsl programs and create program files
33 | ProcessBuilder builder = DEBUG ? new ProcessBuilder(
34 | "glslangValidator",
35 | "--target-env", "opengl",
36 | "--client", "opengl100",
37 | "-Od",
38 | "-t",
39 | //"-H", //print opcodes
40 | "-g", //print debug info
41 | "-e", "main",
42 | "-o", dest.toString(),
43 | file.toString()
44 | ) : new ProcessBuilder(
45 | "glslangValidator",
46 | "--target-env", "opengl",
47 | "--client", "opengl100",
48 | "--quiet",
49 | "-Os",
50 | "-t",
51 | DEBUG ? "-g" : "-g0", //strip debug info
52 | "-e", "main",
53 | "-o", dest.toString(),
54 | file.toString()
55 | );
56 |
57 | return new CompileTask(builder.inheritIO().start(), file.getFileName().toString());
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/client/src/ritzow/sandbox/client/data/ClientOptions.java:
--------------------------------------------------------------------------------
1 | package ritzow.sandbox.client.data;
2 |
3 | import java.io.IOException;
4 | import java.nio.charset.StandardCharsets;
5 | import java.nio.file.Files;
6 | import java.util.Map;
7 | import java.util.function.Function;
8 | import java.util.stream.Collectors;
9 |
10 | class ClientOptions {
11 | private static final Map OPTIONS = loadOptions();
12 |
13 | // private static Map loadOptions() {
14 | // try(var in = Files.newInputStream(StandardClientProperties.OPTIONS_PATH)) {
15 | // Properties options = new Properties();
16 | // options.load(in);
17 | // return (Map