├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
├── org.eclipse.jdt.core.prefs
└── org.eclipse.m2e.core.prefs
├── Images
├── ModelViewer.PNG
├── Screenshot00.PNG
├── Screenshot01.PNG
├── Screenshot02.PNG
├── Screenshot03.PNG
├── Screenshot04.PNG
└── SpaceshipGame.PNG
├── LICENSE
├── README.md
├── Tests
├── ModelViewer.jar
└── SpaceshipGame.jar
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── johnsproject
│ └── jgameengine
│ ├── Engine.java
│ ├── EngineStatistics.java
│ ├── EngineWindow.java
│ ├── GraphicsEngine.java
│ ├── InputEngine.java
│ ├── PhysicsEngine.java
│ ├── event
│ ├── EngineEvent.java
│ ├── EngineKeyListener.java
│ ├── EngineListener.java
│ └── EngineMouseListener.java
│ ├── io
│ └── OBJImporter.java
│ ├── model
│ ├── Animation.java
│ ├── AnimationFrame.java
│ ├── Armature.java
│ ├── Camera.java
│ ├── Face.java
│ ├── FrameBuffer.java
│ ├── Frustum.java
│ ├── FrustumType.java
│ ├── Light.java
│ ├── LightType.java
│ ├── Material.java
│ ├── Mesh.java
│ ├── Model.java
│ ├── RigidBody.java
│ ├── Scene.java
│ ├── SceneObject.java
│ ├── Texture.java
│ ├── Transform.java
│ ├── Vertex.java
│ └── VertexGroup.java
│ ├── rasterization
│ ├── LinearRasterizer2.java
│ ├── LinearRasterizer4.java
│ ├── LinearRasterizer6.java
│ └── Rasterizer.java
│ ├── shading
│ ├── BasicShader.java
│ ├── BasicThreadedShader.java
│ ├── DirectionalLightShadowShader.java
│ ├── FlatShader.java
│ ├── ForwardShaderBuffer.java
│ ├── GouraudShader.java
│ ├── PhongShader.java
│ ├── Shader.java
│ ├── ShaderBuffer.java
│ ├── SpotLightShadowShader.java
│ └── ThreadedShader.java
│ └── util
│ ├── ColorUtils.java
│ ├── FileUtils.java
│ ├── FixedPointUtils.java
│ ├── MatrixUtils.java
│ ├── TransformationUtils.java
│ └── VectorUtils.java
└── test
├── java
└── com
│ └── johnsproject
│ └── jgameengine
│ ├── ModelViewer.java
│ ├── SpaceshipGame.java
│ ├── io
│ └── OBJImporterTest.java
│ └── util
│ ├── FixedPointUtilsTest.java
│ ├── MatrixUtilsTest.java
│ └── VectorUtilsTest.java
└── resources
├── DefaultTest.mtl
├── DefaultTest.obj
├── JohnsProjectLogo.png
├── SpaceshipGame
├── Spaceship.mtl
├── Spaceship.obj
├── SpaceshipFire.mtl
├── SpaceshipFire.obj
├── Terrain.mtl
└── Terrain.obj
├── TestOBJ.mtl
└── TestOBJ.obj
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 | .classpath
3 | .project
4 | .gitignore
5 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | JGameEngine
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.m2e.core.maven2Builder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.m2e.core.maven2Nature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/main/java=UTF-8
3 | encoding//src/test/java=UTF-8
4 | encoding/=UTF-8
5 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
6 | org.eclipse.jdt.core.compiler.compliance=1.5
7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
11 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
12 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
13 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
14 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
15 | org.eclipse.jdt.core.compiler.release=disabled
16 | org.eclipse.jdt.core.compiler.source=1.5
17 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=true
4 | version=1
5 |
--------------------------------------------------------------------------------
/Images/ModelViewer.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Images/ModelViewer.PNG
--------------------------------------------------------------------------------
/Images/Screenshot00.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Images/Screenshot00.PNG
--------------------------------------------------------------------------------
/Images/Screenshot01.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Images/Screenshot01.PNG
--------------------------------------------------------------------------------
/Images/Screenshot02.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Images/Screenshot02.PNG
--------------------------------------------------------------------------------
/Images/Screenshot03.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Images/Screenshot03.PNG
--------------------------------------------------------------------------------
/Images/Screenshot04.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Images/Screenshot04.PNG
--------------------------------------------------------------------------------
/Images/SpaceshipGame.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Images/SpaceshipGame.PNG
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 John's Project
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JGameEngine
2 |
3 | is a lightweight 3D game engine written in Java.
4 |
5 | ## TODO
6 | * [x] No dependencies
7 | * [x] Java 1.5+ compatibility
8 | * [x] Fixed point math only
9 | * [ ] Multithreading
10 | * [x] Graphics engine
11 | * [ ] Physics engine
12 | * [ ] Audio engine
13 | * [ ] Networking engine
14 | * [x] Input engine
15 |
16 | Graphics engine
17 | * [x] Scanline triangle rasterization (linear interpolation, no perspective needed as big triangles are culled)
18 | * [x] Shaders (flat, gouraud and phong shaders)
19 | * [x] Multithreaded shaders
20 | * [x] Point, directional and spot lights
21 | * [x] Directional and spot light shadow mapping
22 | * [x] Skeletal animation (experimental)
23 | * [ ] Skybox
24 | * [x] Custom Wavefront OBJ `.obj` importer
25 | * [ ] Animation importer
26 |
27 | Input engine
28 | * [x] Keyboard
29 | * [x] Mouse
30 | * [ ] Gamepad
31 | * [ ] Touch
32 |
33 | ## Tests
34 |
35 | There are some built in tests for the game engine that can be found in the `Tests` folder.
36 | To run the test just double click the `.jar` file.
37 |
38 | Or open a terminal and type ``java -jar TestFile.jar``
39 |
40 | The window size can be configured using the ``window640x480`` argument.
41 |
42 | The render resolution can be set as a percentage of the window size using the ``render75`` argument.
43 |
44 | ## Videos
45 |
46 | Spaceship game
47 |
48 | [](https://www.youtube.com/watch?v=jaY7MnLMf94)
49 |
50 | Model viewer
51 |
52 | [](https://www.youtube.com/watch?v=6UVMvJErhTc)
53 |
54 | ## Screenshots
55 |
56 | Single threaded basic shader (without lights)
57 |
58 | 
59 |
60 | Multithreaded basic shader (without lights)
61 |
62 | 
63 |
64 | Multithreaded flat shading (directional, spot and point lights and directional and spot light shadows)
65 |
66 | 
67 |
68 | Multithreaded gouraud shading (directional, spot and point lights and directional and spot light shadows)
69 |
70 | 
71 |
72 | Multithreaded phong shading (directional, spot and point lights and directional and spot light shadows)
73 |
74 | 
--------------------------------------------------------------------------------
/Tests/ModelViewer.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Tests/ModelViewer.jar
--------------------------------------------------------------------------------
/Tests/SpaceshipGame.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/Tests/SpaceshipGame.jar
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 |
6 | com.johnsproject
7 | JGameEngine
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | JGameEngine
12 | http://maven.apache.org
13 |
14 |
15 | UTF-8
16 |
17 |
18 |
19 |
20 | junit
21 | junit
22 | 4.12
23 | test
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/Engine.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import com.johnsproject.jgameengine.event.EngineEvent;
7 | import com.johnsproject.jgameengine.event.EngineListener;
8 | import com.johnsproject.jgameengine.model.Scene;
9 | import com.johnsproject.jgameengine.util.FixedPointUtils;
10 |
11 | public final class Engine {
12 |
13 | private static Engine engine = new Engine();
14 |
15 | public static Engine getInstance() {
16 | return engine;
17 | }
18 |
19 | private final List engineListeners;
20 | private Scene scene;
21 | private Thread engineThread;
22 | private int maxUpdateSkip;
23 | private int updateRate;
24 | private boolean limitUpdateRate;
25 | private volatile boolean running;
26 | private long currentTime;
27 | private long previousTime;
28 | private long elapsedTime;
29 | private long updateTime;
30 | private int loops;
31 |
32 | private Engine() {
33 | this.running = false;
34 | this.updateRate = 30;
35 | this.maxUpdateSkip = 5;
36 | this.limitUpdateRate = false;
37 | this.engineListeners = new ArrayList();
38 | startEngineLoop();
39 | }
40 |
41 | public void start() {
42 | currentTime = getTime();
43 | running = true;
44 | }
45 |
46 | public void stop() {
47 | running = false;
48 | }
49 |
50 | private void startEngineLoop() {
51 | engineThread = new Thread(new Runnable() {
52 | public void run() {
53 | updateEngine();
54 | }
55 | });
56 | engineThread.setName("JGameEngine");
57 | engineThread.start();
58 | }
59 |
60 | private void updateEngine() {
61 | while (true) {
62 | if (running) {
63 | elapsedTime = getTime() - previousTime;
64 | previousTime = getTime();
65 | updateTime = 1000 / updateRate;
66 | loops = 0;
67 | callFixedUpdate();
68 | callDynamicUpdate(loops << FixedPointUtils.FP_BIT);
69 | limitUpdateRateSleep(updateTime - elapsedTime);
70 | } else {
71 | sleep();
72 | }
73 | }
74 | }
75 |
76 | private void callFixedUpdate() {
77 | EngineEvent event = new EngineEvent(scene, (int) elapsedTime, 0, 0);
78 | while (((currentTime - getTime()) < 0) && (loops < maxUpdateSkip)) {
79 | for (int i = 0; i < engineListeners.size(); i++) {
80 | engineListeners.get(i).fixedUpdate(event);
81 | }
82 | currentTime += updateTime;
83 | loops++;
84 | }
85 | }
86 |
87 | private void callDynamicUpdate(int deltaTime) {
88 | EngineEvent event = new EngineEvent(scene, (int) elapsedTime, 0, deltaTime);
89 | for (int i = 0; i < engineListeners.size(); i++) {
90 | engineListeners.get(i).dynamicUpdate(event);
91 | }
92 | }
93 |
94 | private void limitUpdateRateSleep(long sleepTime) {
95 | if(limitUpdateRate) {
96 | if (sleepTime > 0) {
97 | try {
98 | Thread.sleep(sleepTime);
99 | } catch (InterruptedException e) {
100 | e.printStackTrace();
101 | }
102 | }
103 | }
104 | }
105 |
106 | private void sleep() {
107 | try {
108 | Thread.sleep(30);
109 | } catch (InterruptedException e) {
110 | e.printStackTrace();
111 | }
112 | }
113 |
114 | private long getTime() {
115 | return System.currentTimeMillis();
116 | }
117 |
118 | public void addEngineListener(EngineListener listener) {
119 | listener.initialize(new EngineEvent(scene, 0, 0, 0));
120 | engineListeners.add(listener);
121 | sortListeners();
122 | }
123 |
124 | public void removeEngineListener(EngineListener listener) {
125 | engineListeners.remove(listener);
126 | sortListeners();
127 | }
128 |
129 | public List getEngineListeners() {
130 | return engineListeners;
131 | }
132 |
133 | public int getUpdateRate() {
134 | return updateRate;
135 | }
136 |
137 | public void setUpdateRate(int updateRate) {
138 | this.updateRate = updateRate;
139 | }
140 |
141 | public boolean limitUpdateRate() {
142 | return limitUpdateRate;
143 | }
144 |
145 | public void limitUpdateRate(boolean limitUpdateRate) {
146 | this.limitUpdateRate = limitUpdateRate;
147 | }
148 |
149 | public int getMaxUpdateSkip() {
150 | return maxUpdateSkip;
151 | }
152 |
153 | public void setMaxUpdateSkip(int maxUpdateSkip) {
154 | this.maxUpdateSkip = maxUpdateSkip;
155 | }
156 |
157 | public Scene getScene() {
158 | return scene;
159 | }
160 |
161 | public void setScene(Scene scene) {
162 | this.scene = scene;
163 | }
164 |
165 | private void sortListeners() {
166 | final int listenerCount = engineListeners.size();
167 | for (int i = 0; i < listenerCount; i++) {
168 | int min_i = i;
169 | for (int j = i + 1; j < listenerCount; j++) {
170 | if (engineListeners.get(j).getLayer() < engineListeners.get(min_i).getLayer()) {
171 | min_i = j;
172 | }
173 | }
174 | EngineListener temp = engineListeners.get(min_i);
175 | engineListeners.set(min_i, engineListeners.get(i));
176 | engineListeners.set(i, temp);
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/EngineStatistics.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine;
2 |
3 | import java.awt.Color;
4 | import java.awt.TextArea;
5 | import java.util.List;
6 |
7 | import com.johnsproject.jgameengine.event.EngineEvent;
8 | import com.johnsproject.jgameengine.event.EngineListener;
9 | import com.johnsproject.jgameengine.model.FrameBuffer;
10 | import com.johnsproject.jgameengine.model.Model;
11 |
12 | public class EngineStatistics implements EngineListener {
13 |
14 | private static final int STATISTICS_X = 10;
15 | private static final int STATISTICS_Y = 30;
16 | private static final int STATISTICS_WIDTH = 180;
17 | private static final int STATISTICS_HEIGHT = 130;
18 | private static final Color STATISTICS_BACKROUND = Color.WHITE;
19 |
20 | private static final long BYTE_TO_MEGABYTE = 1024L * 1024L;
21 |
22 | private final TextArea textArea;
23 | private GraphicsEngine graphicsEngine;
24 | private long averageUpdates;
25 | private long loops;
26 |
27 | public EngineStatistics(EngineWindow window) {
28 | this.textArea = new TextArea("", 0, 0, TextArea.SCROLLBARS_NONE);
29 | window.add(textArea, 0);
30 | }
31 |
32 | public void initialize(EngineEvent e) {
33 | textArea.setLocation(STATISTICS_X, STATISTICS_Y);
34 | textArea.setSize(STATISTICS_WIDTH, STATISTICS_HEIGHT);
35 | textArea.setEditable(false);
36 | textArea.setBackground(STATISTICS_BACKROUND);
37 | graphicsEngine = getGraphicsEngine();
38 | }
39 |
40 | private GraphicsEngine getGraphicsEngine() {
41 | GraphicsEngine graphicsEngine = null;
42 | final List engineListeners = Engine.getInstance().getEngineListeners();
43 | for (int i = 0; i < engineListeners.size(); i++) {
44 | final EngineListener engineListener = engineListeners.get(i);
45 | if(engineListener instanceof GraphicsEngine) {
46 | graphicsEngine = (GraphicsEngine) engineListener;
47 | }
48 | }
49 | return graphicsEngine;
50 | }
51 |
52 | public void fixedUpdate(EngineEvent e) {
53 | final String output = getOutput(e);
54 | textArea.setText(output);
55 | }
56 |
57 | public void dynamicUpdate(EngineEvent e) { }
58 |
59 | public int getLayer() {
60 | return GRAPHICS_ENGINE_LAYER - 1;
61 | }
62 |
63 | private String getOutput(EngineEvent e) {
64 | final List models = e.getScene().getModels();
65 |
66 | String output = "== ENGINE STATISTICS ==\n";
67 | output += getRAMUsage();
68 | output += getCPUTime(e.getElapsedUpdateTime() + 1);
69 | output += getFrameBufferSize();
70 | output += getVertexCount(models);
71 | output += getTriangleCount(models);
72 | return output;
73 | }
74 |
75 | private String getRAMUsage() {
76 | final Runtime runtime = Runtime.getRuntime();
77 | final long totalRAM = runtime.totalMemory() / BYTE_TO_MEGABYTE;
78 | final long usedRAM = (runtime.totalMemory() - runtime.freeMemory()) / BYTE_TO_MEGABYTE;
79 | return "RAM usage\t" + usedRAM + " / " + totalRAM + " MB\n";
80 | }
81 |
82 | private String getCPUTime(long elapsedTime) {
83 | final long updates = 1000 / elapsedTime;
84 | averageUpdates += updates;
85 | loops++;
86 | if(loops >= 100) {
87 | averageUpdates = averageUpdates / loops;
88 | loops = 1;
89 | }
90 | String cpuTime = "CPU time\t" + elapsedTime + " ms\n";
91 | cpuTime += "Updates / s\t" + updates + "\n";
92 | cpuTime += "Average U / s\t" + (averageUpdates / loops) + "\n";
93 | return cpuTime;
94 | }
95 |
96 | private String getFrameBufferSize() {
97 | final FrameBuffer frameBuffer = graphicsEngine.getFrameBuffer();
98 | return "Framebuffer\t" + frameBuffer.getWidth() + "x" + frameBuffer.getHeight() + "\n";
99 | }
100 |
101 | private String getVertexCount(List models) {
102 | int vertexCount = 0;
103 | for (int i = 0; i < models.size(); i++)
104 | vertexCount += models.get(i).getMesh().getVertices().length;
105 |
106 | return "Vertices\t\t" + vertexCount + "\n";
107 | }
108 |
109 | private String getTriangleCount(List models) {
110 | int triangleCount = 0;
111 | for (int i = 0; i < models.size(); i++)
112 | triangleCount += models.get(i).getMesh().getFaces().length;
113 |
114 | return "Triangles\t" + triangleCount + "\n";
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/EngineWindow.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine;
2 |
3 | import java.awt.Canvas;
4 | import java.awt.Frame;
5 | import java.awt.Graphics;
6 | import java.awt.event.ComponentAdapter;
7 | import java.awt.event.ComponentEvent;
8 | import java.awt.event.WindowAdapter;
9 | import java.awt.event.WindowEvent;
10 | import java.awt.image.BufferStrategy;
11 |
12 | import com.johnsproject.jgameengine.event.EngineEvent;
13 | import com.johnsproject.jgameengine.event.EngineListener;
14 | import com.johnsproject.jgameengine.model.FrameBuffer;
15 |
16 | public class EngineWindow extends Frame implements EngineListener {
17 |
18 | private static final long serialVersionUID = 1L;
19 |
20 | private final Canvas canvas;
21 | private BufferStrategy bufferStrategy;
22 | private Graphics graphics;
23 | private FrameBuffer frameBuffer;
24 |
25 | public EngineWindow(FrameBuffer frameBuffer) {
26 | canvas = new Canvas();
27 | setLayout(null);
28 | setFrameBuffer(frameBuffer);
29 | setTitle("JGameEngine");
30 | addWindowListener(handleWindowClose());
31 | addComponentListener(handleWindowResize());
32 | add(canvas);
33 | setVisible(true);
34 | }
35 |
36 | private WindowAdapter handleWindowClose() {
37 | return new WindowAdapter() {
38 | public void windowClosing(WindowEvent we) {
39 | System.exit(0);
40 | }
41 | };
42 | }
43 |
44 | public ComponentAdapter handleWindowResize() {
45 | return new ComponentAdapter() {
46 | public void componentResized(ComponentEvent componentEvent) {
47 | recreateCanvasBuffer();
48 | }
49 | };
50 | }
51 |
52 | public void initialize(EngineEvent e) {}
53 |
54 | public void fixedUpdate(EngineEvent e) {}
55 |
56 | public void dynamicUpdate(EngineEvent e) {
57 | synchronized (canvas) {
58 | final int width = getWidth();
59 | final int height = getHeight();
60 | graphics.clearRect(0, 0, width, height);
61 | graphics.drawImage(frameBuffer.getImage(), 0, 0, width, height, null);
62 | bufferStrategy.show();
63 | }
64 | }
65 |
66 | @Override
67 | public void setVisible(boolean isVisible) {
68 | super.setVisible(isVisible);
69 | recreateCanvasBuffer();
70 | }
71 |
72 | private void recreateCanvasBuffer() {
73 | synchronized (canvas) {
74 | if(isVisible()) {
75 | canvas.setSize(getWidth(), getHeight());
76 | canvas.createBufferStrategy(2);
77 | if(bufferStrategy != null) {
78 | graphics.dispose();
79 | bufferStrategy.dispose();
80 | }
81 | bufferStrategy = canvas.getBufferStrategy();
82 | graphics = bufferStrategy.getDrawGraphics();
83 | }
84 | }
85 | }
86 |
87 | public void setFullscreen(boolean isFullscreen) {
88 | dispose();
89 | if(isFullscreen)
90 | setExtendedState(Frame.MAXIMIZED_BOTH);
91 | else
92 | setExtendedState(Frame.NORMAL);
93 | setVisible(true);
94 | }
95 |
96 | public boolean isFullscreen() {
97 | return getExtendedState() != Frame.NORMAL;
98 | }
99 |
100 | public void setBorders(boolean hasBorders) {
101 | dispose();
102 | setUndecorated(!hasBorders);
103 | setVisible(true);
104 | }
105 |
106 | public boolean hasBorders() {
107 | return !isUndecorated();
108 | }
109 |
110 | public void setFrameBuffer(FrameBuffer frameBuffer) {
111 | setSize(frameBuffer.getWidth(), frameBuffer.getHeight());
112 | this.frameBuffer = frameBuffer;
113 | }
114 |
115 | public FrameBuffer getFrameBuffer() {
116 | return frameBuffer;
117 | }
118 |
119 | public int getLayer() {
120 | return GRAPHICS_ENGINE_LAYER + 1;
121 | }
122 |
123 | public Canvas getCanvas() {
124 | return canvas;
125 | }
126 | }
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/GraphicsEngine.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import com.johnsproject.jgameengine.event.EngineEvent;
7 | import com.johnsproject.jgameengine.event.EngineListener;
8 | import com.johnsproject.jgameengine.model.AnimationFrame;
9 | import com.johnsproject.jgameengine.model.Armature;
10 | import com.johnsproject.jgameengine.model.Camera;
11 | import com.johnsproject.jgameengine.model.Face;
12 | import com.johnsproject.jgameengine.model.FrameBuffer;
13 | import com.johnsproject.jgameengine.model.Material;
14 | import com.johnsproject.jgameengine.model.Mesh;
15 | import com.johnsproject.jgameengine.model.Model;
16 | import com.johnsproject.jgameengine.model.Scene;
17 | import com.johnsproject.jgameengine.model.Transform;
18 | import com.johnsproject.jgameengine.model.Vertex;
19 | import com.johnsproject.jgameengine.model.VertexGroup;
20 | import com.johnsproject.jgameengine.shading.DirectionalLightShadowShader;
21 | import com.johnsproject.jgameengine.shading.ForwardShaderBuffer;
22 | import com.johnsproject.jgameengine.shading.GouraudShader;
23 | import com.johnsproject.jgameengine.shading.Shader;
24 | import com.johnsproject.jgameengine.shading.ShaderBuffer;
25 | import com.johnsproject.jgameengine.shading.SpotLightShadowShader;
26 | import com.johnsproject.jgameengine.util.VectorUtils;
27 |
28 | public class GraphicsEngine implements EngineListener {
29 |
30 | private Shader defaultShader;
31 | private final List shaders;
32 | private ShaderBuffer shaderBuffer;
33 | private FrameBuffer frameBuffer;
34 | private final int[] locationVector;
35 | private final int[] normalVector;
36 | private final int[] multiplyVector;
37 |
38 | public GraphicsEngine(FrameBuffer frameBuffer) {
39 | this.shaderBuffer = new ForwardShaderBuffer();
40 | this.shaders = new ArrayList();
41 | this.frameBuffer = frameBuffer;
42 | this.locationVector = VectorUtils.emptyVector();
43 | this.normalVector = VectorUtils.emptyVector();
44 | this.multiplyVector = VectorUtils.emptyVector();
45 | defaultShader = new GouraudShader();
46 | addShader(new DirectionalLightShadowShader());
47 | addShader(new SpotLightShadowShader());
48 | addShader(defaultShader);
49 | }
50 |
51 | public void initialize(EngineEvent e) {}
52 |
53 | public void fixedUpdate(EngineEvent e) {
54 | Scene scene = e.getScene();
55 | for (int i = 0; i < scene.getModels().size(); i++) {
56 | Model model = scene.getModels().get(i);
57 | final Armature armature = model.getArmature();
58 | if(armature != null) {
59 | armature.nextFrame();
60 | }
61 | }
62 | }
63 |
64 | public void dynamicUpdate(EngineEvent e) {
65 | Scene scene = e.getScene();
66 | frameBuffer.getColorBuffer().fill(0);
67 | frameBuffer.getDepthBuffer().fill(Integer.MAX_VALUE);
68 | frameBuffer.getStencilBuffer().fill(0);
69 | localToWorldSpace(scene);
70 | renderForEachCamera(scene);
71 | }
72 |
73 | private void localToWorldSpace(Scene scene) {
74 | for (int i = 0; i < scene.getModels().size(); i++) {
75 | Model model = scene.getModels().get(i);
76 | if(!model.isActive())
77 | continue;
78 | final Mesh mesh = model.getMesh();
79 | final Armature armature = model.getArmature();
80 | final Transform transform = model.getTransform();
81 | transformVertices(mesh, transform, armature);
82 | transformFaces(mesh, transform);
83 | }
84 | }
85 |
86 | private void transformVertices(Mesh mesh, Transform transform, Armature armature) {
87 | AnimationFrame animationFrame = null;
88 | if(armature != null) {
89 | animationFrame = armature.getCurrentAnimationFrame();
90 | }
91 | for (int v = 0; v < mesh.getVertices().length; v++) {
92 | final Vertex vertex = mesh.getVertex(v);
93 | VectorUtils.copy(vertex.getWorldLocation(), vertex.getLocalLocation());
94 | VectorUtils.copy(vertex.getWorldNormal(), VectorUtils.VECTOR_ZERO);
95 | animateVertex(armature, animationFrame, vertex);
96 | VectorUtils.multiply(vertex.getWorldLocation(), transform.getSpaceExitMatrix());
97 | }
98 | }
99 |
100 | private void animateVertex(Armature armature, AnimationFrame animationFrame, Vertex vertex) {
101 | if(animationFrame != null) {
102 | VectorUtils.copy(locationVector, VectorUtils.VECTOR_ZERO);
103 | VectorUtils.copy(normalVector, VectorUtils.VECTOR_ZERO);
104 | for (int i = 0; i < armature.getVertexGroups().length; i++) {
105 | final VertexGroup vertexGroup = armature.getVertexGroup(i);
106 | final int boneWeight = vertexGroup.getWeight(vertex);
107 | if(boneWeight != -1) {
108 | final int[][] boneMatrix = animationFrame.getBoneMatrix(vertexGroup.getBoneIndex());
109 | applyBone(vertex, boneWeight, boneMatrix);
110 | }
111 | }
112 | VectorUtils.copy(vertex.getWorldLocation(), locationVector);
113 | VectorUtils.copy(vertex.getWorldNormal(), normalVector);
114 | }
115 | }
116 |
117 | private void applyBone(Vertex vertex, int boneWeight, int[][] boneMatrix) {
118 | VectorUtils.copy(multiplyVector, vertex.getWorldLocation());
119 | VectorUtils.multiply(multiplyVector, boneMatrix);
120 | VectorUtils.multiply(multiplyVector, boneWeight);
121 | VectorUtils.add(locationVector, multiplyVector);
122 | VectorUtils.copy(multiplyVector, vertex.getWorldNormal());
123 | VectorUtils.multiply(multiplyVector, boneMatrix);
124 | VectorUtils.multiply(multiplyVector, boneWeight);
125 | VectorUtils.add(normalVector, multiplyVector);
126 | }
127 |
128 | private void transformFaces(Mesh mesh, Transform transform) {
129 | for (int f = 0; f < mesh.getFaces().length; f++) {
130 | final Face face = mesh.getFace(f);
131 | VectorUtils.copy(face.getWorldNormal(), face.getLocalNormal());
132 | VectorUtils.multiply(face.getWorldNormal(), transform.getSpaceExitNormalMatrix());
133 | // calculate vertex normals, just add the face the normals of the faces this vertex is a part of
134 | VectorUtils.add(face.getVertex(0).getWorldNormal(), face.getWorldNormal());
135 | VectorUtils.add(face.getVertex(1).getWorldNormal(), face.getWorldNormal());
136 | VectorUtils.add(face.getVertex(2).getWorldNormal(), face.getWorldNormal());
137 | }
138 | }
139 |
140 | private void renderForEachCamera(Scene scene) {
141 | for (int c = 0; c < scene.getCameras().size(); c++) {
142 | Camera camera = scene.getCameras().get(c);
143 | if(!camera.isActive())
144 | continue;
145 | camera.setRenderTarget(frameBuffer);
146 | shaderBuffer.initialize(camera, scene.getLights());
147 | renderModels(scene);
148 | }
149 | }
150 |
151 | private void renderModels(Scene scene) {
152 | for (int s = 0; s < shaders.size(); s++) {
153 | Shader shader = shaders.get(s);
154 | shader.initialize(shaderBuffer);
155 | for (int m = 0; m < scene.getModels().size(); m++) {
156 | Model model = scene.getModels().get(m);
157 | if(!model.isActive() || model.isCulled())
158 | continue;
159 | final Mesh mesh = model.getMesh();
160 | shadeVertices(mesh, shader);
161 | shader.waitForVertexQueue();
162 |
163 | shadeFaces(mesh, shader);
164 | shader.waitForGeometryQueue();
165 | }
166 | }
167 | }
168 |
169 | private void shadeVertices(Mesh mesh, Shader shader) {
170 | for (int v = 0; v < mesh.getVertices().length; v++) {
171 | final Vertex vertex = mesh.getVertex(v);
172 | final Material material = vertex.getMaterial();
173 | if(canUseShader(shader, material)) {
174 | shader.vertex(vertex);
175 | }
176 | }
177 | }
178 |
179 | private void shadeFaces(Mesh mesh, Shader shader) {
180 | for (int f = 0; f < mesh.getFaces().length; f++) {
181 | final Face face = mesh.getFace(f);
182 | final Material material = face.getMaterial();
183 | if(canUseShader(shader, material)) {
184 | shader.geometry(face);
185 | }
186 | }
187 | }
188 |
189 | private boolean canUseShader(Shader shader, Material material) {
190 | return ((material.getShader() == null) && shader.equals(defaultShader))
191 | || shader.equals(material.getShader())
192 | || shader.isGlobal();
193 | }
194 |
195 | public int getLayer() {
196 | return GRAPHICS_ENGINE_LAYER;
197 | }
198 |
199 | public ShaderBuffer getShaderBuffer() {
200 | return shaderBuffer;
201 | }
202 |
203 | public void initialize(ShaderBuffer shaderBuffer) {
204 | this.shaderBuffer = shaderBuffer;
205 | }
206 |
207 | public void setFrameBuffer(FrameBuffer frameBuffer) {
208 | this.frameBuffer = frameBuffer;
209 | }
210 |
211 | public FrameBuffer getFrameBuffer() {
212 | return frameBuffer;
213 | }
214 |
215 | public List getShaders() {
216 | return shaders;
217 | }
218 |
219 | public Shader getShader(int index) {
220 | return shaders.get(index);
221 | }
222 |
223 | public void addShader(Shader shader) {
224 | shaders.add(shader);
225 | }
226 |
227 | public void removeShader(Shader shader) {
228 | shaders.remove(shader);
229 | }
230 |
231 | public Shader getDefaultShader() {
232 | return defaultShader;
233 | }
234 |
235 | public void setDefaultShader(Shader defaultShader) {
236 | this.defaultShader = defaultShader;
237 | }
238 | }
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/InputEngine.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine;
2 |
3 | import java.awt.AWTEvent;
4 | import java.awt.Point;
5 | import java.awt.Toolkit;
6 | import java.awt.event.AWTEventListener;
7 | import java.awt.event.KeyEvent;
8 | import java.awt.event.MouseEvent;
9 | import java.awt.event.MouseMotionListener;
10 | import java.awt.event.MouseWheelEvent;
11 | import java.awt.event.MouseWheelListener;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | import com.johnsproject.jgameengine.event.EngineEvent;
16 | import com.johnsproject.jgameengine.event.EngineKeyListener;
17 | import com.johnsproject.jgameengine.event.EngineListener;
18 | import com.johnsproject.jgameengine.event.EngineMouseListener;
19 |
20 | public class InputEngine implements EngineListener {
21 |
22 | private static final long AWT_EVENT_MASK = AWTEvent.KEY_EVENT_MASK
23 | + AWTEvent.MOUSE_EVENT_MASK
24 | + AWTEvent.MOUSE_MOTION_EVENT_MASK
25 | + AWTEvent.MOUSE_WHEEL_EVENT_MASK;
26 |
27 | private static final int KEY_HOLD = KeyEvent.KEY_LAST + 1;
28 | private static final int MOUSE_HOLD = KeyEvent.KEY_LAST + 2;
29 |
30 | private static class InputEvent {
31 |
32 | private final int type;
33 | private final KeyEvent keyEvent;
34 | private final MouseEvent mouseEvent;
35 | private final MouseWheelEvent mouseWheelEvent;
36 |
37 | public InputEvent(int type, KeyEvent keyEvent, MouseEvent mouseEvent, MouseWheelEvent mouseWheelEvent) {
38 | this.type = type;
39 | this.keyEvent = keyEvent;
40 | this.mouseEvent = mouseEvent;
41 | this.mouseWheelEvent = mouseWheelEvent;
42 | }
43 |
44 | public int getType() {
45 | return type;
46 | }
47 |
48 | public KeyEvent getKeyEvent() {
49 | return keyEvent;
50 | }
51 |
52 | public MouseEvent getMouseEvent() {
53 | return mouseEvent;
54 | }
55 |
56 | public MouseWheelEvent getMouseWheelEvent() {
57 | return mouseWheelEvent;
58 | }
59 | }
60 |
61 | private Point mouseLocation;
62 | private Point mouseLocationOnScreen;
63 | private final List keyListeners;
64 | private final List mouseListeners;
65 | private final List motionListeners;
66 | private final List wheelListeners;
67 | private final List inputEvents;
68 |
69 | public InputEngine() {
70 | this.mouseLocation = new Point();
71 | this.mouseLocationOnScreen = new Point();
72 | this.inputEvents = new ArrayList();
73 | this.keyListeners = new ArrayList();
74 | this.mouseListeners = new ArrayList();
75 | this.wheelListeners = new ArrayList();
76 | this.motionListeners = new ArrayList();
77 | }
78 |
79 | public Point getMouseLocation() {
80 | return mouseLocation;
81 | }
82 |
83 | public Point getMouseLocationOnScreen() {
84 | return mouseLocationOnScreen;
85 | }
86 |
87 | public boolean isKeyPressed(int keyCode) {
88 | for (int i = 0; i < inputEvents.size(); i++) {
89 | final InputEvent event = inputEvents.get(i);
90 | if((event.getType() == KEY_HOLD) && (event.getKeyEvent().getKeyCode() == keyCode))
91 | return true;
92 | }
93 | return false;
94 | }
95 |
96 | public boolean isMousePressed(int button) {
97 | for (int i = 0; i < inputEvents.size(); i++) {
98 | final InputEvent event = inputEvents.get(i);
99 | if((event.getType() == MOUSE_HOLD) && (event.getMouseEvent().getButton() == button))
100 | return true;
101 | }
102 | return false;
103 | }
104 |
105 | public void addEngineKeyListener(EngineKeyListener listener) {
106 | keyListeners.add(listener);
107 | }
108 |
109 | public void removeEngineKeyListener(EngineKeyListener listener) {
110 | keyListeners.remove(listener);
111 | }
112 |
113 | public void addEngineMouseListener(EngineMouseListener listener) {
114 | mouseListeners.add(listener);
115 | }
116 |
117 | public void removeEngineMouseListener(EngineMouseListener listener) {
118 | mouseListeners.remove(listener);
119 | }
120 |
121 | public void addMouseWheelListener(MouseWheelListener listener) {
122 | wheelListeners.add(listener);
123 | }
124 |
125 | public void removeMouseWheelListener(MouseWheelListener listener) {
126 | wheelListeners.remove(listener);
127 | }
128 |
129 | public void addMouseMotionListener(MouseMotionListener listener) {
130 | motionListeners.add(listener);
131 | }
132 |
133 | public void removeMouseMotionListener(MouseMotionListener listener) {
134 | motionListeners.remove(listener);
135 | }
136 |
137 | public void initialize(EngineEvent e) {
138 | Toolkit.getDefaultToolkit().addAWTEventListener(handleEvent(), AWT_EVENT_MASK);
139 | }
140 |
141 | private AWTEventListener handleEvent() {
142 | return new AWTEventListener() {
143 | public void eventDispatched(AWTEvent event) {
144 | if (event instanceof KeyEvent) {
145 | KeyEvent e = (KeyEvent) event;
146 | handleKeyEvent(e);
147 | }
148 | if (event instanceof MouseEvent) {
149 | MouseEvent e = (MouseEvent) event;
150 | mouseLocation = e.getPoint();
151 | mouseLocationOnScreen = e.getLocationOnScreen();
152 | handleMouseEvent(e);
153 | handleMouseMotionEvent(e);
154 | }
155 | if (event instanceof MouseWheelEvent) {
156 | MouseWheelEvent e = (MouseWheelEvent) event;
157 | handleMouseWheelEvent(e);
158 | }
159 | }
160 | };
161 | }
162 |
163 | private void handleKeyEvent(KeyEvent e) {
164 | final int eventType = e.getID();
165 | int eventIndex;
166 | synchronized (inputEvents) {
167 | switch (eventType) {
168 | case KeyEvent.KEY_TYPED:
169 | eventIndex = getKeyEventIndex(e, KeyEvent.KEY_TYPED);
170 | if(eventIndex < 0)
171 | inputEvents.add(new InputEvent(eventType, e, null, null));
172 | break;
173 |
174 | case KeyEvent.KEY_PRESSED:
175 | eventIndex = getKeyEventIndex(e, KEY_HOLD);
176 | if(eventIndex < 0) {
177 | inputEvents.add(new InputEvent(eventType, e, null, null));
178 | inputEvents.add(new InputEvent(KEY_HOLD, e, null, null));
179 | }
180 | break;
181 |
182 | case KeyEvent.KEY_RELEASED:
183 | eventIndex = getKeyEventIndex(e, KEY_HOLD);
184 | if(eventIndex >= 0) {
185 | inputEvents.remove(eventIndex);
186 | }
187 | inputEvents.add(new InputEvent(eventType, e, null, null));
188 | break;
189 | }
190 | }
191 | }
192 |
193 | private int getKeyEventIndex(KeyEvent e, int eventType) {
194 | synchronized (inputEvents) {
195 | for (int i = 0; i < inputEvents.size(); i++) {
196 | final InputEvent event = inputEvents.get(i);
197 | if(event == null)
198 | continue;
199 | if((event.getType() == eventType) && (event.getKeyEvent().getKeyCode() == e.getKeyCode())) {
200 | return i;
201 | }
202 | }
203 | }
204 | return -1;
205 | }
206 |
207 | private void handleMouseEvent(MouseEvent e) {
208 | final int eventType = e.getID();
209 | synchronized (inputEvents) {
210 | inputEvents.add(new InputEvent(eventType, null, e, null));
211 | switch (eventType) {
212 | case MouseEvent.MOUSE_PRESSED:
213 | inputEvents.add(new InputEvent(MOUSE_HOLD, null, e, null));
214 | break;
215 |
216 | case MouseEvent.MOUSE_RELEASED:
217 | for (int i = 0; i < inputEvents.size(); i++) {
218 | final InputEvent event = inputEvents.get(i);
219 | if(event == null)
220 | continue;
221 | if((event.getType() == MOUSE_HOLD) && (event.getMouseEvent().getButton() == e.getButton())) {
222 | inputEvents.remove(i);
223 | }
224 | }
225 | break;
226 | }
227 | }
228 | }
229 |
230 | private void handleMouseMotionEvent(MouseEvent e) {
231 | final int eventType = e.getID();
232 | synchronized (inputEvents) {
233 | inputEvents.add(new InputEvent(eventType, null, e, null));
234 | }
235 | }
236 |
237 | private void handleMouseWheelEvent(MouseWheelEvent e) {
238 | final int eventType = e.getID();
239 | synchronized (inputEvents) {
240 | inputEvents.add(new InputEvent(eventType, null, null, e));
241 | }
242 | }
243 |
244 | public void fixedUpdate(EngineEvent e) {
245 | synchronized (inputEvents) {
246 | for (int i = 0; i < inputEvents.size(); i++) {
247 | final InputEvent event = inputEvents.get(i);
248 | if(event == null)
249 | continue;
250 | switch (event.getType()) {
251 | case KeyEvent.KEY_PRESSED:
252 | for (int l = 0; l < keyListeners.size(); l++) {
253 | keyListeners.get(l).keyPressed(event.getKeyEvent());
254 | }
255 | break;
256 |
257 | case KeyEvent.KEY_RELEASED:
258 | for (int l = 0; l < keyListeners.size(); l++) {
259 | keyListeners.get(l).keyReleased(event.getKeyEvent());
260 | }
261 | break;
262 |
263 | case KeyEvent.KEY_TYPED:
264 | for (int l = 0; l < keyListeners.size(); l++) {
265 | keyListeners.get(l).keyTyped(event.getKeyEvent());
266 | }
267 | break;
268 |
269 | case KEY_HOLD:
270 | for (int l = 0; l < keyListeners.size(); l++) {
271 | keyListeners.get(l).keyHold(event.getKeyEvent());
272 | }
273 | break;
274 |
275 | case MouseEvent.MOUSE_PRESSED:
276 | for (int l = 0; l < mouseListeners.size(); l++) {
277 | mouseListeners.get(l).mousePressed(event.getMouseEvent());
278 | }
279 | break;
280 |
281 | case MouseEvent.MOUSE_RELEASED:
282 | for (int l = 0; l < mouseListeners.size(); l++) {
283 | mouseListeners.get(l).mouseReleased(event.getMouseEvent());
284 | }
285 | break;
286 |
287 | case MouseEvent.MOUSE_CLICKED:
288 | for (int l = 0; l < mouseListeners.size(); l++) {
289 | mouseListeners.get(l).mouseClicked(event.getMouseEvent());
290 | }
291 | break;
292 |
293 | case MOUSE_HOLD:
294 | for (int l = 0; l < mouseListeners.size(); l++) {
295 | mouseListeners.get(l).mouseHold(event.getMouseEvent());
296 | }
297 | break;
298 |
299 | case MouseEvent.MOUSE_EXITED:
300 | for (int l = 0; l < mouseListeners.size(); l++) {
301 | mouseListeners.get(l).mouseExited(event.getMouseEvent());
302 | }
303 | break;
304 |
305 | case MouseEvent.MOUSE_ENTERED:
306 | for (int l = 0; l < mouseListeners.size(); l++) {
307 | mouseListeners.get(l).mouseEntered(event.getMouseEvent());
308 | }
309 | break;
310 | case MouseEvent.MOUSE_MOVED:
311 | for (int l = 0; l < motionListeners.size(); l++) {
312 | motionListeners.get(l).mouseMoved(event.getMouseEvent());
313 | }
314 | break;
315 |
316 | case MouseEvent.MOUSE_DRAGGED:
317 | for (int l = 0; l < motionListeners.size(); l++) {
318 | motionListeners.get(l).mouseDragged(event.getMouseEvent());
319 | }
320 | break;
321 |
322 | case MouseWheelEvent.WHEEL_UNIT_SCROLL:
323 | for (int l = 0; l < wheelListeners.size(); l++) {
324 | wheelListeners.get(l).mouseWheelMoved(event.getMouseWheelEvent());
325 | }
326 | break;
327 |
328 | case MouseWheelEvent.WHEEL_BLOCK_SCROLL:
329 | for (int l = 0; l < wheelListeners.size(); l++) {
330 | wheelListeners.get(l).mouseWheelMoved(event.getMouseWheelEvent());
331 | }
332 | break;
333 | }
334 | if((event.getType() != KEY_HOLD) && (event.getType() != MOUSE_HOLD)) {
335 | inputEvents.remove(i);
336 | }
337 | }
338 | }
339 | }
340 |
341 | public void dynamicUpdate(EngineEvent e) { }
342 |
343 | public int getLayer() {
344 | return INPUT_ENGINE_LAYER;
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/PhysicsEngine.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine;
2 |
3 | import com.johnsproject.jgameengine.event.EngineEvent;
4 | import com.johnsproject.jgameengine.event.EngineListener;
5 | import com.johnsproject.jgameengine.model.RigidBody;
6 | import com.johnsproject.jgameengine.model.Scene;
7 | import com.johnsproject.jgameengine.model.SceneObject;
8 | import com.johnsproject.jgameengine.model.Transform;
9 | import com.johnsproject.jgameengine.util.FixedPointUtils;
10 | import com.johnsproject.jgameengine.util.VectorUtils;
11 |
12 | public class PhysicsEngine implements EngineListener {
13 |
14 | public static final int FP_EARTH_GRAVITY = FixedPointUtils.toFixedPoint(-9.81f);
15 |
16 | private final int[] vectorCache1;
17 |
18 | public PhysicsEngine() {
19 | this.vectorCache1 = VectorUtils.emptyVector();
20 | }
21 |
22 | public void initialize(EngineEvent e) {
23 |
24 | }
25 |
26 | public void fixedUpdate(EngineEvent e) {
27 | // final Scene scene = e.getScene();
28 | // for (int i = 0; i < scene.getSceneObjects().size(); i++) {
29 | // final SceneObject sceneObject = scene.getSceneObject(i);
30 | // if(!sceneObject.isActive())
31 | // return;
32 | // final RigidBody rigidBody = sceneObject.getRigidBody();
33 | // final Transform transform = sceneObject.getTransform();
34 | // applyForces(sceneObject);
35 | // int[] linearAcceleration = VectorLibrary.divide(rigidBody.getForce(), rigidBody.getMass(), vectorCache1);
36 | //// VectorLibrary.multiply(linearAcceleration, e.getDeltaTime(), linearAcceleration);
37 | // int[] linearVelocity = VectorLibrary.add(rigidBody.getLinearVelocity(), linearAcceleration, rigidBody.getLinearVelocity());
38 | // VectorLibrary.add(transform.getLocation(), linearVelocity, transform.getLocation());
39 | // int[] angularAcceleration = VectorLibrary.divide(rigidBody.getTorque(), rigidBody.getMass(), vectorCache1);
40 | //// VectorLibrary.multiply(angularAcceleration, e.getDeltaTime(), angularAcceleration);
41 | // VectorLibrary.multiply(angularAcceleration, MathLibrary.FP_RAD_DEGREE, angularAcceleration);
42 | // int[] angularVelocity = VectorLibrary.add(rigidBody.getAngularVelocity(), angularAcceleration, rigidBody.getAngularVelocity());
43 | // VectorLibrary.add(transform.getRotation(), angularVelocity, transform.getRotation());
44 | // }
45 | }
46 |
47 | private void applyForces(SceneObject sceneObject) {
48 | final RigidBody rigidBody = sceneObject.getRigidBody();
49 | if(!rigidBody.isKinematic()) {
50 | int gravity = FixedPointUtils.multiply(rigidBody.getMass(), FP_EARTH_GRAVITY);
51 | rigidBody.setForce(0, 0, gravity);
52 | }
53 | }
54 |
55 | public void dynamicUpdate(EngineEvent e) {
56 | final Scene scene = e.getScene();
57 | for (int i = 0; i < scene.getSceneObjects().size(); i++) {
58 | SceneObject sceneObject = scene.getSceneObjects().get(i);
59 | if(!sceneObject.isActive())
60 | return;
61 | final RigidBody rigidBody = sceneObject.getRigidBody();
62 | final Transform transform = sceneObject.getTransform();
63 | applyForces(sceneObject);
64 | int[] linearAcceleration = VectorUtils.copy(vectorCache1, rigidBody.getForce());
65 | VectorUtils.divide(linearAcceleration, rigidBody.getMass());
66 | VectorUtils.multiply(linearAcceleration, e.getDeltaTime());
67 | rigidBody.addLinearVelocity(linearAcceleration);
68 | int[] linearVelocity = VectorUtils.copy(vectorCache1, rigidBody.getLinearVelocity());
69 | VectorUtils.multiply(linearVelocity, e.getDeltaTime());
70 | transform.worldTranslate(linearVelocity);
71 | int[] angularAcceleration = VectorUtils.copy(vectorCache1, rigidBody.getTorque());
72 | VectorUtils.divide(angularAcceleration, rigidBody.getMass());
73 | VectorUtils.multiply(angularAcceleration, e.getDeltaTime());
74 | rigidBody.addAngularVelocity(angularAcceleration);
75 | int[] angularVelocity = VectorUtils.copy(vectorCache1, rigidBody.getAngularVelocity());
76 | VectorUtils.multiply(angularVelocity, e.getDeltaTime());
77 | VectorUtils.multiply(angularVelocity, FixedPointUtils.FP_RAD_DEGREE);
78 | transform.worldRotate(angularVelocity);
79 | }
80 | }
81 |
82 | public int getLayer() {
83 | return PHYSICS_ENGINE_LAYER;
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/event/EngineEvent.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.event;
2 |
3 | import com.johnsproject.jgameengine.model.Scene;
4 |
5 | public class EngineEvent {
6 |
7 | private final Scene scene;
8 | private final int elapsedUpdateTime;
9 | private final int sleepTime;
10 | private final int deltaTime;
11 |
12 | public EngineEvent(Scene scene, int elapsedUpdateTime, int sleepTime, int deltaTime) {
13 | this.scene = scene;
14 | this.elapsedUpdateTime = elapsedUpdateTime;
15 | this.sleepTime = sleepTime;
16 | this.deltaTime = deltaTime;
17 | }
18 |
19 | public Scene getScene() {
20 | return scene;
21 | }
22 |
23 | public int getElapsedUpdateTime() {
24 | return elapsedUpdateTime;
25 | }
26 |
27 | public int getSleepTime() {
28 | return sleepTime;
29 | }
30 |
31 | public int getDeltaTime() {
32 | return deltaTime;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/event/EngineKeyListener.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.event;
2 |
3 | import java.awt.event.KeyEvent;
4 | import java.awt.event.KeyListener;
5 |
6 | public interface EngineKeyListener extends KeyListener {
7 |
8 | public void keyHold(KeyEvent e);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/event/EngineListener.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.event;
2 |
3 | public interface EngineListener {
4 |
5 | public static final int INPUT_ENGINE_LAYER = -100;
6 | public static final int DEFAULT_LAYER = 0;
7 | public static final int PHYSICS_ENGINE_LAYER = 99;
8 | public static final int GRAPHICS_ENGINE_LAYER = 100;
9 |
10 | public void initialize(EngineEvent e);
11 |
12 | public void fixedUpdate(EngineEvent e);
13 |
14 | public void dynamicUpdate(EngineEvent e);
15 |
16 | public int getLayer();
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/event/EngineMouseListener.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.event;
2 |
3 | import java.awt.event.MouseEvent;
4 | import java.awt.event.MouseListener;
5 |
6 | public interface EngineMouseListener extends MouseListener {
7 |
8 | public void mouseHold(MouseEvent e);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Animation.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class Animation {
4 |
5 | private final String name;
6 | private final AnimationFrame[] frames;
7 |
8 | public Animation(String name, AnimationFrame[] frames) {
9 | this.name = name;
10 | this.frames = frames;
11 | }
12 |
13 | public String getName() {
14 | return name;
15 | }
16 |
17 | public AnimationFrame getFrame(int index) {
18 | return frames[index];
19 | }
20 |
21 | public AnimationFrame[] getFrames() {
22 | return frames;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/AnimationFrame.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class AnimationFrame {
4 |
5 | private final int[][][] boneMatrices;
6 |
7 | public AnimationFrame(int[][][] boneMatrices) {
8 | this.boneMatrices = boneMatrices;
9 | }
10 |
11 | public int[][] getBoneMatrix(int index) {
12 | return boneMatrices[index];
13 | }
14 |
15 | public int[][][] getBoneMatrices() {
16 | return boneMatrices;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Armature.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class Armature {
4 |
5 | private final VertexGroup[] vertexGroups;
6 | private final Animation[] animations;
7 | private Animation currentAnimation;
8 | private int currentFrame;
9 | private int animationSpeed;
10 | private boolean loopAnimation;
11 |
12 | public Armature(VertexGroup[] vertexGroups, Animation[] animations) {
13 | this.vertexGroups = vertexGroups;
14 | this.animations = animations;
15 | this.animationSpeed = 1;
16 | this.currentFrame = 0;
17 | }
18 |
19 | public VertexGroup getVertexGroup(int index) {
20 | return vertexGroups[index];
21 | }
22 |
23 | public VertexGroup[] getVertexGroups() {
24 | return vertexGroups;
25 | }
26 |
27 | public Animation getAnimation(int index) {
28 | return animations[index];
29 | }
30 |
31 | public Animation[] getAnimations() {
32 | return animations;
33 | }
34 |
35 | public void playAnimation(String name, boolean loop) {
36 | for (int i = 0; i < animations.length; i++) {
37 | if(animations[i].getName().equals(name)) {
38 | playAnimation(i, loop);
39 | }
40 | }
41 | }
42 |
43 | public void playAnimation(int index, boolean loop) {
44 | loopAnimation = loop;
45 | currentAnimation = animations[index];
46 | }
47 |
48 | public Animation getCurrentAnimation() {
49 | return currentAnimation;
50 | }
51 |
52 | public AnimationFrame getCurrentAnimationFrame() {
53 | if(currentAnimation == null) {
54 | return null;
55 | } else {
56 | return currentAnimation.getFrame(getCurrentFrame());
57 | }
58 | }
59 |
60 | public int getCurrentFrame() {
61 | return currentFrame;
62 | }
63 |
64 | public void nextFrame() {
65 | currentFrame += animationSpeed;
66 | if(!isPlaying()) {
67 | if(loopAnimation) {
68 | currentFrame = 0;
69 | } else {
70 | stopPlaying();
71 | }
72 | }
73 | }
74 |
75 | public boolean isPlaying() {
76 | if(currentAnimation == null) {
77 | return false;
78 | } else {
79 | return getCurrentFrame() < currentAnimation.getFrames().length;
80 | }
81 | }
82 |
83 | public void stopPlaying() {
84 | currentFrame = 0;
85 | currentAnimation = null;
86 | }
87 |
88 | public int getAnimationSpeed() {
89 | return animationSpeed;
90 | }
91 |
92 | public void setAnimationSpeed(int animationSpeed) {
93 | this.animationSpeed = animationSpeed;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Camera.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_ONE;
4 |
5 | public class Camera extends SceneObject {
6 |
7 | public static final String CAMERA_TAG = "Camera";
8 |
9 | private Frustum frustum;
10 | private FrameBuffer renderTarget;
11 | private int lightDistance;
12 | private boolean isMain;
13 |
14 | public Camera(String name, Transform transform) {
15 | super(name, transform);
16 | super.tag = CAMERA_TAG;
17 | super.rigidBody.setKinematic(true);
18 | this.renderTarget = null;
19 | this.frustum = new Frustum(0, FP_ONE, 0, FP_ONE, FP_ONE, FP_ONE * 1000);
20 | this.lightDistance = FP_ONE * 100;
21 | this.isMain = false;
22 | }
23 |
24 | public Camera(String name, Transform transform, Frustum frustum) {
25 | this(name, transform);
26 | this.frustum = frustum;
27 | }
28 |
29 | public Frustum getFrustum() {
30 | return frustum;
31 | }
32 |
33 | public FrameBuffer getRenderTarget() {
34 | return renderTarget;
35 | }
36 |
37 | public void setRenderTarget(FrameBuffer renderTarget) {
38 | if(this.renderTarget != renderTarget) {
39 | this.renderTarget = renderTarget;
40 | frustum.setRenderTargetSize(renderTarget.getWidth(), renderTarget.getHeight());
41 | }
42 | }
43 |
44 | /**
45 | * Returns the max light to camera distance a light can have before the
46 | * light gets culled and doesn't affect the rendering of this camera anymore.
47 | *
48 | * @return The max light to camera distance. Default is 100.
49 | */
50 | public int getMaxLightDistance() {
51 | return lightDistance;
52 | }
53 |
54 | /**
55 | * Sets the max light to camera distance a light can have before the
56 | * light gets culled and doesn't affect the rendering of this camera anymore.
57 | *
58 | * @param lightDistance fixed point value. Default is 100.
59 | */
60 | public void setMaxLightDistance(int lightDistance) {
61 | this.lightDistance = lightDistance;
62 | }
63 |
64 | public boolean isMain() {
65 | return isMain;
66 | }
67 |
68 | public void setMain(boolean isMain) {
69 | this.isMain = isMain;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Face.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class Face {
4 |
5 | private final int index;
6 | private final Vertex[] vertices;
7 | private final int[] localNormal;
8 | private final int[] worldNormal;
9 | private final int[][] uvs;
10 | private final Material material;
11 | private int lightColor;
12 |
13 | public Face(int index, Vertex[] vertices, int[] normal, int[][] uvs, Material material) {
14 | this.index = index;
15 | this.vertices = vertices;
16 | this.localNormal = normal;
17 | this.worldNormal = normal.clone();
18 | this.uvs = uvs;
19 | this.material = material;
20 | }
21 |
22 | public int getIndex() {
23 | return index;
24 | }
25 |
26 | public Vertex getVertex(int index) {
27 | return vertices[index];
28 | }
29 |
30 | public Vertex[] getVertices() {
31 | return vertices;
32 | }
33 |
34 | public int[] getLocalNormal() {
35 | return localNormal;
36 | }
37 |
38 | public int[] getWorldNormal() {
39 | return worldNormal;
40 | }
41 |
42 | public int[] getUV(int index) {
43 | return uvs[index];
44 | }
45 |
46 | public int[][] getUVs() {
47 | return uvs;
48 | }
49 |
50 | public Material getMaterial() {
51 | return material;
52 | }
53 |
54 | /**
55 | * Returns the light color of this {@link Face}.
56 | * The light color is the color of all lights that reach and affect the illumination of
57 | * this Face put together.
58 | *
59 | * @return The light color of this Face.
60 | */
61 | public int getLightColor() {
62 | return lightColor;
63 | }
64 |
65 | /**
66 | * Sets the light color of this {@link Face}.
67 | * The light color is the color of all lights that reach and affect the illumination of
68 | * this Vertex put together.
69 | *
70 | * @param lightColor to set.
71 | */
72 | public void setLightColor(int lightColor) {
73 | this.lightColor = lightColor;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/FrameBuffer.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import java.awt.image.BufferedImage;
4 | import java.awt.image.DataBufferInt;
5 |
6 | import com.johnsproject.jgameengine.util.ColorUtils;
7 |
8 | public class FrameBuffer {
9 |
10 | private final int[] size;
11 | private final BufferedImage image;
12 | private final Texture colorBuffer;
13 | private final Texture depthBuffer;
14 | private final Texture stencilBuffer;
15 |
16 | public FrameBuffer(BufferedImage image) {
17 | this.size = new int[] {image.getWidth(), image.getHeight(), 0, 0};
18 | this.size[2] = size[0] * size[1];
19 | this.image = image;
20 | int[] pixelBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
21 | this.colorBuffer = new Texture(size[0], size[1], pixelBuffer);
22 | this.depthBuffer = new Texture(size[0], size[1]);
23 | this.stencilBuffer = new Texture(size[0], size[1]);
24 | }
25 |
26 | public FrameBuffer(int width, int height) {
27 | this.size = new int[] {width, height, 0, 0};
28 | this.size[2] = width * height;
29 | this.image = new BufferedImage(width, height, ColorUtils.COLOR_TYPE);
30 | int[] pixelBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
31 | this.colorBuffer = new Texture(width, height, pixelBuffer);
32 | this.depthBuffer = new Texture(width, height);
33 | this.stencilBuffer = new Texture(width, height);
34 | }
35 |
36 | public BufferedImage getImage() {
37 | return image;
38 | }
39 |
40 | public Texture getColorBuffer() {
41 | return colorBuffer;
42 | }
43 |
44 | public Texture getDepthBuffer() {
45 | return depthBuffer;
46 | }
47 |
48 | public Texture getStencilBuffer() {
49 | return stencilBuffer;
50 | }
51 |
52 | public int getWidth() {
53 | return size[0];
54 | }
55 |
56 | public int getHeight() {
57 | return size[1];
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Frustum.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_ONE;
4 |
5 | import com.johnsproject.jgameengine.util.FixedPointUtils;
6 | import com.johnsproject.jgameengine.util.MatrixUtils;
7 | import com.johnsproject.jgameengine.util.TransformationUtils;
8 |
9 | public class Frustum {
10 |
11 | private int left;
12 | private int right;
13 | private int top;
14 | private int bottom;
15 | private int near;
16 | private int far;
17 |
18 | private int renderTargetLeft;
19 | private int renderTargetRight;
20 | private int renderTargetTop;
21 | private int renderTargetBottom;
22 |
23 | private int renderTargetWidth;
24 | private int renderTargetHeight;
25 |
26 | private int focalLength;
27 | private int[][] projectionMatrix;
28 |
29 | private FrustumType type;
30 |
31 | public Frustum() {
32 | this.focalLength = FP_ONE;
33 | this.projectionMatrix = MatrixUtils.indentityMatrix();
34 | this.type = FrustumType.PERSPECTIVE;
35 | }
36 |
37 | public Frustum(int left, int right, int top, int bottom, int near, int far) {
38 | this();
39 | this.left = left;
40 | this.right = right;
41 | this.top = top;
42 | this.bottom = bottom;
43 | this.near = near;
44 | this.far = far;
45 | recalculateFrustum();
46 | }
47 |
48 | private void recalculateFrustum() {
49 | renderTargetLeft = FixedPointUtils.multiply(renderTargetWidth, left);
50 | renderTargetRight = FixedPointUtils.multiply(renderTargetWidth, right);
51 | renderTargetTop = FixedPointUtils.multiply(renderTargetHeight, top);
52 | renderTargetBottom = FixedPointUtils.multiply(renderTargetHeight, bottom);
53 | recalculateProjectionMatrix();
54 | }
55 |
56 | private void recalculateProjectionMatrix() {
57 | switch (type) {
58 | case ORTHOGRAPHIC:
59 | TransformationUtils.orthographicMatrix(projectionMatrix, this);
60 | break;
61 |
62 | case PERSPECTIVE:
63 | TransformationUtils.perspectiveMatrix(projectionMatrix, this);
64 | break;
65 | }
66 | }
67 |
68 | public void setFrustum(int left, int right, int top, int bottom, int near, int far) {
69 | this.left = left;
70 | this.right = right;
71 | this.top = top;
72 | this.bottom = bottom;
73 | this.near = near;
74 | this.far = far;
75 | recalculateFrustum();
76 | }
77 |
78 | public void setRenderTargetFrustum(int left, int right, int top, int bottom) {
79 | this.renderTargetLeft = left;
80 | this.renderTargetRight = right;
81 | this.renderTargetTop = top;
82 | this.renderTargetBottom = bottom;
83 | }
84 |
85 | public void setRenderTargetSize(int width, int height) {
86 | this.renderTargetWidth = width;
87 | this.renderTargetHeight = height;
88 | recalculateFrustum();
89 | }
90 |
91 | public int getFocalLength() {
92 | return focalLength;
93 | }
94 |
95 | public void setFocalLength(int focalLength) {
96 | if(this.focalLength != focalLength) {
97 | this.focalLength = focalLength;
98 | recalculateProjectionMatrix();
99 | }
100 | }
101 |
102 | public void setType(FrustumType type) {
103 | if(!this.type.equals(type)) {
104 | this.type = type;
105 | recalculateProjectionMatrix();
106 | }
107 | }
108 |
109 | public int getLeft() {
110 | return left;
111 | }
112 |
113 | public int getRight() {
114 | return right;
115 | }
116 |
117 | public int getTop() {
118 | return top;
119 | }
120 |
121 | public int getBottom() {
122 | return bottom;
123 | }
124 |
125 | public int getNear() {
126 | return near;
127 | }
128 |
129 | public int getFar() {
130 | return far;
131 | }
132 |
133 | public int getRenderTargetLeft() {
134 | return renderTargetLeft;
135 | }
136 |
137 | public int getRenderTargetRight() {
138 | return renderTargetRight;
139 | }
140 |
141 | public int getRenderTargetTop() {
142 | return renderTargetTop;
143 | }
144 |
145 | public int getRenderTargetBottom() {
146 | return renderTargetBottom;
147 | }
148 |
149 | public int getRenderTargetWidth() {
150 | return renderTargetWidth;
151 | }
152 |
153 | public int getRenderTargetHeight() {
154 | return renderTargetHeight;
155 | }
156 |
157 | public int[][] getProjectionMatrix() {
158 | return projectionMatrix;
159 | }
160 |
161 | public FrustumType getType() {
162 | return type;
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/FrustumType.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public enum FrustumType {
4 |
5 | ORTHOGRAPHIC,
6 | PERSPECTIVE
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Light.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import com.johnsproject.jgameengine.util.ColorUtils;
4 | import com.johnsproject.jgameengine.util.FixedPointUtils;
5 | import com.johnsproject.jgameengine.util.TransformationUtils;
6 | import com.johnsproject.jgameengine.util.VectorUtils;
7 |
8 | public class Light extends SceneObject {
9 |
10 | public static final String LIGHT_TAG = "Light";
11 |
12 | private static final int DIRECTIONAL_BIAS = FixedPointUtils.toFixedPoint(0.05f);
13 | private static final int SPOT_BIAS = FixedPointUtils.toFixedPoint(2f);
14 |
15 | private LightType type;
16 | private int intensity;
17 | private int color;
18 | private int ambientColor;
19 | private final int[] directionRotation;
20 | private final int[] direction;
21 | private int spotSize;
22 | private int spotSizeCos;
23 | private int innerSpotSize;
24 | private int innerSpotSizeCos;
25 | private int spotSoftness;
26 | private int constantAttenuation;
27 | private int linearAttenuation;
28 | private int quadraticAttenuation;
29 | private int shadowBias;
30 | private boolean hasShadow;
31 | private boolean isMain;
32 |
33 | public Light(String name, Transform transform) {
34 | super(name, transform);
35 | super.tag = LIGHT_TAG;
36 | super.rigidBody.setKinematic(true);
37 | this.type = LightType.DIRECTIONAL;
38 | this.intensity = FixedPointUtils.FP_ONE;
39 | this.color = ColorUtils.WHITE;
40 | this.ambientColor = ColorUtils.toColor(30, 30, 30);
41 | this.directionRotation = VectorUtils.emptyVector();
42 | this.direction = VectorUtils.VECTOR_FORWARD.clone();
43 | this.constantAttenuation = FixedPointUtils.toFixedPoint(1);
44 | this.linearAttenuation = FixedPointUtils.toFixedPoint(0.09);
45 | this.quadraticAttenuation = FixedPointUtils.toFixedPoint(0.032);
46 | this.shadowBias = DIRECTIONAL_BIAS;
47 | this.hasShadow = true;
48 | this.isMain = false;
49 | setSpotSize(FixedPointUtils.toFixedPoint(45));
50 | setInnerSpotSize(FixedPointUtils.toFixedPoint(35));
51 | }
52 |
53 | public LightType getType() {
54 | return type;
55 | }
56 |
57 | public void setType(LightType type) {
58 | this.type = type;
59 | if(hasShadowBiasDefaultValue()) {
60 | switch (type) {
61 | case DIRECTIONAL:
62 | shadowBias = DIRECTIONAL_BIAS;
63 | break;
64 | case SPOT:
65 | shadowBias = SPOT_BIAS;
66 | break;
67 | default:
68 | break;
69 | }
70 | }
71 | }
72 |
73 | private boolean hasShadowBiasDefaultValue() {
74 | return (shadowBias == DIRECTIONAL_BIAS)
75 | || (shadowBias == SPOT_BIAS);
76 | }
77 |
78 | public int getIntensity() {
79 | return intensity;
80 | }
81 |
82 | public void setIntensity(int strength) {
83 | this.intensity = strength;
84 | }
85 |
86 | public int getColor() {
87 | return color;
88 | }
89 |
90 | public void setColor(int color) {
91 | this.color = color;
92 | }
93 |
94 | public int getAmbientColor() {
95 | return ambientColor;
96 | }
97 |
98 | public void setAmbientColor(int ambientColor) {
99 | this.ambientColor = ambientColor;
100 | }
101 |
102 | /**
103 | * Returns the direction of this {@link Light}.
104 | * The direction is calculated based on the light's rotation.
105 | *
106 | * @return The direction of this Light.
107 | */
108 | public int[] getDirection() {
109 | if(!VectorUtils.equals(directionRotation, transform.getRotation())) {
110 | synchronized (directionRotation) {
111 | VectorUtils.copy(directionRotation, transform.getRotation());
112 | VectorUtils.copy(direction, VectorUtils.VECTOR_FORWARD);
113 | TransformationUtils.rotateX(direction, directionRotation[VectorUtils.VECTOR_X]);
114 | TransformationUtils.rotateY(direction, directionRotation[VectorUtils.VECTOR_Y]);
115 | TransformationUtils.rotateZ(direction, directionRotation[VectorUtils.VECTOR_Z]);
116 | }
117 | }
118 | return direction;
119 | }
120 |
121 | /**
122 | * Returns the cosine of the spot size of this {@link Light}.
123 | * This is needed for lighting calculations.
124 | *
125 | * @return The cosine of the spot size.
126 | */
127 | public int getSpotSizeCosine() {
128 | return spotSizeCos;
129 | }
130 |
131 | public int getSpotSize() {
132 | return spotSize;
133 | }
134 |
135 | public void setSpotSize(int degrees) {
136 | this.spotSize = degrees;
137 | // divide by 2 so the size is the size of the whole spot
138 | this.spotSizeCos = FixedPointUtils.cos(degrees >> 1);
139 | calculateSpotSoftness();
140 | }
141 |
142 | /**
143 | * Returns the cosine of the inner spot size of this {@link Light}.
144 | * This is needed for lighting calculations.
145 | *
146 | * @return The cosine of the inner spot size.
147 | */
148 | public int getInnerSpotSizeCosine() {
149 | return innerSpotSizeCos;
150 | }
151 |
152 | public int getInnerSpotSize() {
153 | return innerSpotSize;
154 | }
155 |
156 | public void setInnerSpotSize(int degrees) {
157 | this.innerSpotSize = degrees;
158 | this.innerSpotSizeCos = FixedPointUtils.cos(degrees >> 1);
159 | calculateSpotSoftness();
160 | }
161 |
162 | /**
163 | * Returns the difference between {@link #getInnerSpotSizeCosine()} and {@link #getSpotSizeCosine()}.
164 | * This is needed for lighting calculations.
165 | *
166 | * @return The difference between innerSpotSize and spotSize.
167 | */
168 | public int getSpotSoftness() {
169 | return spotSoftness;
170 | }
171 |
172 | private void calculateSpotSoftness() {
173 | // + 1 because it can't be 0
174 | spotSoftness = (innerSpotSizeCos - spotSizeCos) + 1;
175 | }
176 |
177 | public int getConstantAttenuation() {
178 | return constantAttenuation;
179 | }
180 |
181 | public void setConstantAttenuation(int constantAttenuation) {
182 | this.constantAttenuation = constantAttenuation;
183 | }
184 |
185 | public int getLinearAttenuation() {
186 | return linearAttenuation;
187 | }
188 |
189 | public void setLinearAttenuation(int linearAttenuation) {
190 | this.linearAttenuation = linearAttenuation;
191 | }
192 |
193 | public int getQuadraticAttenuation() {
194 | return quadraticAttenuation;
195 | }
196 |
197 | public void setQuadraticAttenuation(int quadraticAttenuation) {
198 | this.quadraticAttenuation = quadraticAttenuation;
199 | }
200 |
201 | public int getShadowBias() {
202 | return shadowBias;
203 | }
204 |
205 | public void setShadowBias(int shadowBias) {
206 | this.shadowBias = shadowBias;
207 | }
208 |
209 | public boolean hasShadow() {
210 | return hasShadow;
211 | }
212 |
213 | public void setShadow(boolean hasShadow) {
214 | this.hasShadow = hasShadow;
215 | }
216 |
217 | public boolean isMain() {
218 | return isMain;
219 | }
220 |
221 | public void setMain(boolean isMain) {
222 | this.isMain = isMain;
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/LightType.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public enum LightType {
4 |
5 | DIRECTIONAL,
6 | POINT,
7 | SPOT
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Material.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import com.johnsproject.jgameengine.shading.Shader;
4 |
5 | public class Material {
6 |
7 | private final int index;
8 | private final String name;
9 | private Shader shader;
10 | private int shininess;
11 | private int diffuseColor;
12 | private int specularColor;
13 | private Texture texture;
14 |
15 | public Material(int index, String name) {
16 | this.index = index;
17 | this.name = name;
18 | this.shader = null;
19 | this.shininess = 0;
20 | this.texture = null;
21 | }
22 |
23 | public int getIndex() {
24 | return index;
25 | }
26 |
27 | public String getName() {
28 | return name;
29 | }
30 |
31 | public Shader getShader() {
32 | return shader;
33 | }
34 |
35 | public void setShader(Shader shader) {
36 | this.shader = shader;
37 | }
38 |
39 | public int getShininess() {
40 | return shininess;
41 | }
42 |
43 | public void setShininess(int shininess) {
44 | this.shininess = shininess;
45 | }
46 |
47 | public int getDiffuseColor() {
48 | return diffuseColor;
49 | }
50 |
51 | public void setDiffuseColor(int diffuseColor) {
52 | this.diffuseColor = diffuseColor;
53 | }
54 |
55 | public int getSpecularColor() {
56 | return specularColor;
57 | }
58 |
59 | public void setSpecularColor(int specularColor) {
60 | this.specularColor = specularColor;
61 | }
62 |
63 | public Texture getTexture() {
64 | return texture;
65 | }
66 |
67 | public void setTexture(Texture texture) {
68 | this.texture = texture;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Mesh.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class Mesh {
4 |
5 | private final Vertex[] vertices;
6 | private final Face[] faces;
7 | private final Material[] materials;
8 |
9 | public Mesh(Vertex[] vertices, Face[] faces, Material[] materials) {
10 | this.vertices = vertices;
11 | this.faces = faces;
12 | this.materials = materials;
13 | }
14 |
15 | public Vertex[] getVertices(){
16 | return vertices;
17 | }
18 |
19 | public Vertex getVertex(int index){
20 | return vertices[index];
21 | }
22 |
23 | public Face[] getFaces() {
24 | return faces;
25 | }
26 |
27 | public Face getFace(int index) {
28 | return faces[index];
29 | }
30 |
31 | public Material[] getMaterials() {
32 | return materials;
33 | }
34 |
35 | public Material getMaterial(int index) {
36 | return materials[index];
37 | }
38 |
39 | public Material getMaterial(String name) {
40 | for (int i = 0; i < materials.length; i++) {
41 | Material material = materials[i];
42 | if (material.getName().equals(name)) {
43 | return material;
44 | }
45 | }
46 | return materials[0];
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Model.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class Model extends SceneObject {
4 |
5 | public static final String MODEL_TAG = "Model";
6 |
7 | private final Mesh mesh;
8 | private final Armature armature;
9 |
10 | public Model (String name, Transform transform, Mesh mesh) {
11 | super(name, transform);
12 | super.tag = MODEL_TAG;
13 | this.mesh = mesh;
14 | this.armature = null;
15 | }
16 |
17 | public Model (String name, Transform transform, Mesh mesh, Armature armature) {
18 | super(name, transform);
19 | super.tag = MODEL_TAG;
20 | this.mesh = mesh;
21 | this.armature = armature;
22 | }
23 |
24 | public Mesh getMesh() {
25 | return mesh;
26 | }
27 |
28 | public Armature getArmature() {
29 | return armature;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/RigidBody.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import static com.johnsproject.jgameengine.util.VectorUtils.*;
4 |
5 | import com.johnsproject.jgameengine.util.FixedPointUtils;
6 | import com.johnsproject.jgameengine.util.MatrixUtils;
7 | import com.johnsproject.jgameengine.util.VectorUtils;
8 |
9 | public class RigidBody {
10 |
11 | private boolean kinematic;
12 | private int mass;
13 | private final int[] force;
14 | private final int[] torque;
15 | private final int[] linearVelocity;
16 | private final int[] angularVelocity;
17 | private final int[][] momentOfInertia;
18 |
19 | public RigidBody() {
20 | this.kinematic = false;
21 | this.mass = FixedPointUtils.FP_ONE;
22 | this.force = VectorUtils.emptyVector();
23 | this.torque = VectorUtils.emptyVector();
24 | this.linearVelocity = VectorUtils.emptyVector();
25 | this.angularVelocity = VectorUtils.emptyVector();
26 | this.momentOfInertia = MatrixUtils.indentityMatrix();
27 | }
28 |
29 | public boolean isKinematic() {
30 | return kinematic;
31 | }
32 |
33 | public void setKinematic(boolean kinematic) {
34 | this.kinematic = kinematic;
35 | }
36 |
37 | public int getMass() {
38 | return mass;
39 | }
40 |
41 | public void setMass(int mass) {
42 | this.mass = mass;
43 | }
44 |
45 | public int[] getForce() {
46 | return force;
47 | }
48 |
49 | public int[] getTorque() {
50 | return torque;
51 | }
52 |
53 | public int[] getLinearVelocity() {
54 | return linearVelocity;
55 | }
56 |
57 | public int[] getAngularVelocity() {
58 | return angularVelocity;
59 | }
60 |
61 | public int[][] getMomentOfInertia() {
62 | return momentOfInertia;
63 | }
64 |
65 | public void setForce(int x, int y, int z) {
66 | force[VECTOR_X] = x;
67 | force[VECTOR_Y] = y;
68 | force[VECTOR_Z] = z;
69 | }
70 |
71 | public void addForce(int x, int y, int z) {
72 | force[VECTOR_X] += x;
73 | force[VECTOR_Y] += y;
74 | force[VECTOR_Z] += z;
75 | }
76 |
77 | public void addForce(int[] vector) {
78 | force[VECTOR_X] += vector[VECTOR_X];
79 | force[VECTOR_Y] += vector[VECTOR_Y];
80 | force[VECTOR_Z] += vector[VECTOR_Z];
81 | }
82 |
83 | public void setTorque(int x, int y, int z) {
84 | torque[VECTOR_X] = x;
85 | torque[VECTOR_Y] = y;
86 | torque[VECTOR_Z] = z;
87 | }
88 |
89 | public void addTorque(int x, int y, int z) {
90 | torque[VECTOR_X] += x;
91 | torque[VECTOR_Y] += y;
92 | torque[VECTOR_Z] += z;
93 | }
94 |
95 | public void addTorque(int[] vector) {
96 | torque[VECTOR_X] += vector[VECTOR_X];
97 | torque[VECTOR_Y] += vector[VECTOR_Y];
98 | torque[VECTOR_Z] += vector[VECTOR_Z];
99 | }
100 |
101 | public void setLinearVelocity(int x, int y, int z) {
102 | linearVelocity[VECTOR_X] = x;
103 | linearVelocity[VECTOR_Y] = y;
104 | linearVelocity[VECTOR_Z] = z;
105 | }
106 |
107 | public void addLinearVelocity(int x, int y, int z) {
108 | linearVelocity[VECTOR_X] += x;
109 | linearVelocity[VECTOR_Y] += y;
110 | linearVelocity[VECTOR_Z] += z;
111 | }
112 |
113 | public void addLinearVelocity(int[] vector) {
114 | linearVelocity[VECTOR_X] += vector[VECTOR_X];
115 | linearVelocity[VECTOR_Y] += vector[VECTOR_Y];
116 | linearVelocity[VECTOR_Z] += vector[VECTOR_Z];
117 | }
118 |
119 | // needs to be converted to radians
120 | public void setAngularVelocity(int x, int y, int z) {
121 | angularVelocity[VECTOR_X] = x;
122 | angularVelocity[VECTOR_Y] = y;
123 | angularVelocity[VECTOR_Z] = z;
124 | }
125 |
126 | public void addAngularVelocity(int x, int y, int z) {
127 | angularVelocity[VECTOR_X] += x;
128 | angularVelocity[VECTOR_Y] += y;
129 | angularVelocity[VECTOR_Z] += z;
130 | }
131 |
132 | public void addAngularVelocity(int[] vector) {
133 | angularVelocity[VECTOR_X] += vector[VECTOR_X];
134 | angularVelocity[VECTOR_Y] += vector[VECTOR_Y];
135 | angularVelocity[VECTOR_Z] += vector[VECTOR_Z];
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Scene.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import java.util.ArrayList;
4 |
5 | public class Scene {
6 |
7 | private Camera mainCamera;
8 | private Light mainLight;
9 | private final ArrayList sceneObjects;
10 | private final ArrayList models;
11 | private final ArrayList cameras;
12 | private final ArrayList lights;
13 |
14 | public Scene() {
15 | this.sceneObjects = new ArrayList();
16 | this.models = new ArrayList();
17 | this.cameras = new ArrayList();
18 | this.lights = new ArrayList();
19 | }
20 |
21 | public ArrayList getSceneObjects() {
22 | return sceneObjects;
23 | }
24 |
25 | public SceneObject getSceneObject(String name) {
26 | for (int i = 0; i < sceneObjects.size(); i++) {
27 | SceneObject sceneObject = sceneObjects.get(i);
28 | if(sceneObject.getName().equals(name)) {
29 | return sceneObject;
30 | }
31 | }
32 | return null;
33 | }
34 |
35 | private void removeSceneObject(String name) {
36 | for (int i = 0; i < sceneObjects.size(); i++) {
37 | if(sceneObjects.get(i).getName().equals(name)) {
38 | sceneObjects.remove(i);
39 | }
40 | }
41 | }
42 |
43 | public void addModel(Model model){
44 | sceneObjects.add(model);
45 | models.add(model);
46 | }
47 |
48 | public void removeModel(String name){
49 | removeSceneObject(name);
50 | for (int i = 0; i < models.size(); i++) {
51 | if(models.get(i).getName().equals(name)) {
52 | models.remove(i);
53 | }
54 | }
55 | }
56 |
57 | public ArrayList getModels() {
58 | return models;
59 | }
60 |
61 | public Model getModel(String name) {
62 | return (Model)getSceneObject(name);
63 | }
64 |
65 | public void addLight(Light light){
66 | if(mainLight == null) {
67 | setMainDirectionalLight(light);
68 | }
69 | sceneObjects.add(light);
70 | lights.add(light);
71 | }
72 |
73 | public void removeLight(String name){
74 | removeSceneObject(name);
75 | for (int i = 0; i < lights.size(); i++) {
76 | if(lights.get(i).getName().equals(name)) {
77 | lights.remove(i);
78 | }
79 | }
80 | }
81 |
82 | public ArrayList getLights() {
83 | return lights;
84 | }
85 |
86 | public Light getLight(String name) {
87 | return (Light)getSceneObject(name);
88 | }
89 |
90 | public Light getMainDirectionalLight() {
91 | return mainLight;
92 | }
93 |
94 | public void setMainDirectionalLight(Light mainLight) {
95 | if(this.mainLight != null) {
96 | this.mainLight.setMain(false);
97 | }
98 | mainLight.setMain(true);
99 | this.mainLight = mainLight;
100 | }
101 |
102 | public void addCamera(Camera camera){
103 | if(mainCamera == null) {
104 | setMainCamera(camera);
105 | }
106 | sceneObjects.add(camera);
107 | cameras.add(camera);
108 | }
109 |
110 | public void removeCamera(String name){
111 | removeSceneObject(name);
112 | for (int i = 0; i < cameras.size(); i++) {
113 | if(cameras.get(i).getName().equals(name)) {
114 | cameras.remove(i);
115 | }
116 | }
117 | }
118 |
119 | public ArrayList getCameras() {
120 | return cameras;
121 | }
122 |
123 | public Camera getCamera(String name) {
124 | return (Camera)getSceneObject(name);
125 | }
126 |
127 | public Camera getMainCamera() {
128 | return mainCamera;
129 | }
130 |
131 | public void setMainCamera(Camera mainCamera) {
132 | if(this.mainCamera != null) {
133 | this.mainCamera.setMain(false);
134 | }
135 | mainCamera.setMain(true);
136 | this.mainCamera = mainCamera;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/SceneObject.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class SceneObject {
4 |
5 | protected String tag;
6 | protected boolean active;
7 | protected boolean culled;
8 | protected final String name;
9 | protected final Transform transform;
10 | protected final RigidBody rigidBody;
11 |
12 | public SceneObject(String name, Transform transform) {
13 | this.tag = "";
14 | this.name = name;
15 | this.transform = transform;
16 | this.active = true;
17 | this.culled = false;
18 | this.rigidBody = new RigidBody();
19 | }
20 |
21 | public Transform getTransform() {
22 | return this.transform;
23 | }
24 |
25 | public String getName() {
26 | return name;
27 | }
28 |
29 | public RigidBody getRigidBody() {
30 | return rigidBody;
31 | }
32 |
33 | public boolean isActive() {
34 | return active;
35 | }
36 |
37 | public void setActive(boolean active) {
38 | this.active = active;
39 | }
40 |
41 | public String getTag() {
42 | return tag;
43 | }
44 |
45 | public void setTag(String tag) {
46 | this.tag = tag;
47 | }
48 |
49 | public boolean isCulled() {
50 | return culled;
51 | }
52 |
53 | public void setCulled(boolean culled) {
54 | this.culled = culled;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Texture.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import java.awt.image.BufferedImage;
4 | import java.awt.image.DataBufferInt;
5 |
6 | public class Texture {
7 |
8 | private final int[] pixels;
9 | private final int[] size;
10 |
11 | public Texture (BufferedImage bufferedImage){
12 | int width = bufferedImage.getWidth();
13 | int height = bufferedImage.getHeight();
14 | this.size = new int[] {width, height, width * height, 0};
15 | this.pixels = ((DataBufferInt)bufferedImage.getRaster().getDataBuffer()).getData();
16 | }
17 |
18 | public Texture (int width, int height, int[] pixels){
19 | this.size = new int[] {width, height, width * height, 0};
20 | this.pixels = pixels;
21 | }
22 |
23 | public Texture (int width, int height){
24 | this.size = new int[] {width, height, width * height, 0};
25 | this.pixels = new int[size[2]];
26 | }
27 |
28 | public int[] getPixels() {
29 | return pixels;
30 | }
31 |
32 | public int getWidth() {
33 | return size[0];
34 | }
35 |
36 | public int getHeight() {
37 | return size[1];
38 | }
39 |
40 | public int getPixel(int x, int y) {
41 | x = x >= 0 ? x : 0;
42 | x = x < size[0] ? x : size[0] - 1;
43 | y = y >= 0 ? y : 0;
44 | y = y < size[1] ? y : size[1] - 1;
45 | return pixels[x + (y * size[0])];
46 | }
47 |
48 | public void setPixel(int x, int y, int value) {
49 | x = x >= 0 ? x : 0;
50 | x = x < size[0] ? x : size[0] - 1;
51 | y = y >= 0 ? y : 0;
52 | y = y < size[1] ? y : size[1] - 1;
53 | pixels[x + (y * size[0])] = value;
54 | }
55 |
56 | public void fill(int value) {
57 | int[] pixelBuffer = getPixels();
58 | for (int i = 0; i < pixelBuffer.length; i++) {
59 | pixelBuffer[i] = value;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Transform.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 |
4 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_ONE;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_X;
6 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Y;
7 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Z;
8 |
9 | import com.johnsproject.jgameengine.util.FixedPointUtils;
10 | import com.johnsproject.jgameengine.util.MatrixUtils;
11 | import com.johnsproject.jgameengine.util.TransformationUtils;
12 | import com.johnsproject.jgameengine.util.VectorUtils;
13 |
14 | public class Transform {
15 |
16 | private final int[] location;
17 | private final int[] rotation;
18 | private final int[] scale;
19 |
20 | private final int[][] matrixCache1;
21 | private final int[][] matrixCache2;
22 | private final int[][] spaceEnterMatrix;
23 | private final int[][] spaceEnterNormalMatrix;
24 | private final int[][] spaceExitMatrix;
25 | private final int[][] spaceExitNormalMatrix;
26 |
27 | public Transform() {
28 | this(VectorUtils.emptyVector(), VectorUtils.emptyVector(), VectorUtils.VECTOR_ONE.clone());
29 | }
30 |
31 | public Transform(int[] location, int[] rotation, int[] scale) {
32 | this.location = location;
33 | this.rotation = rotation;
34 | this.scale = scale;
35 | this.matrixCache1 = MatrixUtils.indentityMatrix();
36 | this.matrixCache2 = MatrixUtils.indentityMatrix();
37 | this.spaceEnterMatrix = MatrixUtils.indentityMatrix();
38 | this.spaceEnterNormalMatrix = MatrixUtils.indentityMatrix();
39 | this.spaceExitMatrix = MatrixUtils.indentityMatrix();
40 | this.spaceExitNormalMatrix = MatrixUtils.indentityMatrix();
41 | recalculateMatrices();
42 | }
43 |
44 | private void recalculateMatrices() {
45 | spaceExitMatrix();
46 | spaceExitNormalMatrix();
47 | spaceEnterMatrix();
48 | spaceEnterNormalMatrix();
49 | }
50 |
51 | private void spaceExitMatrix() {
52 | MatrixUtils.copy(spaceExitMatrix, MatrixUtils.MATRIX_IDENTITY);
53 | scale(spaceExitMatrix);
54 | rotateX(spaceExitMatrix, rotation[VECTOR_X]);
55 | rotateY(spaceExitMatrix, rotation[VECTOR_Y]);
56 | rotateZ(spaceExitMatrix, rotation[VECTOR_Z]);
57 | translate(spaceExitMatrix);
58 | }
59 |
60 | private void spaceExitNormalMatrix() {
61 | MatrixUtils.copy(spaceExitNormalMatrix, MatrixUtils.MATRIX_IDENTITY);
62 | scale(spaceExitNormalMatrix);
63 | rotateX(spaceExitNormalMatrix, rotation[VECTOR_X]);
64 | rotateY(spaceExitNormalMatrix, rotation[VECTOR_Y]);
65 | rotateZ(spaceExitNormalMatrix, rotation[VECTOR_Z]);
66 | if ((scale[VECTOR_X] != scale[VECTOR_Y]) || (scale[VECTOR_Y] != scale[VECTOR_Z])) {
67 | MatrixUtils.inverse(spaceExitNormalMatrix, matrixCache2);
68 | MatrixUtils.transpose(matrixCache2, spaceExitNormalMatrix);
69 | }
70 | }
71 |
72 | private void spaceEnterMatrix() {
73 | int scaleX = FixedPointUtils.divide(FP_ONE, scale[VECTOR_X] == 0 ? 1 : scale[VECTOR_X]);
74 | int scaleY = FixedPointUtils.divide(FP_ONE, scale[VECTOR_Y] == 0 ? 1 : scale[VECTOR_Y]);
75 | int scaleZ = FixedPointUtils.divide(FP_ONE, scale[VECTOR_Z] == 0 ? 1 : scale[VECTOR_Z]);
76 | VectorUtils.invert(location);
77 | VectorUtils.invert(rotation);
78 | MatrixUtils.copy(spaceEnterMatrix, MatrixUtils.MATRIX_IDENTITY);
79 | translate(spaceEnterMatrix);
80 | rotateZ(spaceEnterMatrix, rotation[VECTOR_Z]);
81 | rotateY(spaceEnterMatrix, rotation[VECTOR_Y]);
82 | rotateX(spaceEnterMatrix, rotation[VECTOR_X]);
83 | scale(spaceEnterMatrix, scaleX, scaleY, scaleZ);
84 | VectorUtils.invert(location);
85 | VectorUtils.invert(rotation);
86 | }
87 |
88 | private void spaceEnterNormalMatrix() {
89 | int scaleX = FixedPointUtils.divide(FP_ONE, scale[VECTOR_X] == 0 ? 1 : scale[VECTOR_X]);
90 | int scaleY = FixedPointUtils.divide(FP_ONE, scale[VECTOR_Y] == 0 ? 1 : scale[VECTOR_Y]);
91 | int scaleZ = FixedPointUtils.divide(FP_ONE, scale[VECTOR_Z] == 0 ? 1 : scale[VECTOR_Z]);
92 | VectorUtils.invert(rotation);
93 | MatrixUtils.copy(spaceEnterNormalMatrix, MatrixUtils.MATRIX_IDENTITY);
94 | rotateZ(spaceEnterNormalMatrix, rotation[VECTOR_Z]);
95 | rotateY(spaceEnterNormalMatrix, rotation[VECTOR_Y]);
96 | rotateX(spaceEnterNormalMatrix, rotation[VECTOR_X]);
97 | scale(spaceEnterNormalMatrix, scaleX, scaleY, scaleZ);
98 | VectorUtils.invert(rotation);
99 | if ((scale[VECTOR_X] != scale[VECTOR_Y]) || (scale[VECTOR_Y] != scale[VECTOR_Z])) {
100 | MatrixUtils.inverse(spaceEnterNormalMatrix, matrixCache2);
101 | MatrixUtils.transpose(matrixCache2, spaceEnterNormalMatrix);
102 | }
103 | }
104 |
105 | private void translate(int[][] matrix) {
106 | TransformationUtils.translationMatrix(matrixCache1, location);
107 | MatrixUtils.copy(matrixCache2, matrix);
108 | MatrixUtils.multiply(matrixCache1, matrixCache2, matrix);
109 | }
110 |
111 | private void scale(int[][] matrix) {
112 | scale(matrix, scale[VECTOR_X], scale[VECTOR_Y], scale[VECTOR_Z]);
113 | }
114 |
115 | private void scale(int[][] matrix, int x, int y, int z) {
116 | TransformationUtils.scaleMatrix(matrixCache1, x, y, z);
117 | MatrixUtils.copy(matrixCache2, matrix);
118 | MatrixUtils.multiply(matrixCache1, matrixCache2, matrix);
119 | }
120 |
121 | private void rotateX(int[][] matrix, int angle) {
122 | TransformationUtils.xRotationMatrix(matrixCache1, angle);
123 | MatrixUtils.copy(matrixCache2, matrix);
124 | MatrixUtils.multiply(matrixCache1, matrixCache2, matrix);
125 | }
126 |
127 | private void rotateY(int[][] matrix, int angle) {
128 | TransformationUtils.yRotationMatrix(matrixCache1, angle);
129 | MatrixUtils.copy(matrixCache2, matrix);
130 | MatrixUtils.multiply(matrixCache1, matrixCache2, matrix);
131 | }
132 |
133 | private void rotateZ(int[][] matrix, int angle) {
134 | TransformationUtils.zRotationMatrix(matrixCache1, angle);
135 | MatrixUtils.copy(matrixCache2, matrix);
136 | MatrixUtils.multiply(matrixCache1, matrixCache2, matrix);
137 | }
138 |
139 | public void setLocation(int x, int y, int z) {
140 | location[VECTOR_X] = x;
141 | location[VECTOR_Y] = y;
142 | location[VECTOR_Z] = z;
143 | recalculateMatrices();
144 | }
145 |
146 | public void setRotation(int x, int y, int z) {
147 | rotation[VECTOR_X] = x;
148 | rotation[VECTOR_Y] = y;
149 | rotation[VECTOR_Z] = z;
150 | recalculateMatrices();
151 | }
152 |
153 | public void setScale(int x, int y, int z) {
154 | scale[VECTOR_X] = x;
155 | scale[VECTOR_Y] = y;
156 | scale[VECTOR_Z] = z;
157 | recalculateMatrices();
158 | }
159 |
160 | public void worldTranslate(int x, int y, int z) {
161 | setLocation(location[VECTOR_X] + x, location[VECTOR_Y] + y, location[VECTOR_Z] + z);
162 | }
163 |
164 | public void worldTranslate(int[] vector) {
165 | worldTranslate(vector[VECTOR_X], vector[VECTOR_Y], vector[VECTOR_Z]);
166 | }
167 |
168 | public void localTranslate(int x, int y, int z) {
169 | worldTranslate(localToWorld(x, y, z));
170 | }
171 |
172 | public void localTranslate(int[] vector) {
173 | localTranslate(vector[VECTOR_X], vector[VECTOR_Y], vector[VECTOR_Z]);
174 | }
175 |
176 | public void worldRotate(int x, int y, int z) {
177 | setRotation(rotation[VECTOR_X] + x, rotation[VECTOR_Y] + y, rotation[VECTOR_Z] + z);
178 | }
179 |
180 | public void worldRotate(int[] angles) {
181 | worldRotate(angles[VECTOR_X], angles[VECTOR_Y], angles[VECTOR_Z]);
182 | }
183 |
184 | public void localRotate(int x, int y, int z) {
185 | worldRotate(localToWorld(x, y, z));
186 | }
187 |
188 | public void localRotate(int[] angles) {
189 | localRotate(angles[VECTOR_X], angles[VECTOR_Y], angles[VECTOR_Z]);
190 | }
191 |
192 | public void worldScale(int x, int y, int z) {
193 | setScale(scale[VECTOR_X] + x, scale[VECTOR_Y] + y, scale[VECTOR_Z] + z);
194 | }
195 |
196 | public void worldScale(int[] vector) {
197 | worldScale(vector[VECTOR_X], vector[VECTOR_Y], vector[VECTOR_Z]);
198 | }
199 |
200 | public void localScale(int x, int y, int z) {
201 | worldScale(localToWorld(x, y, z));
202 | }
203 |
204 | public void localScale(int[] vector) {
205 | localScale(vector[VECTOR_X], vector[VECTOR_Y], vector[VECTOR_Z]);
206 | }
207 |
208 | private int[] localToWorld(int x, int y, int z) {
209 | final int[] vector = matrixCache1[0];
210 | VectorUtils.copy(vector, VectorUtils.VECTOR_ZERO);
211 | vector[VECTOR_X] = x;
212 | vector[VECTOR_Y] = y;
213 | vector[VECTOR_Z] = z;
214 | TransformationUtils.rotateX(vector, rotation[VectorUtils.VECTOR_X]);
215 | TransformationUtils.rotateY(vector, rotation[VectorUtils.VECTOR_Y]);
216 | TransformationUtils.rotateZ(vector, rotation[VectorUtils.VECTOR_Z]);
217 | return vector;
218 | }
219 |
220 | public int[] getLocation() {
221 | return location;
222 | }
223 |
224 | public int[] getRotation() {
225 | return rotation;
226 | }
227 |
228 | public int[] getScale() {
229 | return scale;
230 | }
231 |
232 | public int[][] getSpaceEnterMatrix() {
233 | return spaceEnterMatrix;
234 | }
235 |
236 | public int[][] getSpaceEnterNormalMatrix() {
237 | return spaceEnterNormalMatrix;
238 | }
239 |
240 | public int[][] getSpaceExitMatrix() {
241 | return spaceExitMatrix;
242 | }
243 |
244 | public int[][] getSpaceExitNormalMatrix() {
245 | return spaceExitNormalMatrix;
246 | }
247 | }
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/Vertex.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | import com.johnsproject.jgameengine.util.VectorUtils;
4 |
5 | public class Vertex {
6 |
7 | private final int index;
8 | private final int[] localLocation;
9 | private final int[] worldLocation;
10 | private final int[] worldNormal;
11 | private final int[] location;
12 | private final Material material;
13 | private int lightColor;
14 |
15 | public Vertex(int index, int[] location, Material material) {
16 | this.index = index;
17 | this.localLocation = location;
18 | this.worldLocation = VectorUtils.emptyVector();
19 | this.worldNormal = VectorUtils.emptyVector();
20 | this.location = VectorUtils.emptyVector();
21 | this.material = material;
22 | }
23 |
24 | public int getIndex() {
25 | return index;
26 | }
27 |
28 | public int[] getLocalLocation() {
29 | return localLocation;
30 | }
31 |
32 | public int[] getWorldLocation() {
33 | return worldLocation;
34 | }
35 |
36 | public int[] getWorldNormal() {
37 | return worldNormal;
38 | }
39 |
40 | public int[] getLocation() {
41 | return location;
42 | }
43 |
44 | public Material getMaterial() {
45 | return material;
46 | }
47 |
48 | /**
49 | * Returns the light color of this {@link Vertex}.
50 | * The light color is the color of all lights that reach and affect the illumination of
51 | * this Vertex put together.
52 | *
53 | * @return The light color of this Vertex.
54 | */
55 | public int getLightColor() {
56 | return lightColor;
57 | }
58 |
59 | /**
60 | * Sets the light color of this {@link Vertex}.
61 | * The light color is the color of all lights that reach and affect the illumination of
62 | * this Vertex put together.
63 | *
64 | * @param lightColor to set.
65 | */
66 | public void setLightColor(int lightColor) {
67 | this.lightColor = lightColor;
68 | }
69 | }
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/model/VertexGroup.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.model;
2 |
3 | public class VertexGroup {
4 |
5 | private final int boneIndex;
6 | private final Vertex[] vertices;
7 | private final int[] weights;
8 |
9 | public VertexGroup(int boneIndex, Vertex[] vertices, int[] weights) {
10 | this.boneIndex = boneIndex;
11 | this.vertices = vertices;
12 | this.weights = weights;
13 | }
14 |
15 | public int getBoneIndex() {
16 | return boneIndex;
17 | }
18 |
19 | public Vertex getVertex(int index) {
20 | return vertices[index];
21 | }
22 |
23 | public Vertex[] getVertices() {
24 | return vertices;
25 | }
26 |
27 | public int getWeight(Vertex vertex) {
28 | for (int i = 0; i < vertices.length; i++) {
29 | if(vertices[i].getIndex() == vertex.getIndex()) {
30 | return getWeight(i);
31 | }
32 | }
33 | return -1;
34 | }
35 |
36 | public int getWeight(int index) {
37 | return weights[index];
38 | }
39 |
40 | public int[] getWeights() {
41 | return weights;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/rasterization/Rasterizer.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.rasterization;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_BIT;
4 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_ONE;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_X;
6 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Y;
7 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Z;
8 |
9 | import com.johnsproject.jgameengine.model.Face;
10 | import com.johnsproject.jgameengine.model.Frustum;
11 | import com.johnsproject.jgameengine.shading.Shader;
12 | import com.johnsproject.jgameengine.util.FixedPointUtils;
13 | import com.johnsproject.jgameengine.util.VectorUtils;
14 |
15 | public class Rasterizer {
16 |
17 | protected final Shader shader;
18 | protected final int[] location;
19 | protected final int[] location0;
20 | protected final int[] location1;
21 | protected final int[] location2;
22 | protected final int[] location3;
23 |
24 | protected int sortY00, sortY01;
25 | protected int sortY10, sortY11;
26 | protected int sortY20, sortY21;
27 |
28 | protected int y2y1, y3y1, y3y2;
29 | protected int dx1, dx2;
30 | protected int dz1, dz2;
31 | protected int x1, x2;
32 | protected int y1, y2;
33 | protected int z;
34 | protected int dxdx;
35 | protected int dz;
36 |
37 | private int renderTargetLeft;
38 | private int renderTargetRight;
39 | private int renderTargetTop;
40 | private int renderTargetBottom;
41 | private boolean frustumCull;
42 | private int faceCull;
43 |
44 |
45 | public Rasterizer(Shader shader) {
46 | this.shader = shader;
47 | this.location = VectorUtils.emptyVector();
48 | this.location0 = VectorUtils.emptyVector();
49 | this.location1 = VectorUtils.emptyVector();
50 | this.location2 = VectorUtils.emptyVector();
51 | this.location3 = VectorUtils.emptyVector();
52 | this.frustumCull = true;
53 | this.faceCull = -1;
54 | }
55 |
56 | public void setFrustumCull(boolean frustumCull) {
57 | this.frustumCull = frustumCull;
58 | }
59 |
60 | public void setFaceCull(int faceCull) {
61 | this.faceCull = faceCull;
62 | }
63 |
64 | public void draw(Face face, Frustum frustum) {
65 | copyLocations(face);
66 | copyFrustum(frustum);
67 | if(isCulled())
68 | return;
69 | sortY();
70 | if (location1[VECTOR_Y] == location2[VECTOR_Y]) {
71 | drawBottomTriangle();
72 | } else if (location0[VECTOR_Y] == location1[VECTOR_Y]) {
73 | drawTopTriangle();
74 | } else {
75 | splitTriangle();
76 | swapSplitedBottomTriangle();
77 | drawBottomTriangle();
78 | swapSplitedTopTriangle();
79 | drawTopTriangle();
80 | }
81 | }
82 |
83 | protected void copyLocations(Face face) {
84 | VectorUtils.copy(location0, face.getVertex(0).getLocation());
85 | VectorUtils.copy(location1, face.getVertex(1).getLocation());
86 | VectorUtils.copy(location2, face.getVertex(2).getLocation());
87 | }
88 |
89 | protected void copyFrustum(Frustum frustum) {
90 | renderTargetLeft = frustum.getRenderTargetLeft();
91 | renderTargetRight = frustum.getRenderTargetRight();
92 | renderTargetTop = frustum.getRenderTargetTop();
93 | renderTargetBottom = frustum.getRenderTargetBottom();
94 | }
95 |
96 | protected boolean isCulled() {
97 | return isBiggerThanRenderTarget() || isOutOfFrustum() || isBackface();
98 | }
99 |
100 | private boolean isBiggerThanRenderTarget() {
101 | final int width0 = Math.abs(location0[VECTOR_X] - location1[VECTOR_X]);
102 | final int width1 = Math.abs(location2[VECTOR_X] - location1[VECTOR_X]);
103 | final int width2 = Math.abs(location2[VECTOR_X] - location0[VECTOR_X]);
104 | final int height0 = Math.abs(location0[VECTOR_Y] - location1[VECTOR_Y]);
105 | final int height1 = Math.abs(location2[VECTOR_Y] - location1[VECTOR_Y]);
106 | final int height2 = Math.abs(location2[VECTOR_Y] - location0[VECTOR_Y]);
107 | final int width = Math.max(width0, Math.max(width1, width2));
108 | final int height = Math.max(height0, Math.max(height1, height2));
109 | if((width > (renderTargetRight - renderTargetLeft)) || (height > (renderTargetBottom - renderTargetTop))) {
110 | return true;
111 | }
112 | return false;
113 | }
114 |
115 | private boolean isOutOfFrustum() {
116 | if(frustumCull) {
117 | final boolean insideWidth1 = (location0[VECTOR_X] > renderTargetLeft) && (location0[VECTOR_X] < renderTargetRight);
118 | final boolean insideWidth2 = (location1[VECTOR_X] > renderTargetLeft) && (location1[VECTOR_X] < renderTargetRight);
119 | final boolean insideWidth3 = (location2[VECTOR_X] > renderTargetLeft) && (location2[VECTOR_X] < renderTargetRight);
120 | final boolean insideHeight1 = (location0[VECTOR_Y] > renderTargetTop) && (location0[VECTOR_Y] < renderTargetBottom);
121 | final boolean insideHeight2 = (location1[VECTOR_Y] > renderTargetTop) && (location1[VECTOR_Y] < renderTargetBottom);
122 | final boolean insideHeight3 = (location2[VECTOR_Y] > renderTargetTop) && (location2[VECTOR_Y] < renderTargetBottom);
123 | final boolean insideDepth1 = (location0[VECTOR_Z] > 0) && (location0[VECTOR_Z] < FP_ONE);
124 | final boolean insideDepth2 = (location1[VECTOR_Z] > 0) && (location1[VECTOR_Z] < FP_ONE);
125 | final boolean insideDepth3 = (location2[VECTOR_Z] > 0) && (location2[VECTOR_Z] < FP_ONE);
126 | if ((!insideDepth1 && !insideDepth2 && !insideDepth3)
127 | || (!insideHeight1 && !insideHeight2 && !insideHeight3)
128 | || (!insideWidth1 && !insideWidth2 && !insideWidth3)) {
129 | return true;
130 | }
131 | }
132 | return false;
133 | }
134 |
135 | private boolean isBackface() {
136 | final int x0 = location0[VECTOR_X];
137 | final int x1 = location1[VECTOR_X];
138 | final int x2 = location2[VECTOR_X];
139 | final int y0 = location0[VECTOR_Y];
140 | final int y1 = location1[VECTOR_Y];
141 | final int y2 = location2[VECTOR_Y];
142 | final int triangleSize = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
143 | return triangleSize * faceCull < 0;
144 | }
145 |
146 | protected void sortY() {
147 | sortY00 = location0[VECTOR_Y];
148 | sortY01 = location1[VECTOR_Y];
149 | if (sortY00 > sortY01) {
150 | VectorUtils.swap(location0, location1);
151 | }
152 | sortY10 = location1[VECTOR_Y];
153 | sortY11 = location2[VECTOR_Y];
154 | if (sortY10 > sortY11) {
155 | VectorUtils.swap(location1, location2);
156 | }
157 | sortY20 = location0[VECTOR_Y];
158 | sortY21 = location1[VECTOR_Y];
159 | if (sortY20 > sortY21) {
160 | VectorUtils.swap(location0, location1);
161 | }
162 | }
163 |
164 | protected int splitTriangle() {
165 | int dy = FixedPointUtils.divide(location1[VECTOR_Y] - location0[VECTOR_Y], location2[VECTOR_Y] - location0[VECTOR_Y]);
166 | location3[VECTOR_X] = location0[VECTOR_X] + FixedPointUtils.multiply(dy, location2[VECTOR_X] - location0[VECTOR_X]);
167 | location3[VECTOR_Y] = location1[VECTOR_Y];
168 | location3[VECTOR_Z] = location0[VECTOR_Z] + FixedPointUtils.multiply(dy, location2[VECTOR_Z] - location0[VECTOR_Z]);
169 | return dy;
170 | }
171 |
172 | protected void swapSplitedBottomTriangle() {
173 | VectorUtils.swap(location3, location2);
174 | }
175 |
176 | protected void swapSplitedTopTriangle() {
177 | VectorUtils.swap(location3, location2);
178 | VectorUtils.swap(location0, location1);
179 | VectorUtils.swap(location1, location3);
180 | }
181 |
182 | private void drawBottomTriangle() {
183 | initializeBottomTriangle();
184 | if(dx1 < dx2) {
185 | initializeDx2GreaterDx1();
186 | for (; y1 <= y2; y1++) {
187 | drawScanline(x1, x2, y1, z, dz);
188 | incrementBottomDx2GreaterDx1();
189 | }
190 | } else {
191 | initializeDx1GreaterDx2();
192 | for (; y1 <= y2; y1++) {
193 | drawScanline(x1, x2, y1, z, dz);
194 | incrementBottomDx1GreaterDx2();
195 | }
196 | }
197 | }
198 |
199 | protected void initializeBottomTriangle() {
200 | final int xShifted = location0[VECTOR_X] << FP_BIT;
201 | y2y1 = location1[VECTOR_Y] - location0[VECTOR_Y];
202 | y2y1 = y2y1 == 0 ? 1 : y2y1;
203 | y3y1 = y2y1;
204 | y2y1 = FixedPointUtils.divide(FP_ONE, y2y1);
205 | y3y1 = FixedPointUtils.divide(FP_ONE, y3y1);
206 | dx1 = FixedPointUtils.multiply(location1[VECTOR_X] - location0[VECTOR_X], y2y1);
207 | dx2 = FixedPointUtils.multiply(location2[VECTOR_X] - location0[VECTOR_X], y3y1);
208 | dz1 = FixedPointUtils.multiply(location1[VECTOR_Z] - location0[VECTOR_Z], y2y1);
209 | dz2 = FixedPointUtils.multiply(location2[VECTOR_Z] - location0[VECTOR_Z], y3y1);
210 | x1 = xShifted;
211 | x2 = xShifted;
212 | y1 = location0[VECTOR_Y];
213 | y2 = location1[VECTOR_Y];
214 | z = location0[VECTOR_Z] << FP_BIT;
215 | }
216 |
217 | protected void incrementBottomDx2GreaterDx1() {
218 | x1 += dx1;
219 | x2 += dx2;
220 | z += dz1;
221 | }
222 |
223 | protected void incrementBottomDx1GreaterDx2() {
224 | x1 += dx2;
225 | x2 += dx1;
226 | z += dz2;
227 | }
228 |
229 | private void drawTopTriangle() {
230 | initializeTopTriangle();
231 | if (dx1 > dx2) {
232 | initializeDx1GreaterDx2();
233 | for (; y1 > y2; y1--) {
234 | drawScanline(x1, x2, y1, z, dz);
235 | incrementTopDx2GreaterDx1();
236 | }
237 | } else {
238 | initializeDx2GreaterDx1();
239 | for (; y1 > y2; y1--) {
240 | drawScanline(x1, x2, y1, z, dz);
241 | incrementTopDx1GreaterDx2();
242 | }
243 | }
244 | }
245 |
246 | protected void initializeTopTriangle() {
247 | final int xShifted = location2[VECTOR_X] << FP_BIT;
248 | y3y1 = location2[VECTOR_Y] - location0[VECTOR_Y];
249 | y3y2 = location2[VECTOR_Y] - location1[VECTOR_Y];
250 | y3y1 = y3y1 == 0 ? 1 : y3y1;
251 | y3y2 = y3y2 == 0 ? 1 : y3y2;
252 | y3y1 = FixedPointUtils.divide(FP_ONE, y3y1);
253 | y3y2 = FixedPointUtils.divide(FP_ONE, y3y2);
254 | dx1 = FixedPointUtils.multiply(location2[VECTOR_X] - location0[VECTOR_X], y3y1);
255 | dx2 = FixedPointUtils.multiply(location2[VECTOR_X] - location1[VECTOR_X], y3y2);
256 | dz1 = FixedPointUtils.multiply(location2[VECTOR_Z] - location0[VECTOR_Z], y3y1);
257 | dz2 = FixedPointUtils.multiply(location2[VECTOR_Z] - location1[VECTOR_Z], y3y2);
258 | x1 = xShifted;
259 | x2 = xShifted;
260 | y1 = location2[VECTOR_Y];
261 | y2 = location0[VECTOR_Y];
262 | z = location2[VECTOR_Z] << FP_BIT;
263 | }
264 |
265 | protected void incrementTopDx2GreaterDx1() {
266 | x1 -= dx1;
267 | x2 -= dx2;
268 | z -= dz1;
269 | }
270 |
271 | protected void incrementTopDx1GreaterDx2() {
272 | x1 -= dx2;
273 | x2 -= dx1;
274 | z -= dz2;
275 | }
276 |
277 | protected void initializeDx2GreaterDx1() {
278 | dxdx = dx2 - dx1;
279 | dxdx = dxdx == 0 ? 1 : dxdx;
280 | dxdx = FixedPointUtils.divide(FP_ONE, dxdx);
281 | dz = FixedPointUtils.multiply(dz2 - dz1, dxdx);
282 | }
283 |
284 | protected void initializeDx1GreaterDx2() {
285 | dxdx = dx1 - dx2;
286 | dxdx = dxdx == 0 ? 1 : dxdx;
287 | dxdx = FixedPointUtils.divide(FP_ONE, dxdx);
288 | dz = FixedPointUtils.multiply(dz1 - dz2, dxdx);
289 | }
290 |
291 | private void drawScanline(int x1, int x2, int y, int z, int dz) {
292 | x1 >>= FP_BIT;
293 | x2 >>= FP_BIT;
294 | for (; x1 <= x2; x1++) {
295 | location[VECTOR_X] = x1;
296 | location[VECTOR_Y] = y;
297 | location[VECTOR_Z] = z >> FP_BIT;
298 | shader.fragment();
299 | z += dz;
300 | }
301 | }
302 |
303 | public int[] getLocation() {
304 | return location;
305 | }
306 | }
307 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/BasicShader.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.shading;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_BIT;
4 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_X;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Y;
6 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Z;
7 |
8 | import com.johnsproject.jgameengine.model.Camera;
9 | import com.johnsproject.jgameengine.model.Face;
10 | import com.johnsproject.jgameengine.model.Frustum;
11 | import com.johnsproject.jgameengine.model.Material;
12 | import com.johnsproject.jgameengine.model.Texture;
13 | import com.johnsproject.jgameengine.model.Vertex;
14 | import com.johnsproject.jgameengine.rasterization.LinearRasterizer2;
15 | import com.johnsproject.jgameengine.util.ColorUtils;
16 | import com.johnsproject.jgameengine.util.TransformationUtils;
17 | import com.johnsproject.jgameengine.util.VectorUtils;
18 |
19 | /**
20 | * The BasicShader is a shader that only contains the core functionality needed to show models on the screen,
21 | * it draws the models without illumination using it's diffuse color or texture and depth tests each fragment before drawing it.
22 | * It's purpose is to help people learn how the shaders work.
23 | *
24 | * @author John Ferraz Salomon
25 | */
26 | public class BasicShader implements Shader {
27 |
28 | private ForwardShaderBuffer shaderBuffer;
29 |
30 | // rasterizer used to draw the faces
31 | private LinearRasterizer2 rasterizer;
32 | // camera that the graphics engine is currently rendering to
33 | private Camera camera;
34 | // frustum that vertices will be projected to
35 | private Frustum frustum;
36 | // the color of the face to draw
37 | private int diffuseColor;
38 | // texture of the face to draw
39 | private Texture texture;
40 |
41 | public BasicShader() {
42 | rasterizer = new LinearRasterizer2(this);
43 | }
44 |
45 | public void initialize(ShaderBuffer shaderBuffer) {
46 | this.shaderBuffer = (ForwardShaderBuffer) shaderBuffer;
47 | this.camera = shaderBuffer.getCamera();
48 | this.frustum = camera.getFrustum();
49 | }
50 |
51 | public void vertex(Vertex vertex) {
52 | final int[] location = vertex.getLocation();
53 | // reset the location of the vertex
54 | VectorUtils.copy(location, vertex.getWorldLocation());
55 | // transform the vertex to camera space
56 | VectorUtils.multiply(location, camera.getTransform().getSpaceEnterMatrix());
57 | // transform the vertex to screen space
58 | VectorUtils.multiply(location, frustum.getProjectionMatrix());
59 | // port the vertex to the center of the screen
60 | TransformationUtils.screenportVector(location, frustum);
61 | }
62 |
63 | // nothing to do here
64 | public void waitForVertexQueue() {}
65 |
66 | public void geometry(Face face) {
67 | final Material material = face.getMaterial();
68 | diffuseColor = material.getDiffuseColor();
69 | texture = material.getTexture();
70 | // set the texture space location of the vertices to the rasterizer so they're interpolated
71 | setUVs(face);
72 | // draw face
73 | rasterizer.linearDraw2(face, frustum);
74 | }
75 |
76 | private void setUVs(Face face) {
77 | if(texture != null) {
78 | // port uvs to texture space, don't use FixedPointUtils.multiply as the rasterizer interpolates fixed point vectors
79 | int[] uv = face.getUV(0);
80 | int u = uv[VECTOR_X] * texture.getWidth();
81 | int v = uv[VECTOR_Y] * texture.getHeight();
82 | rasterizer.setVector00(u, v, 0);
83 |
84 | uv = face.getUV(1);
85 | u = uv[VECTOR_X] * texture.getWidth();
86 | v = uv[VECTOR_Y] * texture.getHeight();
87 | rasterizer.setVector01(u, v, 0);
88 |
89 | uv = face.getUV(2);
90 | u = uv[VECTOR_X] * texture.getWidth();
91 | v = uv[VECTOR_Y] * texture.getHeight();
92 | rasterizer.setVector02(u, v, 0);
93 | }
94 | }
95 |
96 | // nothing to do here
97 | public void waitForGeometryQueue() {}
98 |
99 | public void fragment() {
100 | final Texture depthBuffer = shaderBuffer.getCamera().getRenderTarget().getDepthBuffer();
101 | final Texture colorBuffer = shaderBuffer.getCamera().getRenderTarget().getColorBuffer();
102 | // get the location of this fragment in screen space
103 | final int x = rasterizer.getLocation()[VECTOR_X];
104 | final int y = rasterizer.getLocation()[VECTOR_Y];
105 | final int z = rasterizer.getLocation()[VECTOR_Z];
106 | // test if there is a fragment front of this fragment, if not draw this fragment
107 | if (depthBuffer.getPixel(x, y) > z) {
108 | // get location of this fragment in texture space
109 | final int[] uv = rasterizer.getVector0();
110 | // get texture color of this fragment
111 | final int textureColor = getFragmentTexelColor(uv);
112 | // calculate the color of this fragment
113 | final int color = ColorUtils.multiplyColor(textureColor, diffuseColor);
114 | // update the color and depth buffers
115 | colorBuffer.setPixel(x, y, color);
116 | depthBuffer.setPixel(x, y, z);
117 | }
118 | }
119 |
120 | private int getFragmentTexelColor(int[] uv) {
121 | if(texture == null) {
122 | return ColorUtils.WHITE;
123 | } else {
124 | // The result will be, but pixels are not accessed with fixed point
125 | final int u = uv[VECTOR_X] >> FP_BIT;
126 | final int v = uv[VECTOR_Y] >> FP_BIT;
127 | return texture.getPixel(u, v);
128 | }
129 | }
130 |
131 | public ShaderBuffer getShaderBuffer() {
132 | return shaderBuffer;
133 | }
134 |
135 | public boolean isGlobal() {
136 | // global shaders are applied to all models
137 | return false;
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/BasicThreadedShader.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.shading;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_BIT;
4 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_X;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Y;
6 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Z;
7 |
8 | import com.johnsproject.jgameengine.model.Camera;
9 | import com.johnsproject.jgameengine.model.Face;
10 | import com.johnsproject.jgameengine.model.FrameBuffer;
11 | import com.johnsproject.jgameengine.model.Frustum;
12 | import com.johnsproject.jgameengine.model.Material;
13 | import com.johnsproject.jgameengine.model.Texture;
14 | import com.johnsproject.jgameengine.model.Vertex;
15 | import com.johnsproject.jgameengine.rasterization.LinearRasterizer2;
16 | import com.johnsproject.jgameengine.util.ColorUtils;
17 | import com.johnsproject.jgameengine.util.TransformationUtils;
18 | import com.johnsproject.jgameengine.util.VectorUtils;
19 |
20 | /**
21 | * The BasicThreadedShader is a shader that only contains the core functionality needed to show models on the screen using
22 | * the multithreaded shading system, it draws the models without illumination using it's diffuse color or texture
23 | * and depth tests each fragment before drawing it. It's purpose is to help people learn how the multithreaded shaders work.
24 | *
25 | * @author John Ferraz Salomon
26 | */
27 | public class BasicThreadedShader extends ThreadedShader {
28 |
29 | public boolean isGlobal() {
30 | // global shaders are applied to all models
31 | return false;
32 | }
33 |
34 | @Override
35 | public ThreadedVertexShader[] createVertexShaders(int count) {
36 | // create shaders used by the threads
37 | final ThreadedVertexShader[] shaders = new VertexShader[count];
38 | for (int i = 0; i < shaders.length; i++)
39 | shaders[i] = new VertexShader();
40 | return shaders;
41 | }
42 |
43 | @Override
44 | public ThreadedGeometryShader[] createGeometryShaders(int count) {
45 | // create shaders used by the threads
46 | final ThreadedGeometryShader[] shaders = new GeometryShader[count];
47 | for (int i = 0; i < shaders.length; i++)
48 | shaders[i] = new GeometryShader();
49 | return shaders;
50 | }
51 |
52 | private static class VertexShader extends ThreadedVertexShader {
53 |
54 | private ForwardShaderBuffer shaderBuffer;
55 |
56 | // camera that the graphics engine is currently rendering to
57 | private Camera camera;
58 | // frustum that vertices will be projected to
59 | private Frustum frustum;
60 |
61 | public void initialize(ShaderBuffer shaderBuffer) {
62 | this.shaderBuffer = (ForwardShaderBuffer) shaderBuffer;
63 | this.camera = shaderBuffer.getCamera();
64 | this.frustum = camera.getFrustum();
65 | }
66 |
67 | public void vertex(Vertex vertex) {
68 | final int[] location = vertex.getLocation();
69 | // reset the location of the vertex
70 | VectorUtils.copy(location, vertex.getWorldLocation());
71 | // transform the vertex to camera space
72 | VectorUtils.multiply(location, camera.getTransform().getSpaceEnterMatrix());
73 | // transform the vertex to screen space
74 | VectorUtils.multiply(location, frustum.getProjectionMatrix());
75 | // port the vertex to the center of the screen
76 | TransformationUtils.screenportVector(location, frustum);
77 | }
78 |
79 | public ShaderBuffer getShaderBuffer() {
80 | return shaderBuffer;
81 | }
82 | }
83 |
84 | private static class GeometryShader extends ThreadedGeometryShader {
85 |
86 | private ForwardShaderBuffer shaderBuffer;
87 |
88 | // rasterizer used to draw the faces
89 | private LinearRasterizer2 rasterizer;
90 | // camera that the graphics engine is currently rendering to
91 | private Camera camera;
92 | // frustum used to cull the faces before drawing
93 | private Frustum frustum;
94 | // the frame buffer the faces will be drawn to
95 | private FrameBuffer frameBuffer;
96 | // the color of the face to draw
97 | private int diffuseColor;
98 | // texture of the face to draw
99 | private Texture texture;
100 |
101 | public GeometryShader() {
102 | rasterizer = new LinearRasterizer2(this);
103 | }
104 |
105 | public void initialize(ShaderBuffer shaderBuffer) {
106 | this.shaderBuffer = (ForwardShaderBuffer) shaderBuffer;
107 | this.camera = shaderBuffer.getCamera();
108 | this.frustum = camera.getFrustum();
109 | this.frameBuffer = camera.getRenderTarget();
110 | }
111 |
112 | public void geometry(Face face) {
113 | final Material material = face.getMaterial();
114 | diffuseColor = material.getDiffuseColor();
115 | texture = material.getTexture();
116 | // set the texture space location of the vertices to the rasterizer so they're interpolated
117 | setUVs(face);
118 | // draw face
119 | rasterizer.linearDraw2(face, frustum);
120 | }
121 |
122 | private void setUVs(Face face) {
123 | if(texture != null) {
124 | // port uvs to texture space, don't use FixedPointUtils.multiply as the rasterizer interpolates fixed point vectors
125 | int[] uv = face.getUV(0);
126 | int u = uv[VECTOR_X] * texture.getWidth();
127 | int v = uv[VECTOR_Y] * texture.getHeight();
128 | rasterizer.setVector00(u, v, 0);
129 |
130 | uv = face.getUV(1);
131 | u = uv[VECTOR_X] * texture.getWidth();
132 | v = uv[VECTOR_Y] * texture.getHeight();
133 | rasterizer.setVector01(u, v, 0);
134 |
135 | uv = face.getUV(2);
136 | u = uv[VECTOR_X] * texture.getWidth();
137 | v = uv[VECTOR_Y] * texture.getHeight();
138 | rasterizer.setVector02(u, v, 0);
139 | }
140 | }
141 |
142 | public void fragment() {
143 | final Texture depthBuffer = frameBuffer.getDepthBuffer();
144 | final Texture colorBuffer = frameBuffer.getColorBuffer();
145 | // get the location of this fragment in screen space
146 | final int x = rasterizer.getLocation()[VECTOR_X];
147 | final int y = rasterizer.getLocation()[VECTOR_Y];
148 | final int z = rasterizer.getLocation()[VECTOR_Z];
149 | // test if there is a fragment front of this fragment, if not draw this fragment
150 | if (depthBuffer.getPixel(x, y) > z) {
151 | // get location of this fragment in texture space
152 | final int[] uv = rasterizer.getVector0();
153 | // get texture color of this fragment
154 | final int textureColor = getFragmentTexelColor(uv);
155 | // calculate the color of this fragment
156 | final int color = ColorUtils.multiplyColor(textureColor, diffuseColor);
157 | // update the color and depth buffers
158 | colorBuffer.setPixel(x, y, color);
159 | depthBuffer.setPixel(x, y, z);
160 | }
161 | }
162 |
163 | private int getFragmentTexelColor(int[] uv) {
164 | if(texture == null) {
165 | return ColorUtils.WHITE;
166 | } else {
167 | // The result will be, but pixels are not accessed with fixed point
168 | final int u = uv[VECTOR_X] >> FP_BIT;
169 | final int v = uv[VECTOR_Y] >> FP_BIT;
170 | return texture.getPixel(u, v);
171 | }
172 | }
173 |
174 | public ShaderBuffer getShaderBuffer() {
175 | return shaderBuffer;
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/DirectionalLightShadowShader.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.shading;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_ONE;
4 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_X;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Y;
6 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Z;
7 |
8 | import com.johnsproject.jgameengine.model.Camera;
9 | import com.johnsproject.jgameengine.model.Face;
10 | import com.johnsproject.jgameengine.model.Frustum;
11 | import com.johnsproject.jgameengine.model.Light;
12 | import com.johnsproject.jgameengine.model.Texture;
13 | import com.johnsproject.jgameengine.model.Vertex;
14 | import com.johnsproject.jgameengine.rasterization.Rasterizer;
15 | import com.johnsproject.jgameengine.util.MatrixUtils;
16 | import com.johnsproject.jgameengine.util.TransformationUtils;
17 | import com.johnsproject.jgameengine.util.VectorUtils;
18 |
19 | public class DirectionalLightShadowShader extends ThreadedShader {
20 |
21 | public boolean isGlobal() {
22 | return true;
23 | }
24 |
25 | @Override
26 | public ThreadedVertexShader[] createVertexShaders(int count) {
27 | final ThreadedVertexShader[] shaders = new VertexShader[count];
28 | for (int i = 0; i < shaders.length; i++)
29 | shaders[i] = new VertexShader();
30 | return shaders;
31 | }
32 |
33 | @Override
34 | public ThreadedGeometryShader[] createGeometryShaders(int count) {
35 | final ThreadedGeometryShader[] shaders = new GeometryShader[count];
36 | for (int i = 0; i < shaders.length; i++)
37 | shaders[i] = new GeometryShader();
38 | return shaders;
39 | }
40 |
41 | private static class VertexShader extends ThreadedVertexShader {
42 |
43 | private ForwardShaderBuffer shaderBuffer;
44 | private Frustum lightFrustum;
45 |
46 | public void initialize(ShaderBuffer shaderBuffer) {
47 | this.shaderBuffer = (ForwardShaderBuffer) shaderBuffer;
48 | initialize();
49 | }
50 |
51 | private void initialize() {
52 | lightFrustum = shaderBuffer.getDirectionalLightFrustum();
53 | }
54 |
55 | public void vertex(Vertex vertex) {
56 | if(shaderBuffer.getShadowDirectionalLight() != null) {
57 | final int[] location = vertex.getLocation();
58 | VectorUtils.copy(location, vertex.getWorldLocation());
59 | VectorUtils.multiply(location, lightFrustum.getProjectionMatrix());
60 | TransformationUtils.screenportVector(location, lightFrustum);
61 | }
62 | }
63 |
64 | public ShaderBuffer getShaderBuffer() {
65 | return shaderBuffer;
66 | }
67 | }
68 |
69 | private static class GeometryShader extends ThreadedGeometryShader {
70 |
71 | private ForwardShaderBuffer shaderBuffer;
72 | private final Rasterizer rasterizer = new Rasterizer(this);
73 | private Light light;
74 | private Frustum lightFrustum;
75 | private Texture shadowMap;
76 | private int shadowBias;
77 |
78 | private Frustum cameraFrustum;
79 | private int[][] cameraMatrix = MatrixUtils.indentityMatrix();
80 | private int renderTargetLeft;
81 | private int renderTargetRight;
82 | private int renderTargetTop;
83 | private int renderTargetBottom;
84 | private int[] location0 = VectorUtils.emptyVector();
85 | private int[] location1 = VectorUtils.emptyVector();
86 | private int[] location2 = VectorUtils.emptyVector();
87 |
88 | public void initialize(ShaderBuffer shaderBuffer) {
89 | this.shaderBuffer = (ForwardShaderBuffer) shaderBuffer;
90 | initialize();
91 | }
92 |
93 | private void initialize() {
94 | light = shaderBuffer.getShadowDirectionalLight();
95 | lightFrustum = shaderBuffer.getDirectionalLightFrustum();
96 | shadowMap = shaderBuffer.getDirectionalShadowMap();
97 | if(light != null) {
98 | shadowBias = light.getShadowBias() >> 10;
99 | final Camera camera = shaderBuffer.getCamera();
100 | cameraFrustum = camera.getFrustum();
101 | final int[][] cameraSpaceMatrix = camera.getTransform().getSpaceEnterMatrix();
102 | final int[][] projectionMatrix = cameraFrustum.getProjectionMatrix();
103 |
104 | MatrixUtils.multiply(projectionMatrix, cameraSpaceMatrix, cameraMatrix);
105 |
106 | final int tolerance = 1024;
107 | renderTargetLeft = cameraFrustum.getRenderTargetLeft() - tolerance;
108 | renderTargetRight = cameraFrustum.getRenderTargetRight() + tolerance;
109 | renderTargetTop = cameraFrustum.getRenderTargetTop() - tolerance;
110 | renderTargetBottom = cameraFrustum.getRenderTargetBottom() + tolerance;
111 | }
112 | }
113 |
114 | public void geometry(Face face) {
115 | if((light != null) && isInCameraView(face)) {
116 | rasterizer.setFrustumCull(false);
117 | rasterizer.draw(face, lightFrustum);
118 | }
119 | }
120 |
121 | private boolean isInCameraView(Face face) {
122 | VectorUtils.copy(location0, face.getVertex(0).getWorldLocation());
123 | VectorUtils.multiply(location0, cameraMatrix);
124 | TransformationUtils.screenportVector(location0, cameraFrustum);
125 |
126 | VectorUtils.copy(location1, face.getVertex(1).getWorldLocation());
127 | VectorUtils.multiply(location1, cameraMatrix);
128 | TransformationUtils.screenportVector(location1, cameraFrustum);
129 |
130 | VectorUtils.copy(location2, face.getVertex(2).getWorldLocation());
131 | VectorUtils.multiply(location2, cameraMatrix);
132 | TransformationUtils.screenportVector(location2, cameraFrustum);
133 |
134 | final boolean insideWidth1 = (location0[VECTOR_X] > renderTargetLeft) && (location0[VECTOR_X] < renderTargetRight);
135 | final boolean insideWidth2 = (location1[VECTOR_X] > renderTargetLeft) && (location1[VECTOR_X] < renderTargetRight);
136 | final boolean insideWidth3 = (location2[VECTOR_X] > renderTargetLeft) && (location2[VECTOR_X] < renderTargetRight);
137 | final boolean insideHeight1 = (location0[VECTOR_Y] > renderTargetTop) && (location0[VECTOR_Y] < renderTargetBottom);
138 | final boolean insideHeight2 = (location1[VECTOR_Y] > renderTargetTop) && (location1[VECTOR_Y] < renderTargetBottom);
139 | final boolean insideHeight3 = (location2[VECTOR_Y] > renderTargetTop) && (location2[VECTOR_Y] < renderTargetBottom);
140 | final boolean insideDepth1 = (location0[VECTOR_Z] > 0) && (location0[VECTOR_Z] < FP_ONE);
141 | final boolean insideDepth2 = (location1[VECTOR_Z] > 0) && (location1[VECTOR_Z] < FP_ONE);
142 | final boolean insideDepth3 = (location2[VECTOR_Z] > 0) && (location2[VECTOR_Z] < FP_ONE);
143 | if ((!insideDepth1 && !insideDepth2 && !insideDepth3)
144 | || (!insideHeight1 && !insideHeight2 && !insideHeight3)
145 | || (!insideWidth1 && !insideWidth2 && !insideWidth3)) {
146 | return false;
147 | }
148 | return true;
149 | }
150 |
151 | public void fragment() {
152 | final int x = rasterizer.getLocation()[VECTOR_X];
153 | final int y = rasterizer.getLocation()[VECTOR_Y];
154 | final int z = rasterizer.getLocation()[VECTOR_Z] + shadowBias;
155 | if (shadowMap.getPixel(x, y) > z) {
156 | shadowMap.setPixel(x, y, z);
157 | }
158 | }
159 |
160 | public ShaderBuffer getShaderBuffer() {
161 | return shaderBuffer;
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/ForwardShaderBuffer.java:
--------------------------------------------------------------------------------
1 |
2 | package com.johnsproject.jgameengine.shading;
3 |
4 | import static com.johnsproject.jgameengine.util.FixedPointUtils.*;
5 |
6 | import java.util.List;
7 |
8 | import com.johnsproject.jgameengine.model.Camera;
9 | import com.johnsproject.jgameengine.model.Frustum;
10 | import com.johnsproject.jgameengine.model.FrustumType;
11 | import com.johnsproject.jgameengine.model.Light;
12 | import com.johnsproject.jgameengine.model.Texture;
13 | import com.johnsproject.jgameengine.util.MatrixUtils;
14 | import com.johnsproject.jgameengine.util.VectorUtils;
15 |
16 | public class ForwardShaderBuffer implements ShaderBuffer {
17 |
18 | private Camera camera;
19 | private List lights;
20 |
21 | private final int[][] projectionMatrix;
22 |
23 | private Light shadowDirectionalLight;
24 | private final Frustum directionalLightFrustum;
25 | private final Texture directionalShadowMap;
26 |
27 | private Light shadowSpotLight;
28 | private final Frustum spotLightFrustum;
29 | private final Texture spotShadowMap;
30 |
31 | private boolean foundMainDirectionalLight;
32 | private long spotLightDistance;
33 |
34 | public ForwardShaderBuffer() {
35 | this.projectionMatrix = MatrixUtils.indentityMatrix();
36 |
37 | this.directionalLightFrustum = new Frustum(0, FP_ONE, 0, FP_ONE, FP_ONE, FP_ONE * 10000);
38 | this.directionalLightFrustum.setType(FrustumType.ORTHOGRAPHIC);
39 | this.directionalLightFrustum.setFocalLength(FP_ONE >> 3);
40 | this.directionalShadowMap = new Texture(2048, 2048);
41 |
42 | this.spotLightFrustum = new Frustum(0, FP_ONE, 0, FP_ONE, FP_HALF, FP_ONE * 1000);
43 | this.spotLightFrustum.setFocalLength(FP_ONE >> 2);
44 | this.spotShadowMap = new Texture(1024, 1024);
45 | }
46 |
47 | public void initialize(Camera camera, List lights) {
48 | this.camera = camera;
49 | this.lights = lights;
50 | final int[] cameraLocation = camera.getTransform().getLocation();
51 | long maxLightDistance = camera.getMaxLightDistance();
52 | // square the far distance because square distance calculation is used to save performance
53 | maxLightDistance = (maxLightDistance * maxLightDistance) >> FP_BIT;
54 | resetLightIndices();
55 | for(int i = 0; i < lights.size(); i++) {
56 | final Light light = lights.get(i);
57 | if(!light.isActive())
58 | continue;
59 | final int[] lightLocation = light.getTransform().getLocation();
60 | final long lightDistance = VectorUtils.squaredDistance(lightLocation, cameraLocation);
61 | light.setCulled(lightDistance > maxLightDistance);
62 | if(light.isCulled() || !light.hasShadow())
63 | continue;
64 | searchNearestLights(light, lightDistance);
65 | }
66 | initializeLightMatrices();
67 | }
68 |
69 | private void resetLightIndices() {
70 | shadowDirectionalLight = null;
71 | shadowSpotLight = null;
72 | foundMainDirectionalLight = false;
73 | spotLightDistance = Integer.MAX_VALUE;
74 | }
75 |
76 | private void searchNearestLights(Light light, long lightDistance) {
77 | switch (light.getType()) {
78 | case DIRECTIONAL:
79 | searchNearestDirectionalLight(light, lightDistance);
80 | break;
81 | case SPOT:
82 | searchNearestSpotLight(light, lightDistance);
83 | break;
84 | default:
85 | break;
86 | }
87 | }
88 |
89 | private void searchNearestDirectionalLight(Light light, long lightDistance) {
90 | if(!foundMainDirectionalLight) {
91 | shadowDirectionalLight = light;
92 | if(light.isMain()) {
93 | foundMainDirectionalLight = true;
94 | }
95 | }
96 | }
97 |
98 | private void searchNearestSpotLight(Light light, long lightDistance) {
99 | if(spotLightDistance == Integer.MIN_VALUE)
100 | return;
101 | if(lightDistance < spotLightDistance) {
102 | spotLightDistance = lightDistance;
103 | shadowSpotLight = light;
104 | if(light.isMain()) {
105 | spotLightDistance = Integer.MIN_VALUE;
106 | }
107 | }
108 | }
109 |
110 | private void initializeLightMatrices() {
111 | if(camera.isMain()) {
112 | initializeDirectionalLightMatrix();
113 | initializeSpotLightMatrix();
114 | }
115 | }
116 |
117 | private void initializeDirectionalLightMatrix() {
118 | if (shadowDirectionalLight != null) {
119 | final int portWidth = directionalShadowMap.getWidth();
120 | final int portHeight = directionalShadowMap.getHeight();
121 | directionalLightFrustum.setRenderTargetSize(portWidth, portHeight);
122 | directionalShadowMap.fill(Integer.MAX_VALUE);
123 | final int[][] lightSpaceMatrix = shadowDirectionalLight.getTransform().getSpaceEnterMatrix();
124 | final int[][] frustumProjectionMatrix = directionalLightFrustum.getProjectionMatrix();
125 | MatrixUtils.copy(projectionMatrix, frustumProjectionMatrix);
126 | MatrixUtils.multiply(projectionMatrix, lightSpaceMatrix, frustumProjectionMatrix);
127 | }
128 | }
129 |
130 | private void initializeSpotLightMatrix() {
131 | if (shadowSpotLight != null) {
132 | final int portWidth = spotShadowMap.getWidth();
133 | final int portHeight = spotShadowMap.getHeight();
134 | spotLightFrustum.setRenderTargetSize(portWidth, portHeight);
135 | spotShadowMap.fill(Integer.MAX_VALUE);
136 | final int[][] lightSpaceMatrix = shadowSpotLight.getTransform().getSpaceEnterMatrix();
137 | final int[][] frustumProjectionMatrix = spotLightFrustum.getProjectionMatrix();
138 | MatrixUtils.copy(projectionMatrix, frustumProjectionMatrix);
139 | MatrixUtils.multiply(projectionMatrix, lightSpaceMatrix, frustumProjectionMatrix);
140 | }
141 | }
142 |
143 | public Camera getCamera() {
144 | return camera;
145 | }
146 |
147 | public List getLights() {
148 | return lights;
149 | }
150 |
151 | public Light getShadowDirectionalLight() {
152 | return shadowDirectionalLight;
153 | }
154 |
155 | public Texture getDirectionalShadowMap() {
156 | return directionalShadowMap;
157 | }
158 |
159 | public Frustum getDirectionalLightFrustum() {
160 | return directionalLightFrustum;
161 | }
162 |
163 | public Light getShadowSpotLight() {
164 | return shadowSpotLight;
165 | }
166 |
167 | public Texture getSpotShadowMap() {
168 | return spotShadowMap;
169 | }
170 |
171 | public Frustum getSpotLightFrustum() {
172 | return spotLightFrustum;
173 | }
174 | }
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/Shader.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.shading;
2 |
3 | import com.johnsproject.jgameengine.model.Face;
4 | import com.johnsproject.jgameengine.model.Vertex;
5 |
6 | public interface Shader {
7 |
8 | void initialize(ShaderBuffer shaderBuffer);
9 |
10 | void vertex(Vertex vertex);
11 |
12 | /**
13 | * Used to notify the {@link ThreadedShader} that all vertices have been shaded
14 | * and it should wait until the queue is empty. No implementation is required.
15 | */
16 | void waitForVertexQueue();
17 |
18 | void geometry(Face face);
19 |
20 | /**
21 | * Used to notify the {@link ThreadedShader} that all faces have been shaded
22 | * and it should wait until the queue is empty. No implementation is required.
23 | */
24 | void waitForGeometryQueue();
25 |
26 | void fragment();
27 |
28 | ShaderBuffer getShaderBuffer();
29 |
30 | /**
31 | * Is this shader a global shader?
32 | * Global shaders are used on all Models.
33 | *
34 | * @return If this shader is a global shader.
35 | */
36 | boolean isGlobal();
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/ShaderBuffer.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.shading;
2 |
3 | import java.util.List;
4 |
5 | import com.johnsproject.jgameengine.model.Camera;
6 | import com.johnsproject.jgameengine.model.Light;
7 |
8 | public interface ShaderBuffer {
9 |
10 | public void initialize(Camera camera, List lights);
11 |
12 | public List getLights();
13 |
14 | public Camera getCamera();
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/SpotLightShadowShader.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.shading;
2 |
3 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_X;
4 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Y;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Z;
6 |
7 | import com.johnsproject.jgameengine.model.Face;
8 | import com.johnsproject.jgameengine.model.Frustum;
9 | import com.johnsproject.jgameengine.model.Light;
10 | import com.johnsproject.jgameengine.model.Texture;
11 | import com.johnsproject.jgameengine.model.Vertex;
12 | import com.johnsproject.jgameengine.rasterization.Rasterizer;
13 | import com.johnsproject.jgameengine.util.TransformationUtils;
14 | import com.johnsproject.jgameengine.util.VectorUtils;
15 |
16 | public class SpotLightShadowShader extends ThreadedShader {
17 |
18 | public boolean isGlobal() {
19 | return true;
20 | }
21 |
22 | @Override
23 | public ThreadedVertexShader[] createVertexShaders(int count) {
24 | final ThreadedVertexShader[] shaders = new VertexShader[count];
25 | for (int i = 0; i < shaders.length; i++)
26 | shaders[i] = new VertexShader();
27 | return shaders;
28 | }
29 |
30 | @Override
31 | public ThreadedGeometryShader[] createGeometryShaders(int count) {
32 | final ThreadedGeometryShader[] shaders = new GeometryShader[count];
33 | for (int i = 0; i < shaders.length; i++)
34 | shaders[i] = new GeometryShader();
35 | return shaders;
36 | }
37 |
38 | private static class VertexShader extends ThreadedVertexShader {
39 |
40 | private ForwardShaderBuffer shaderBuffer;
41 | private Frustum lightFrustum;
42 |
43 | public void initialize(ShaderBuffer shaderBuffer) {
44 | this.shaderBuffer = (ForwardShaderBuffer) shaderBuffer;
45 | initialize();
46 | }
47 |
48 | private void initialize() {
49 | lightFrustum = shaderBuffer.getSpotLightFrustum();
50 | }
51 |
52 | public void vertex(Vertex vertex) {
53 | if(shaderBuffer.getShadowSpotLight() != null) {
54 | final int[] location = vertex.getLocation();
55 | VectorUtils.copy(location, vertex.getWorldLocation());
56 | VectorUtils.multiply(location, lightFrustum.getProjectionMatrix());
57 | TransformationUtils.screenportVector(location, lightFrustum);
58 | }
59 | }
60 |
61 | public ShaderBuffer getShaderBuffer() {
62 | return shaderBuffer;
63 | }
64 | }
65 |
66 | private static class GeometryShader extends ThreadedGeometryShader {
67 |
68 | private ForwardShaderBuffer shaderBuffer;
69 | private final Rasterizer rasterizer;
70 | private Light light;
71 | private Frustum lightFrustum;
72 | private Texture shadowMap;
73 | private int shadowBias;
74 |
75 |
76 | public GeometryShader() {
77 | rasterizer = new Rasterizer(this);
78 | }
79 |
80 | public void initialize(ShaderBuffer shaderBuffer) {
81 | this.shaderBuffer = (ForwardShaderBuffer) shaderBuffer;
82 | initialize();
83 | }
84 |
85 | private void initialize() {
86 | light = shaderBuffer.getShadowSpotLight();
87 | lightFrustum = shaderBuffer.getSpotLightFrustum();
88 | shadowMap = shaderBuffer.getSpotShadowMap();
89 | if(light != null)
90 | shadowBias = light.getShadowBias() >> 10;
91 | }
92 |
93 | public void geometry(Face face) {
94 | if(light != null) {
95 | rasterizer.setFrustumCull(false);
96 | rasterizer.draw(face, lightFrustum);
97 | }
98 | }
99 |
100 | public void fragment() {
101 | final int x = rasterizer.getLocation()[VECTOR_X];
102 | final int y = rasterizer.getLocation()[VECTOR_Y];
103 | final int z = rasterizer.getLocation()[VECTOR_Z] + shadowBias;
104 | if (shadowMap.getPixel(x, y) > z) {
105 | shadowMap.setPixel(x, y, z);
106 | }
107 | }
108 |
109 | public ShaderBuffer getShaderBuffer() {
110 | return shaderBuffer;
111 | }
112 |
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/shading/ThreadedShader.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.shading;
2 |
3 | import java.util.concurrent.ArrayBlockingQueue;
4 | import java.util.concurrent.BlockingQueue;
5 |
6 | import com.johnsproject.jgameengine.model.Face;
7 | import com.johnsproject.jgameengine.model.Vertex;
8 |
9 | public abstract class ThreadedShader implements Shader {
10 |
11 | private final BlockingQueue vertexQueue;
12 | private final BlockingQueue geometryQueue;
13 | private final ThreadedVertexShader[] vertexShaders;
14 | private final ThreadedGeometryShader[] geometryShaders;
15 |
16 | public ThreadedShader() {
17 | final int coreCount = Runtime.getRuntime().availableProcessors();
18 | vertexQueue = new ArrayBlockingQueue(coreCount * 32);
19 | geometryQueue = new ArrayBlockingQueue(coreCount * 32);
20 |
21 | vertexShaders = createVertexShaders(coreCount);
22 | for (int i = 0; i < vertexShaders.length; i++) {
23 | vertexShaders[i].setQueue(vertexQueue);
24 | vertexShaders[i].start();
25 | }
26 |
27 | geometryShaders = createGeometryShaders(coreCount);
28 | for (int i = 0; i < geometryShaders.length; i++) {
29 | geometryShaders[i].setQueue(geometryQueue);
30 | geometryShaders[i].start();
31 | }
32 | }
33 |
34 | public abstract ThreadedVertexShader[] createVertexShaders(int count);
35 |
36 | public abstract ThreadedGeometryShader[] createGeometryShaders(int count);
37 |
38 | public void initialize(ShaderBuffer shaderBuffer) {
39 | for (int i = 0; i < vertexShaders.length; i++)
40 | vertexShaders[i].initialize(shaderBuffer);
41 |
42 | for (int i = 0; i < geometryShaders.length; i++)
43 | geometryShaders[i].initialize(shaderBuffer);
44 | }
45 |
46 | public void vertex(Vertex vertex) {
47 | try {
48 | vertexQueue.put(vertex);
49 | } catch (InterruptedException e) {
50 | e.printStackTrace();
51 | }
52 | }
53 |
54 | public void geometry(Face face) {
55 | try {
56 | geometryQueue.put(face);
57 | } catch (InterruptedException e) {
58 | e.printStackTrace();
59 | }
60 | }
61 |
62 | public void fragment() {}
63 |
64 | public ShaderBuffer getShaderBuffer() {
65 | return vertexShaders[0].getShaderBuffer();
66 | }
67 |
68 | public void waitForVertexQueue() {
69 | for (int i = 0; i < vertexShaders.length; i++)
70 | if((!vertexQueue.isEmpty()) || (vertexShaders[i].getState() != Thread.State.WAITING))
71 | i = 0;
72 | }
73 |
74 | public void waitForGeometryQueue() {
75 | for (int i = 0; i < geometryShaders.length; i++)
76 | if((!geometryQueue.isEmpty()) || (geometryShaders[i].getState() != Thread.State.WAITING))
77 | i = 0;
78 | }
79 |
80 | protected static abstract class ThreadedVertexShader extends Thread implements Shader {
81 |
82 | private BlockingQueue queue;
83 |
84 | public ThreadedVertexShader() {
85 | super("VertexShaderThread");
86 | }
87 |
88 | public void waitForVertexQueue() { }
89 |
90 | public void geometry(Face face) { }
91 |
92 | public void waitForGeometryQueue() { }
93 |
94 | public void fragment() { }
95 |
96 | public void run() {
97 | while(true) {
98 | try {
99 | vertex(queue.take());
100 | } catch (InterruptedException e) {
101 | e.printStackTrace();
102 | }
103 | }
104 | }
105 |
106 | private void setQueue(BlockingQueue queue) {
107 | this.queue = queue;
108 | }
109 |
110 | public boolean isGlobal() {
111 | return false;
112 | }
113 | }
114 |
115 | protected static abstract class ThreadedGeometryShader extends Thread implements Shader {
116 |
117 | private BlockingQueue queue;
118 |
119 | public ThreadedGeometryShader() {
120 | super("GeometryShaderThread");
121 | }
122 |
123 | public void vertex(Vertex vertex) { }
124 |
125 | public void waitForVertexQueue() { }
126 |
127 | public void waitForGeometryQueue() { }
128 |
129 | public void run() {
130 | while(true) {
131 | try {
132 | geometry(queue.take());
133 | } catch (InterruptedException e) {
134 | e.printStackTrace();
135 | }
136 | }
137 | }
138 |
139 | private void setQueue(BlockingQueue queue) {
140 | this.queue = queue;
141 | }
142 |
143 | public boolean isGlobal() {
144 | return false;
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/util/ColorUtils.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.util;
2 |
3 | import java.awt.Color;
4 | import java.awt.image.BufferedImage;
5 |
6 | /**
7 | * The ColorUtils class contains methods for generating integer sRGB colors
8 | * and performing integer sRGB color operations such as lerp, multiply and add.
9 | *
10 | * @author John Ferraz Salomon
11 | */
12 | public final class ColorUtils {
13 |
14 | /**
15 | * Some default integer sRGB colors.
16 | */
17 | public static final int WHITE = Color.white.getRGB(),
18 | BLUE = Color.blue.getRGB(),
19 | CYAN = Color.cyan.getRGB(),
20 | GREEN = Color.green.getRGB(),
21 | ORANGE = Color.orange.getRGB(),
22 | RED = Color.red.getRGB(),
23 | YELLOW = Color.yellow.getRGB(),
24 | MAGENTA = Color.magenta.getRGB(),
25 | PINK = Color.pink.getRGB(),
26 | GRAY = Color.gray.getRGB(),
27 | BLACK = Color.black.getRGB();
28 |
29 |
30 | /**
31 | * Bits count representing each color value.
32 | */
33 | public static final byte COLOR_BITS = 8;
34 |
35 | /**
36 | * Value representing the range of each color value. (0 - {@value #COLOR_ONE})
37 | */
38 | public static final int COLOR_ONE = (1 << COLOR_BITS) - 1;
39 |
40 | /**
41 | * Number representing the BufferedImage color type that the ColorProcessor handles.
42 | */
43 | public static final byte COLOR_TYPE = BufferedImage.TYPE_INT_ARGB;
44 |
45 | private static final byte GREENSHIFT = COLOR_BITS;
46 | private static final byte REDSHIFT = COLOR_BITS * 2;
47 | private static final byte ALPHASHIFT = COLOR_BITS * 3;
48 |
49 | private ColorUtils() { }
50 |
51 | /**
52 | * Returns a integer sRGB color.
53 | * (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue)
54 | *
55 | * @param a
56 | * @param r
57 | * @param g
58 | * @param b
59 | * @return
60 | */
61 | public static int toColor(int a, int r, int g, int b) {
62 | int color = 0;
63 | color |= Math.min(COLOR_ONE, Math.max(a, 0)) << ALPHASHIFT;
64 | color |= Math.min(COLOR_ONE, Math.max(r, 0)) << REDSHIFT;
65 | color |= Math.min(COLOR_ONE, Math.max(g, 0)) << GREENSHIFT;
66 | color |= Math.min(COLOR_ONE, Math.max(b, 0));
67 | return color;
68 | }
69 |
70 | /**
71 | * Returns a integer sRGB color, with alpha = 255.
72 | * (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue)
73 | *
74 | * @param r
75 | * @param g
76 | * @param b
77 | * @return
78 | */
79 | public static int toColor(int r, int g, int b) {
80 | int color = 0;
81 | color |= (255) << ALPHASHIFT;
82 | color |= Math.min(COLOR_ONE, Math.max(r, 0)) << REDSHIFT;
83 | color |= Math.min(COLOR_ONE, Math.max(g, 0)) << GREENSHIFT;
84 | color |= Math.min(COLOR_ONE, Math.max(b, 0));
85 | return color;
86 | }
87 |
88 | /**
89 | * Returns the blue component of the given color.
90 | * The color should be in sRGB color space.
91 | *
92 | * @param color
93 | * @return
94 | */
95 | public static int getBlue(int color) {
96 | return (color) & COLOR_ONE;
97 | }
98 |
99 | /**
100 | * Returns the green component of the given color.
101 | * The color should be in sRGB color space.
102 | *
103 | * @param color
104 | * @return
105 | */
106 | public static int getGreen(int color) {
107 | return (color >> GREENSHIFT) & COLOR_ONE;
108 | }
109 |
110 | /**
111 | * Returns the red component of the given color.
112 | * The color should be in sRGB color space.
113 | *
114 | * @param color
115 | * @return
116 | */
117 | public static int getRed(int color) {
118 | return (color >> REDSHIFT) & COLOR_ONE;
119 | }
120 |
121 | /**
122 | * Returns the alpha component of the given color.
123 | * The color should be in sRGB color space.
124 | *
125 | * @param color
126 | * @return
127 | */
128 | public static int getAlpha(int color) {
129 | return (color >> ALPHASHIFT) & COLOR_ONE;
130 | }
131 |
132 |
133 | /**
134 | * Returns the result of the multiplication of color and factor.
135 | * This method only changes the RGB values of the color.
136 | *
137 | * @param color
138 | * @param factor a fixed point value between 0 and {@link FixedPointUtils#FP_ONE}
139 | * @return
140 | */
141 | public static int multiply(int color, int factor) {
142 | int r = getRed(color), g = getGreen(color), b = getBlue(color), a = getAlpha(color);
143 | r = FixedPointUtils.multiply(r, factor);
144 | g = FixedPointUtils.multiply(g, factor);
145 | b = FixedPointUtils.multiply(b, factor);
146 | return toColor(a, r, g, b);
147 | }
148 |
149 | /**
150 | * Returns the result of the multiplication of color and factor.
151 | *
152 | * @param color
153 | * @param factor a fixed point value between 0 and {@link FixedPointUtils#FP_ONE}
154 | * @return
155 | */
156 | public static int multiplyARGB(int color, int factor) {
157 | int r = getRed(color), g = getGreen(color), b = getBlue(color), a = getAlpha(color);
158 | r = FixedPointUtils.multiply(r, factor);
159 | g = FixedPointUtils.multiply(g, factor);
160 | b = FixedPointUtils.multiply(b, factor);
161 | a = FixedPointUtils.multiply(a, factor);
162 | return toColor(a, r, g, b);
163 | }
164 |
165 | /**
166 | * Returns a color containing the added components of color1 and color2.
167 | * This method only changes the RGB values of the color.
168 | *
169 | * @param color1
170 | * @param color2
171 | * @return
172 | */
173 | public static int add(int color1, int color2) {
174 | int r1 = getRed(color1), g1 = getGreen(color1), b1 = getBlue(color1), a1 = getAlpha(color1);
175 | int r2 = getRed(color2), g2 = getGreen(color2), b2 = getBlue(color2);
176 | int r = (r1 + r2);
177 | int g = (g1 + g2);
178 | int b = (b1 + b2);
179 | return toColor(a1, r, g, b);
180 | }
181 |
182 | /**
183 | * Returns a color containing the added components of color1 and color2.
184 | *
185 | * @param color1
186 | * @param color2
187 | * @return
188 | */
189 | public static int addARGB(int color1, int color2) {
190 | int r1 = getRed(color1), g1 = getGreen(color1), b1 = getBlue(color1), a1 = getAlpha(color1);
191 | int r2 = getRed(color2), g2 = getGreen(color2), b2 = getBlue(color2), a2 = getAlpha(color2);
192 | int r = (r1 + r2);
193 | int g = (g1 + g2);
194 | int b = (b1 + b2);
195 | int a = (a1 + a2);
196 | return toColor(a, r, g, b);
197 | }
198 |
199 | /**
200 | * Returns a color containing the multiplied components of color1 and color2.
201 | * This method only changes the RGB values of the color.
202 | *
203 | * @param color1
204 | * @param color2
205 | * @return
206 | */
207 | public static int multiplyColor(int color1, int color2) {
208 | int r1 = getRed(color1), g1 = getGreen(color1), b1 = getBlue(color1), a1 = getAlpha(color1);
209 | int r2 = getRed(color2), g2 = getGreen(color2), b2 = getBlue(color2);
210 | int r = (r1 * r2) >> COLOR_BITS;
211 | int g = (g1 * g2) >> COLOR_BITS;
212 | int b = (b1 * b2) >> COLOR_BITS;
213 | return toColor(a1, r, g, b);
214 | }
215 |
216 | /**
217 | * Returns a color containing the multiplied components of color1 and color2.
218 | *
219 | * @param color1
220 | * @param color2
221 | * @return
222 | */
223 | public static int multiplyColorARGB(int color1, int color2) {
224 | int r1 = getRed(color1), g1 = getGreen(color1), b1 = getBlue(color1), a1 = getAlpha(color1);
225 | int r2 = getRed(color2), g2 = getGreen(color2), b2 = getBlue(color2), a2 = getAlpha(color2);
226 | int r = (r1 * r2) >> COLOR_BITS;
227 | int g = (g1 * g2) >> COLOR_BITS;
228 | int b = (b1 * b2) >> COLOR_BITS;
229 | int a = (a1 * a2) >> COLOR_BITS;
230 | return toColor(a, r, g, b);
231 | }
232 |
233 | /**
234 | * Returns a color that is the linear interpolation of color1 and color2 by the given factor.
235 | * The factor should be in the range 0-{@value #COLOR_ONE}.
236 | * This method only changes the RGB values of the color.
237 | *
238 | * @param color1
239 | * @param color2
240 | * @param factor
241 | * @return
242 | */
243 | public static int lerp(int color1, int color2, int factor) {
244 | int r1 = getRed(color1), g1 = getGreen(color1), b1 = getBlue(color1), a1 = getAlpha(color1);
245 | int r2 = getRed(color2), g2 = getGreen(color2), b2 = getBlue(color2);
246 | int r = (r1 + (((r2 - r1) * factor) >> COLOR_BITS));
247 | int g = (g1 + (((g2 - g1) * factor) >> COLOR_BITS));
248 | int b = (b1 + (((b2 - b1) * factor) >> COLOR_BITS));
249 | return toColor(a1, r, g, b);
250 | }
251 |
252 | /**
253 | * Returns a color that is the linear interpolation of color1 and color2 by the given factor.
254 | * The factor should be in the range 0-{@value #COLOR_ONE}.
255 | *
256 | * @param color1
257 | * @param color2
258 | * @param factor
259 | * @return
260 | */
261 | public static int lerpARGB(int color1, int color2, int factor) {
262 | int r1 = getRed(color1), g1 = getGreen(color1), b1 = getBlue(color1), a1 = getAlpha(color1);
263 | int r2 = getRed(color2), g2 = getGreen(color2), b2 = getBlue(color2), a2 = getAlpha(color2);
264 | int r = (r1 + (((r2 - r1) * factor) >> COLOR_BITS));
265 | int g = (g1 + (((g2 - g1) * factor) >> COLOR_BITS));
266 | int b = (b1 + (((b2 - b1) * factor) >> COLOR_BITS));
267 | int a = (a1 + (((a2 - a1) * factor) >> COLOR_BITS));
268 | return toColor(a, r, g, b);
269 | }
270 |
271 | /**
272 | * Returns a string containing the data of the given color.
273 | *
274 | * @param color
275 | * @return
276 | */
277 | public static String toString(int color) {
278 | int r1 = getRed(color), g1 = getGreen(color), b1 = getBlue(color), a1 = getAlpha(color);
279 | return "(" + a1 + ", " + g1 + ", " + b1 + ", " + r1 + ")";
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/util/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.util;
2 |
3 | import java.awt.Image;
4 | import java.awt.image.BufferedImage;
5 | import java.io.BufferedReader;
6 | import java.io.FileInputStream;
7 | import java.io.FileNotFoundException;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 | import java.io.InputStreamReader;
12 | import java.io.ObjectInputStream;
13 | import java.io.ObjectOutputStream;
14 | import java.io.Serializable;
15 |
16 | import javax.imageio.ImageIO;
17 |
18 | /**
19 | * The FileUtils class contains methods for writing/reading files from the file system.
20 | *
21 | * @author John Ferraz Salomon
22 | */
23 | public final class FileUtils {
24 |
25 | private FileUtils() {}
26 |
27 | /**
28 | * Reads the content of the file at the specified path and returns it.
29 | *
30 | * @param path of the file
31 | * @return String representation of the data in the specified file.
32 | * @throws IOException If the file does not exist, is a directory rather than a regular file,
33 | * or for some other reason cannot be opened for reading.
34 | */
35 | public static String readFile(String path) throws IOException {
36 | String content = null;
37 | FileInputStream fileInputStream = null;
38 | try {
39 | fileInputStream = new FileInputStream(path);
40 | content = readStream(fileInputStream);
41 | } finally {
42 | if (fileInputStream != null) {
43 | fileInputStream.close();
44 | }
45 | }
46 | return content;
47 | }
48 |
49 | /**
50 | * Writes the object to the file at the specified path using serialization.
51 | * Only objects that implement the {@link Serializable} interface can be written.
52 | *
53 | * @param path file path.
54 | * @param obj object to write. Has to implement Serializable interface.
55 | * @throws IOException If an I/O error occurs while writing stream header.
56 | */
57 | public static void writeObjectToFile(String path, Object obj) throws IOException {
58 | FileOutputStream fileOutputStream = null;
59 | try {
60 | fileOutputStream = new FileOutputStream(path);
61 | ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
62 | out.writeObject(obj);
63 | out.close();
64 | fileOutputStream.close();
65 | }catch (FileNotFoundException e) {
66 | e.printStackTrace();
67 | }
68 | }
69 |
70 | /**
71 | * Reads the object from the file at the specified path using serialization.
72 | * Only objects written using {@link #writeObjectToFile}
73 | * or {@link ObjectOutputStream} can be parsed.
74 | *
75 | * @param path file path.
76 | * @return The object in the specified file.
77 | * @throws IOException If an I/O error occurs while reading stream header.
78 | */
79 | public static Object readObjectFromFile(String path) throws IOException {
80 | Object result = null;
81 | FileInputStream fileInputStream = null;
82 | try {
83 | fileInputStream = new FileInputStream(path);
84 | ObjectInputStream in = new ObjectInputStream(fileInputStream);
85 | result = in.readObject();
86 | in.close();
87 | fileInputStream.close();
88 | }catch (FileNotFoundException e) {
89 | e.printStackTrace();
90 | } catch (ClassNotFoundException e) {
91 | e.printStackTrace();
92 | }
93 | return result;
94 | }
95 |
96 | /**
97 | * Reads the content of the specified {@link InputStream} and returns it.
98 | *
99 | * @param stream InputStream to read from.
100 | * @return String representation of the data in the specified InputStream.
101 | * @throws IOException If an I/O error occurs.
102 | */
103 | public static String readStream(InputStream stream) throws IOException {
104 | BufferedReader in = new BufferedReader(new InputStreamReader(stream));
105 |
106 | StringBuilder stringBuilder = new StringBuilder();
107 | for (String line = in.readLine(); line != null; line = in.readLine()) {
108 | stringBuilder.append(line);
109 | stringBuilder.append("\n");
110 | }
111 |
112 | in.close();
113 | return stringBuilder.toString();
114 | }
115 |
116 | /**
117 | * Loads the image at the specified path and returns it as a {@link BufferedImage}.
118 | *
119 | * @param path image path.
120 | * @return Loaded image as a BufferedImage.
121 | * @throws IOException If an error occurs while loading the image.
122 | */
123 | public static BufferedImage loadImage(String path) throws IOException {
124 | BufferedImage image = null;
125 | FileInputStream fileInputStream = null;
126 | try {
127 | fileInputStream = new FileInputStream(path);
128 | BufferedImage tmp = ImageIO.read(fileInputStream);
129 | image = new BufferedImage(tmp.getWidth(), tmp.getHeight(), ColorUtils.COLOR_TYPE);
130 | image.createGraphics().drawImage(tmp, 0, 0, null);
131 | image.createGraphics().dispose();
132 | } finally {
133 | if (fileInputStream != null) {
134 | fileInputStream.close();
135 | }
136 | }
137 | image.flush();
138 | return image;
139 | }
140 |
141 | /**
142 | * Loads an image from the specified {@link InputStream} and returns it as a {@link BufferedImage}.
143 | *
144 | * @param stream InputStream to read from.
145 | * @return Loaded image as a BufferedImage.
146 | * @throws IOException If an error occurs while loading the image.
147 | */
148 | public static BufferedImage loadImage(InputStream stream) throws IOException {
149 | BufferedImage image = null;
150 | try {
151 | BufferedImage tmp = ImageIO.read(stream);
152 | image = new BufferedImage(tmp.getWidth(), tmp.getHeight(), ColorUtils.COLOR_TYPE);
153 | image.createGraphics().drawImage(tmp, 0, 0, null);
154 | image.createGraphics().dispose();
155 | } finally {}
156 | image.flush();
157 | return image;
158 | }
159 |
160 | /**
161 | * Loads the image at the specified path and returns it as a {@link BufferedImage}
162 | * with the specified size.
163 | *
164 | * @param path image path.
165 | * @param width destination width.
166 | * @param height destination height.
167 | * @return Loaded image as a BufferedImage.
168 | * @throws IOException If an error occurs while loading the image.
169 | */
170 | public static BufferedImage loadImage(String path, int width, int height) throws IOException {
171 | BufferedImage image = null;
172 | FileInputStream fileInputStream = null;
173 | try {
174 | fileInputStream = new FileInputStream(path);
175 | image = ImageIO.read(fileInputStream);
176 | } finally {
177 | if (fileInputStream != null) {
178 | fileInputStream.close();
179 | }
180 | }
181 | Image tmp = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
182 | BufferedImage resized = new BufferedImage(width, height, ColorUtils.COLOR_TYPE);
183 | resized.createGraphics().drawImage(tmp, 0, 0, null);
184 | resized.createGraphics().dispose();
185 | return resized;
186 | }
187 |
188 | /**
189 | * Loads an image from the specified {@link InputStream} and returns it as a {@link BufferedImage}
190 | * with the specified size.
191 | *
192 | * @param stream InputStream to read from.
193 | * @param width destination width.
194 | * @param height destination height.
195 | * @return Loaded image as a BufferedImage.
196 | * @throws IOException If an error occurs while loading the image.
197 | */
198 | public static BufferedImage loadImage(InputStream stream, int width, int height) throws IOException {
199 | BufferedImage image = new BufferedImage(1, 1, 1);
200 | try {
201 | image = ImageIO.read(stream);
202 | } finally { }
203 | Image tmp = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
204 | BufferedImage resized = new BufferedImage(width, height, ColorUtils.COLOR_TYPE);
205 | resized.createGraphics().drawImage(tmp, 0, 0, null);
206 | resized.createGraphics().dispose();
207 | return resized;
208 | }
209 |
210 | }
211 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/util/FixedPointUtils.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.util;
2 |
3 | /**
4 | * The FixedPointUtils class contains methods for generating fixed point numbers and
5 | * performing fixed point math operations such as power, square root, multiply,
6 | * divide, and some trigonometric functions.
7 | *
8 | * @author John Ferraz Salomon
9 | */
10 | public final class FixedPointUtils {
11 |
12 | /**
13 | * This is the bit representation of the default fixed point precision value.
14 | * That means that the 'point' is at the {@value #FP_BIT}th bit of the integer.
15 | */
16 | public static final byte FP_BIT = 15;
17 |
18 | /**
19 | * This is the integer representation of the default fixed point precision value.
20 | * It is the same as the fixed point '1'.
21 | */
22 | public static final int FP_ONE = 1 << FP_BIT;
23 |
24 | /**
25 | * It is the same as the fixed point '0.5'.
26 | */
27 | public static final int FP_HALF = FP_ONE >> 1;
28 |
29 | public static final int FP_DEGREE_RAD = toFixedPoint(Math.PI / 180.0f);
30 | public static final int FP_RAD_DEGREE = toFixedPoint(180.0f / Math.PI);
31 |
32 | private static final short[] sinLUT = new short[] {
33 | 0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, 5690, 6252, 6813, 7371, 7927, 8481,
34 | 9032, 9580, 10126, 10668, 11207, 11743, 12275, 12803, 13328, 13848, 14365, 14876, 15384,
35 | 15886, 16384, 16877, 17364, 17847, 18324, 18795, 19261, 19720, 20174, 20622, 21063, 21498,
36 | 21926, 22348, 22763, 23170, 23571, 23965, 24351, 24730, 25102, 25466, 25822, 26170, 26510,
37 | 26842, 27166, 27482, 27789, 28088, 28378, 28660, 28932, 29197, 29452, 29698, 29935, 30163,
38 | 30382, 30592, 30792, 30983, 31164, 31336, 31499, 31651, 31795, 31928, 32052, 32166, 32270,
39 | 32365, 32449, 32524, 32588, 32643, 32688, 32723, 32748, 32763, 32767
40 | };
41 |
42 | private FixedPointUtils() { }
43 |
44 | /**
45 | * Returns the fixed point representation of value.
46 | *
47 | * @param value
48 | * @return
49 | */
50 | public static int toFixedPoint(String value) {
51 | return toFixedPoint(Float.parseFloat(value));
52 | }
53 |
54 | /**
55 | * Returns the fixed point representation of value.
56 | *
57 | * @param value
58 | * @return
59 | */
60 | public static int toFixedPoint(double value) {
61 | return (int)Math.round(value * FP_ONE);
62 | }
63 |
64 | /**
65 | * Returns the floating point representation of value.
66 | *
67 | * @param value fixed point value.
68 | * @return
69 | */
70 | public static double toDouble(long value) {
71 | return (double)value / FP_ONE;
72 | }
73 |
74 | /**
75 | * Returns the degrees converted to radians.
76 | *
77 | * @param degrees
78 | * @return
79 | */
80 | public static int toRadians(int degrees) {
81 | return multiply(degrees, FP_DEGREE_RAD);
82 | }
83 |
84 |
85 | /**
86 | * Returns the radians converted to degrees.
87 | *
88 | * @param value
89 | * @return
90 | */
91 | public static int toDegrees(int radians) {
92 | return multiply(radians, FP_RAD_DEGREE);
93 | }
94 |
95 | /**
96 | * Returns the fixed point sine of the specified angle.
97 | *
98 | * @param degrees
99 | * @return The sine of the specified angle.
100 | */
101 | public static int sin(int degrees) {
102 | degrees >>= FP_BIT;
103 | degrees = ((degrees % 360) + 360) % 360;
104 | final int quadrant = degrees;
105 | degrees %= 90;
106 | if (quadrant < 90) {
107 | return sinLUT[degrees];
108 | }
109 | if (quadrant < 180) {
110 | return sinLUT[90 - degrees];
111 | }
112 | if (quadrant < 270) {
113 | return -sinLUT[degrees];
114 | }
115 | if (quadrant < 360) {
116 | return -sinLUT[90 - degrees];
117 | }
118 | return 0;
119 | }
120 |
121 | /**
122 | * Returns the fixed point cosine of the specified angle.
123 | *
124 | * @param degrees
125 | * @return The cosine of the specified angle.
126 | */
127 | public static int cos(int degrees) {
128 | degrees >>= FP_BIT;
129 | degrees = ((degrees % 360) + 360) % 360;
130 | final int quadrant = degrees;
131 | degrees %= 90;
132 | if (quadrant < 90) {
133 | return sinLUT[90 - degrees];
134 | }
135 | if (quadrant < 180) {
136 | return -sinLUT[degrees];
137 | }
138 | if (quadrant < 270) {
139 | return -sinLUT[90 - degrees];
140 | }
141 | if (quadrant < 360) {
142 | return sinLUT[degrees];
143 | }
144 | return 0;
145 | }
146 |
147 | /**
148 | * Returns the fixed point tangent of the specified angle.
149 | *
150 | * @param degrees
151 | * @return The tangent of the specified angle.
152 | */
153 | public static int tan(int degrees) {
154 | // no need to convert to long, the range of sine is 0 - 1
155 | return (sin(degrees) << FP_BIT) / cos(degrees);
156 | }
157 |
158 | /**
159 | * Returns the fixed point angle in degrees of the specified fixed point sine.
160 | * The angle is in the range -90 - 90.
161 | *
162 | * @param sine
163 | * @return The angle of the specified sine.
164 | */
165 | public static int asin(int sine) {
166 | final int absSine = Math.abs(sine);
167 | for (int i = 1; i < sinLUT.length; i++) {
168 | final int lut0 = sinLUT[i - 1];
169 | final int lut1 = sinLUT[i];
170 | if((absSine > lut0) && (absSine <= lut1)) {
171 | int degrees = i;
172 | // if the sine is more like lut1 than lut0 the angle is i
173 | // else it's i - 1
174 | final int half = (lut1 - lut0) >> 1;
175 | if(absSine < lut0 + half) {
176 | degrees--;
177 | }
178 | degrees <<= FP_BIT;
179 | if(sine > 0)
180 | return degrees;
181 | else
182 | return -degrees;
183 | }
184 | }
185 | return 0;
186 | }
187 |
188 | /**
189 | * Returns the fixed point angle in degrees of the specified fixed point cosine.
190 | * The angle is in the range 0 - 180.
191 | *
192 | * @param cosine
193 | * @return The angle of the specified cosine.
194 | */
195 | public static int acos(int cosine) {
196 | final int absCosine = Math.abs(cosine);
197 | for (int i = 1; i < sinLUT.length; i++) {
198 | final int lut0 = sinLUT[i - 1];
199 | final int lut1 = sinLUT[i];
200 | if((absCosine >= lut0) && (absCosine <= lut1)) {
201 | int degrees = i;
202 | final int half = (lut1 - lut0) >> 1;
203 | if(absCosine < lut0 + half) {
204 | degrees--;
205 | }
206 | if(cosine < 0)
207 | return (degrees + 90) << FP_BIT;
208 | else
209 | return (90 - degrees) << FP_BIT;
210 | }
211 | }
212 | return 0;
213 | }
214 |
215 | /**
216 | * Returns the given value in the range min-max.
217 | *
218 | * @param value value to wrap.
219 | * @param min
220 | * @param max
221 | * @return
222 | */
223 | public static int normalize(int value, int min, int max) {
224 | int range = max - min;
225 | return (min + ((((value - min) % range) + range) % range));
226 | }
227 |
228 | /**
229 | * Returns the given value from min to max. if value < min return min. if value
230 | * > max return max.
231 | *
232 | * @param value
233 | * @param min
234 | * @param max
235 | * @return
236 | */
237 | public static int clamp(int value, int min, int max) {
238 | return Math.min(max, Math.max(value, min));
239 | }
240 |
241 | /**
242 | * Returns a pseudo generated random value.
243 | *
244 | * @return
245 | */
246 | public static int random(int seed) {
247 | seed += seed + (seed & seed);
248 | return seed;
249 | }
250 |
251 | /**
252 | * Returns a pseudo generated random value in the range min-max.
253 | *
254 | * @param min lowest random value.
255 | * @param max highest random value.
256 | * @return
257 | */
258 | public static int random(int seed, int min, int max) {
259 | return normalize(random(seed), min, max);
260 | }
261 |
262 | /**
263 | * Returns the square root of the given number. If number < 0 the method returns
264 | * 0.
265 | *
266 | * @param number fixed point number.
267 | * @return fixed point result.
268 | */
269 | public static int sqrt(long number) {
270 | // integral part
271 | int num = (int)(number >> FP_BIT);
272 | if(num < 0) {
273 | return 0;
274 | }
275 | int c = 1 << 15;
276 | int g = c;
277 | for (int i = 0; i < 16; i++) {
278 | if (g * g > num) {
279 | g ^= c;
280 | }
281 | c >>= 1;
282 | if (c == 0) {
283 | break;
284 | }
285 | g |= c;
286 | }
287 | long result = (g << FP_BIT);
288 | // fractional part
289 | final short increment = FP_ONE >> 7;
290 | for (; (result * result + FP_HALF) >> FP_BIT < number; result += increment);
291 | result -= increment;
292 | return (int)result;
293 | }
294 |
295 | /**
296 | * Returns the power of the given number.
297 | *
298 | * @param base fixed point number.
299 | * @param exp fixed point number.
300 | * @return fixed point result.
301 | */
302 | public static int pow(long base, int exp) {
303 | exp >>= FP_BIT;
304 | long result = FP_ONE;
305 | while (exp != 0) {
306 | if ((exp & 1) == 1) {
307 | result = (result * base + FP_HALF) >> FP_BIT;
308 | }
309 | exp >>= 1;
310 | base = (base * base + FP_HALF) >> FP_BIT;
311 | }
312 | return (int) result;
313 | }
314 |
315 | /**
316 | * Returns the product of the multiplication of value1 and value2.
317 | *
318 | * @param value1 fixed point number.
319 | * @param value2 fixed point number.
320 | * @return fixed point result.
321 | */
322 | public static int multiply(long value1, long value2) {
323 | long result = value1 * value2 + FP_HALF;
324 | return (int) (result >> FP_BIT);
325 | }
326 |
327 | /**
328 | * Returns the quotient of the division.
329 | *
330 | * @param dividend fixed point number.
331 | * @param divisor not fixed point number.
332 | * @return fixed point result.
333 | */
334 | public static int divide(long dividend, long divisor) {
335 | long result = dividend << FP_BIT;
336 | result /= divisor;
337 | return (int) result;
338 | }
339 |
340 | /**
341 | * Returns a string containing the data of the given fixed point value.
342 | *
343 | * @param vector fixed point number.
344 | * @return
345 | */
346 | public static String toString(int value) {
347 | float floatValue = (float)value / FP_ONE;
348 | return "" + floatValue;
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/src/main/java/com/johnsproject/jgameengine/util/TransformationUtils.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.util;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.*;
4 | import static com.johnsproject.jgameengine.util.MatrixUtils.*;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.*;
6 |
7 | import com.johnsproject.jgameengine.model.Frustum;
8 |
9 | public final class TransformationUtils {
10 |
11 | private TransformationUtils() { }
12 |
13 | public static int[][] orthographicMatrix(int[][] matrix, Frustum frustum) {
14 | final int top = frustum.getRenderTargetTop();
15 | final int bottom = frustum.getRenderTargetBottom();
16 | final int near = frustum.getNear();
17 | final int far = frustum.getFar();
18 | final int scaleFactor = FixedPointUtils.multiply(frustum.getFocalLength(), bottom - top + 1);
19 | final int[][] projectionMatrix = MatrixUtils.copy(matrix, MatrixUtils.MATRIX_IDENTITY);
20 | projectionMatrix[0][0] = scaleFactor >> 5;
21 | projectionMatrix[1][1] = -scaleFactor >> 5;
22 | projectionMatrix[2][2] = -FixedPointUtils.divide(FP_ONE, far);
23 | projectionMatrix[3][2] = -FixedPointUtils.divide(near, far);
24 | projectionMatrix[3][3] = FP_ONE;
25 | return projectionMatrix;
26 | }
27 |
28 | public static int[][] perspectiveMatrix(int[][] matrix, Frustum frustum) {
29 | final int top = frustum.getRenderTargetTop();
30 | final int bottom = frustum.getRenderTargetBottom();
31 | final int near = frustum.getNear();
32 | final int far = frustum.getFar();
33 | final int farNear = far - near;
34 | final int scaleFactor = FixedPointUtils.multiply(frustum.getFocalLength(), bottom - top + 1);
35 | final int[][] projectionMatrix = MatrixUtils.copy(matrix, MatrixUtils.MATRIX_IDENTITY);
36 | projectionMatrix[0][0] = scaleFactor;
37 | projectionMatrix[1][1] = -scaleFactor;
38 | projectionMatrix[2][2] = -FixedPointUtils.divide(far, farNear);
39 | projectionMatrix[3][2] = -FixedPointUtils.divide(FixedPointUtils.multiply(near, far), farNear);
40 | projectionMatrix[2][3] = -FP_ONE;
41 | projectionMatrix[3][3] = 0;
42 | return projectionMatrix;
43 | }
44 |
45 | public static int[] screenportVector(int[] location, Frustum frustum) {
46 | final int left = frustum.getRenderTargetLeft();
47 | final int right = frustum.getRenderTargetRight();
48 | final int top = frustum.getRenderTargetTop();
49 | final int bottom = frustum.getRenderTargetBottom();
50 | final int halfWidth = left + ((right - left) >> 1);
51 | final int halfHeight = top + ((bottom - top) >> 1);
52 | location[VECTOR_X] += halfWidth;
53 | location[VECTOR_Y] += halfHeight;
54 | return location;
55 | }
56 |
57 | public static int[] translate(int[] vector, int[] direction) {
58 | return translate(vector, direction[VECTOR_X], direction[VECTOR_Y], direction[VECTOR_Z]);
59 | }
60 |
61 | public static int[] translate(int[] vector, int x, int y, int z) {
62 | vector[VECTOR_X] = vector[VECTOR_X] + x;
63 | vector[VECTOR_Y] = vector[VECTOR_Y] + y;
64 | vector[VECTOR_Z] = vector[VECTOR_Z] + z;
65 | return vector;
66 | }
67 |
68 | public static int[] scale(int[] vector, int factor) {
69 | vector[VECTOR_X] = FixedPointUtils.multiply(vector[VECTOR_X], factor);
70 | vector[VECTOR_Y] = FixedPointUtils.multiply(vector[VECTOR_Y], factor);
71 | vector[VECTOR_Z] = FixedPointUtils.multiply(vector[VECTOR_Z], factor);
72 | return vector;
73 | }
74 |
75 | /**
76 | * Sets result equals the vector reflected across reflectionVector.
77 | *
78 | * @param vector
79 | * @param reflectionVector
80 | * @param result
81 | */
82 | public static int[] reflect(int[] vector, int[] reflectionVector) {
83 | final int x = reflectionVector[VECTOR_X];
84 | final int y = reflectionVector[VECTOR_Y];
85 | final int z = reflectionVector[VECTOR_Z];
86 | final int dot = (int)(2 * VectorUtils.dotProduct(vector, reflectionVector));
87 | VectorUtils.multiply(reflectionVector, dot);
88 | VectorUtils.subtract(vector, reflectionVector);
89 | reflectionVector[VECTOR_X] = x;
90 | reflectionVector[VECTOR_Y] = y;
91 | reflectionVector[VECTOR_Z] = z;
92 | return vector;
93 | }
94 |
95 | /**
96 | * Sets result equals the vector rotated around (0, 0, 0) at x axis by the given angle.
97 | *
98 | * @param vector
99 | * @param angle
100 | * @param result
101 | * @return
102 | */
103 | public static int[] rotateX(int[] vector, int angle) {
104 | final int sin = FixedPointUtils.sin(angle);
105 | final int cos = FixedPointUtils.cos(angle);
106 | final int x = vector[VECTOR_X];
107 | final int y = vector[VECTOR_Y];
108 | final int z = vector[VECTOR_Z];
109 | vector[VECTOR_X] = x;
110 | vector[VECTOR_Y] = FixedPointUtils.multiply(y, cos);
111 | vector[VECTOR_Y] -= FixedPointUtils.multiply(z, sin);
112 | vector[VECTOR_Z] = FixedPointUtils.multiply(z, cos);
113 | vector[VECTOR_Z] += FixedPointUtils.multiply(y, sin);
114 | return vector;
115 | }
116 |
117 | /**
118 | * Sets result equals the vector rotated around (0, 0, 0) at y axis by the given angle.
119 | *
120 | * @param vector
121 | * @param angle
122 | * @param result
123 | * @return
124 | */
125 | public static int[] rotateY(int[] vector, int angle) {
126 | final int sin = FixedPointUtils.sin(angle);
127 | final int cos = FixedPointUtils.cos(angle);
128 | final int x = vector[VECTOR_X];
129 | final int y = vector[VECTOR_Y];
130 | final int z = vector[VECTOR_Z];
131 | vector[VECTOR_X] = FixedPointUtils.multiply(x, cos);
132 | vector[VECTOR_X] += FixedPointUtils.multiply(z, sin);
133 | vector[VECTOR_Y] = y;
134 | vector[VECTOR_Z] = FixedPointUtils.multiply(z, cos);
135 | vector[VECTOR_Z] -= FixedPointUtils.multiply(x, sin);
136 | return vector;
137 | }
138 |
139 | /**
140 | * Sets result equals the vector rotated around (0, 0, 0) at z axis by the given angle.
141 | *
142 | * @param vector
143 | * @param angle
144 | * @param result
145 | * @return
146 | */
147 | public static int[] rotateZ(int[] vector, int angle) {
148 | final int sin = FixedPointUtils.sin(angle);
149 | final int cos = FixedPointUtils.cos(angle);
150 | final int x = vector[VECTOR_X];
151 | final int y = vector[VECTOR_Y];
152 | final int z = vector[VECTOR_Z];
153 | vector[VECTOR_X] = FixedPointUtils.multiply(x, cos);
154 | vector[VECTOR_X] += FixedPointUtils.multiply(y, sin);
155 | vector[VECTOR_Y] = FixedPointUtils.multiply(y, cos);
156 | vector[VECTOR_Y] -= FixedPointUtils.multiply(x, sin);
157 | vector[VECTOR_Z] = z;
158 | return vector;
159 | }
160 |
161 | /**
162 | * Sets result equals the translated matrix.
163 | *
164 | * @param matrix
165 | * @param x
166 | * @param y
167 | * @param z
168 | * @param result
169 | */
170 | public static int[][] translationMatrix(int[][] matrix, int[] vector) {
171 | return translationMatrix(matrix, vector[VECTOR_X], vector[VECTOR_Y], vector[VECTOR_Z]);
172 | }
173 |
174 | /**
175 | * Sets result equals the translated matrix.
176 | *
177 | * @param matrix
178 | * @param x
179 | * @param y
180 | * @param z
181 | * @param result
182 | */
183 | public static int[][] translationMatrix(int[][] matrix, int x, int y , int z) {
184 | MatrixUtils.copy(matrix, MATRIX_IDENTITY);
185 | matrix[3][0] = x;
186 | matrix[3][1] = y;
187 | matrix[3][2] = z;
188 | return matrix;
189 | }
190 |
191 | public static int[][] scaleMatrix(int[][] matrix, int[] vector) {
192 | return scaleMatrix(matrix, vector[VECTOR_X], vector[VECTOR_Y], vector[VECTOR_Z]);
193 | }
194 |
195 | public static int[][] scaleMatrix(int[][] matrix, int x, int y, int z) {
196 | MatrixUtils.copy(matrix, MATRIX_IDENTITY);
197 | matrix[0][0] = x;
198 | matrix[1][1] = y;
199 | matrix[2][2] = z;
200 | return matrix;
201 | }
202 |
203 | public static int[][] xRotationMatrix(int[][] matrix, int angle) {
204 | MatrixUtils.copy(matrix, MATRIX_IDENTITY);
205 | final int cos = FixedPointUtils.cos(angle);
206 | final int sin = FixedPointUtils.sin(angle);
207 | matrix[1][1] = cos;
208 | matrix[1][2] = sin;
209 | matrix[2][1] = -sin;
210 | matrix[2][2] = cos;
211 | return matrix;
212 | }
213 |
214 | public static int[][] yRotationMatrix(int[][] matrix, int angle) {
215 | MatrixUtils.copy(matrix, MATRIX_IDENTITY);
216 | final int cos = FixedPointUtils.cos(angle);
217 | final int sin = FixedPointUtils.sin(angle);
218 | matrix[0][0] = cos;
219 | matrix[0][2] = -sin;
220 | matrix[2][0] = sin;
221 | matrix[2][2] = cos;
222 | return matrix;
223 | }
224 |
225 | public static int[][] zRotationMatrix(int[][] matrix, int angle) {
226 | MatrixUtils.copy(matrix, MATRIX_IDENTITY);
227 | final int cos = FixedPointUtils.cos(angle);
228 | final int sin = FixedPointUtils.sin(angle);
229 | matrix[0][0] = cos;
230 | matrix[0][1] = sin;
231 | matrix[1][0] = -sin;
232 | matrix[1][1] = cos;
233 | return matrix;
234 | }
235 | }
--------------------------------------------------------------------------------
/src/test/java/com/johnsproject/jgameengine/SpaceshipGame.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine;
2 |
3 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_BIT;
4 | import static com.johnsproject.jgameengine.util.FixedPointUtils.FP_HALF;
5 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_X;
6 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Y;
7 | import static com.johnsproject.jgameengine.util.VectorUtils.VECTOR_Z;
8 |
9 | import java.awt.event.KeyEvent;
10 | import java.io.IOException;
11 |
12 | import com.johnsproject.jgameengine.event.EngineEvent;
13 | import com.johnsproject.jgameengine.event.EngineListener;
14 | import com.johnsproject.jgameengine.io.OBJImporter;
15 | import com.johnsproject.jgameengine.model.Camera;
16 | import com.johnsproject.jgameengine.model.FrameBuffer;
17 | import com.johnsproject.jgameengine.model.Light;
18 | import com.johnsproject.jgameengine.model.Mesh;
19 | import com.johnsproject.jgameengine.model.Model;
20 | import com.johnsproject.jgameengine.model.Scene;
21 | import com.johnsproject.jgameengine.model.Texture;
22 | import com.johnsproject.jgameengine.model.Transform;
23 | import com.johnsproject.jgameengine.shading.BasicThreadedShader;
24 | import com.johnsproject.jgameengine.shading.DirectionalLightShadowShader;
25 | import com.johnsproject.jgameengine.shading.FlatShader;
26 | import com.johnsproject.jgameengine.shading.GouraudShader;
27 | import com.johnsproject.jgameengine.shading.PhongShader;
28 | import com.johnsproject.jgameengine.util.FileUtils;
29 | import com.johnsproject.jgameengine.util.FixedPointUtils;
30 | import com.johnsproject.jgameengine.util.VectorUtils;
31 |
32 | @SuppressWarnings("unused")
33 | public class SpaceshipGame implements EngineListener {
34 |
35 | private static final int WINDOW_WIDTH = 1920;
36 | private static final int WINDOW_HEIGHT = 1080;
37 |
38 | private final FrameBuffer frameBuffer;
39 | private final EngineWindow window;
40 | private final GraphicsEngine graphicsEngine;
41 | private final EngineStatistics engineStats;
42 | private final InputEngine inputEngine = new InputEngine();
43 | private final Scene scene = new Scene();
44 |
45 | private Transform cameraTransform;
46 | private Transform lightTransform;
47 | private Transform spaceshipTransform;
48 |
49 | private Model spaceshipFireModel;
50 | private Transform spaceshipFireTransform;
51 |
52 | private int spaceshipFireScale = 2000;
53 |
54 | public static void main(String[] args) {
55 | int width = WINDOW_WIDTH;
56 | int height = WINDOW_HEIGHT;
57 | int scaling = 100;
58 | for (int i = 0; i < args.length; i++) {
59 | final String setting = args[i];
60 | if(setting.contains("window")) {
61 | final String[] resolution = setting.replace("window", "").split("x");
62 | width = Integer.parseInt(resolution[0]);
63 | height = Integer.parseInt(resolution[1]);
64 | }
65 | else if(setting.contains("render")) {
66 | scaling = Integer.parseInt(setting.replace("render", ""));
67 | }
68 | }
69 | new SpaceshipGame(width, height, scaling);
70 | }
71 |
72 | public SpaceshipGame(int width, int height, int scaling) {
73 | frameBuffer = new FrameBuffer((width * scaling) / 100, (height * scaling) / 100);
74 | window = new EngineWindow(frameBuffer);
75 | graphicsEngine = new GraphicsEngine(frameBuffer);
76 | engineStats = new EngineStatistics(window);
77 | window.setSize(width, height);
78 |
79 | Engine.getInstance().setScene(scene);
80 | Engine.getInstance().addEngineListener(this);
81 | Engine.getInstance().addEngineListener(graphicsEngine);
82 | Engine.getInstance().addEngineListener(inputEngine);
83 | Engine.getInstance().addEngineListener(window);
84 | Engine.getInstance().addEngineListener(engineStats);
85 | Engine.getInstance().start();
86 | }
87 |
88 | public void initialize(EngineEvent e) {
89 | window.setTitle("Spaceship Game");
90 |
91 | // remove unused shaders to save performance
92 | graphicsEngine.getShaders().clear();
93 | graphicsEngine.addShader(new DirectionalLightShadowShader());
94 | graphicsEngine.addShader(new GouraudShader());
95 | graphicsEngine.setDefaultShader(graphicsEngine.getShader(1));
96 | try {
97 | final ClassLoader classLoader = this.getClass().getClassLoader();
98 | loadTerrain(classLoader);
99 | loadSpaceshipFire(classLoader);
100 | loadSpaceship(classLoader);
101 | createCamera();
102 | createLight();
103 | } catch (IOException ioE) {
104 | ioE.printStackTrace();
105 | }
106 | moveCameraToSpaceship();
107 | }
108 |
109 | private void loadTerrain(ClassLoader classLoader) throws IOException {
110 | final Model terrainModel = OBJImporter.parseResource(classLoader, "SpaceshipGame/Terrain.obj");
111 | scene.addModel(terrainModel);
112 |
113 | terrainModel.getTransform().setScale(3 << FP_BIT, 3 << FP_BIT, 3 << FP_BIT);
114 |
115 | final Texture texture = new Texture(FileUtils.loadImage(this.getClass().getResourceAsStream("/JohnsProjectLogo.png")));
116 | terrainModel.getMesh().getMaterial(0).setTexture(texture);
117 | }
118 |
119 | private void loadSpaceshipFire(ClassLoader classLoader) throws IOException {
120 | spaceshipFireModel = OBJImporter.parseResource(classLoader, "SpaceshipGame/SpaceshipFire.obj");
121 | scene.addModel(spaceshipFireModel);
122 |
123 | spaceshipFireTransform = spaceshipFireModel.getTransform();
124 | spaceshipFireTransform.setScale(FP_HALF, FP_HALF, FP_HALF);
125 |
126 | // the basic shader has no illumination so it will have a glow like effect
127 | final BasicThreadedShader basicShader = new BasicThreadedShader();
128 | graphicsEngine.addShader(basicShader);
129 | spaceshipFireModel.getMesh().getMaterial(0).setShader(basicShader);
130 | }
131 |
132 | private void loadSpaceship(ClassLoader classLoader) throws IOException {
133 | final Model spaceshipModel = OBJImporter.parseResource(classLoader, "SpaceshipGame/Spaceship.obj");
134 | scene.addModel(spaceshipModel);
135 |
136 | spaceshipTransform = spaceshipModel.getTransform();
137 | spaceshipTransform.worldTranslate(0, 5 << FP_BIT, 0);
138 | spaceshipTransform.setScale(FP_HALF, FP_HALF, FP_HALF);
139 | }
140 |
141 | private void createCamera() {
142 | cameraTransform = new Transform();
143 | cameraTransform.worldRotate(-45 << FP_BIT, 0, 0);
144 | final Camera camera = new Camera("Camera", cameraTransform);
145 | scene.addCamera(camera);
146 | }
147 |
148 | private void createLight() {
149 | lightTransform = new Transform();
150 | lightTransform.worldRotate(-60 << FP_BIT, 25 << FP_BIT, 0);
151 | final Light directionalLight = new Light("DirectionalLight", lightTransform);
152 | scene.addLight(directionalLight);
153 | }
154 |
155 | public void fixedUpdate(EngineEvent e) {
156 | spaceshipFireScale = -spaceshipFireScale;
157 | spaceshipFireTransform.worldScale(spaceshipFireScale, spaceshipFireScale, spaceshipFireScale);
158 | }
159 |
160 | public void dynamicUpdate(EngineEvent e) {
161 | final boolean isAnyMoveKeyPressed = inputEngine.isKeyPressed(KeyEvent.VK_W) || inputEngine.isKeyPressed(KeyEvent.VK_S)
162 | || inputEngine.isKeyPressed(KeyEvent.VK_A) || inputEngine.isKeyPressed(KeyEvent.VK_D);
163 |
164 | if(isAnyMoveKeyPressed) {
165 | moveSpaceshipWithKeyboard(e.getDeltaTime());
166 | moveFireToSpaceship();
167 | moveCameraToSpaceship();
168 | moveLightToCamera();
169 | }
170 | spaceshipFireModel.setActive(isAnyMoveKeyPressed);
171 | }
172 |
173 | private void moveSpaceshipWithKeyboard(int deltaTime) {
174 | if(inputEngine.isKeyPressed(KeyEvent.VK_W) && inputEngine.isKeyPressed(KeyEvent.VK_A)) {
175 | spaceshipTransform.setRotation(0, 45 << FP_BIT, 0);
176 | }
177 | else if(inputEngine.isKeyPressed(KeyEvent.VK_W) && inputEngine.isKeyPressed(KeyEvent.VK_D)) {
178 | spaceshipTransform.setRotation(0, -45 << FP_BIT, 0);
179 | }
180 | else if(inputEngine.isKeyPressed(KeyEvent.VK_S) && inputEngine.isKeyPressed(KeyEvent.VK_A)) {
181 | spaceshipTransform.setRotation(0, 135 << FP_BIT, 0);
182 | }
183 | else if(inputEngine.isKeyPressed(KeyEvent.VK_S) && inputEngine.isKeyPressed(KeyEvent.VK_D)) {
184 | spaceshipTransform.setRotation(0, -135 << FP_BIT, 0);
185 | }
186 | else if(inputEngine.isKeyPressed(KeyEvent.VK_W)) {
187 | spaceshipTransform.setRotation(0, 0, 0);
188 | }
189 | else if(inputEngine.isKeyPressed(KeyEvent.VK_S)) {
190 | spaceshipTransform.setRotation(0, 180 << FP_BIT, 0);
191 | }
192 | else if(inputEngine.isKeyPressed(KeyEvent.VK_A)) {
193 | spaceshipTransform.setRotation(0, 90 << FP_BIT, 0);
194 | }
195 | else if(inputEngine.isKeyPressed(KeyEvent.VK_D)) {
196 | spaceshipTransform.setRotation(0, -90 << FP_BIT, 0);
197 | }
198 | spaceshipTransform.localTranslate(0, 0, -FixedPointUtils.multiply(FP_HALF, deltaTime));
199 | }
200 |
201 |
202 | private void moveFireToSpaceship() {
203 | spaceshipFireTransform.setLocation(0, 0, 4 << FP_BIT);
204 | VectorUtils.multiply(spaceshipFireTransform.getLocation(), spaceshipTransform.getSpaceExitMatrix());
205 |
206 | final int[] spaceshipRotation = spaceshipTransform.getRotation();
207 | spaceshipFireTransform.setRotation(spaceshipRotation[VECTOR_X], spaceshipRotation[VECTOR_Y], spaceshipRotation[VECTOR_Z]);
208 | }
209 |
210 | private void moveCameraToSpaceship() {
211 | final int[] spaceshipLocation = spaceshipTransform.getLocation();
212 | cameraTransform.setLocation(spaceshipLocation[VECTOR_X], spaceshipLocation[VECTOR_Y], spaceshipLocation[VECTOR_Z]);
213 | cameraTransform.worldTranslate(0, 20 << FP_BIT, 15 << FP_BIT);
214 | }
215 |
216 | private void moveLightToCamera() {
217 | final int[] cameraLocation = cameraTransform.getLocation();
218 | lightTransform.setLocation(cameraLocation[VECTOR_X], cameraLocation[VECTOR_Y], cameraLocation[VECTOR_Z]);
219 | }
220 |
221 | public int getLayer() {
222 | return DEFAULT_LAYER;
223 | }
224 | }
--------------------------------------------------------------------------------
/src/test/java/com/johnsproject/jgameengine/io/OBJImporterTest.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.io;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNotNull;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.util.List;
9 |
10 | import org.junit.Test;
11 |
12 | import com.johnsproject.jgameengine.model.Face;
13 | import com.johnsproject.jgameengine.model.Material;
14 | import com.johnsproject.jgameengine.model.Mesh;
15 | import com.johnsproject.jgameengine.model.Vertex;
16 | import com.johnsproject.jgameengine.util.ColorUtils;
17 | import com.johnsproject.jgameengine.util.FileUtils;
18 | import com.johnsproject.jgameengine.util.FixedPointUtils;
19 | import com.johnsproject.jgameengine.util.VectorUtils;
20 |
21 | public class OBJImporterTest {
22 |
23 | @Test
24 | public void parseMaterialsTest() throws Exception {
25 | final String data = readResource("TestOBJ.mtl");
26 | final List materials = OBJImporter.parseMaterials(data);
27 | assert(materials.size() == 2);
28 |
29 | Material material = materials.get(0);
30 | assert(material.getIndex() == 0);
31 | assertEquals(material.getName(), "Material");
32 |
33 | int shininess = FixedPointUtils.toFixedPoint(17.647059);
34 | assert(material.getShininess() == shininess);
35 |
36 | int r = Math.round(0.8f * ColorUtils.COLOR_ONE);
37 | int g = Math.round(0.8f * ColorUtils.COLOR_ONE);
38 | int b = Math.round(0.8f * ColorUtils.COLOR_ONE);
39 | int color = ColorUtils.toColor(r, g, b);
40 | assert(material.getDiffuseColor() == color);
41 |
42 | r = Math.round(0.5f * ColorUtils.COLOR_ONE);
43 | g = Math.round(0.5f * ColorUtils.COLOR_ONE);
44 | b = Math.round(0.5f * ColorUtils.COLOR_ONE);
45 | color = ColorUtils.toColor(r, g, b);
46 | assert(material.getSpecularColor() == color);
47 |
48 | material = materials.get(1);
49 | assert(material.getIndex() == 1);
50 | assertEquals(material.getName(), "Material.001");
51 |
52 | shininess = FixedPointUtils.toFixedPoint(96.078431);
53 | assert(material.getShininess() == shininess);
54 |
55 | r = Math.round(0.64f * ColorUtils.COLOR_ONE);
56 | g = Math.round(0.64f * ColorUtils.COLOR_ONE);
57 | b = Math.round(0.64f * ColorUtils.COLOR_ONE);
58 | color = ColorUtils.toColor(r, g, b);
59 | assert(material.getDiffuseColor() == color);
60 |
61 | r = Math.round(0.5f * ColorUtils.COLOR_ONE);
62 | g = Math.round(0.5f * ColorUtils.COLOR_ONE);
63 | b = Math.round(0.5f * ColorUtils.COLOR_ONE);
64 | color = ColorUtils.toColor(r, g, b);
65 | assert(material.getSpecularColor() == color);
66 | }
67 |
68 | @Test
69 | public void parseVerticesTest() throws Exception {
70 | final String data = readResource("TestOBJ.obj");
71 | final List vertices = OBJImporter.parseVertices(data);
72 | assert(vertices.size() == 16);
73 |
74 | OBJImporter.VertexData vertex = vertices.get(0);
75 | int x = FixedPointUtils.toFixedPoint(-4.198291);
76 | int y = FixedPointUtils.toFixedPoint(2.563459);
77 | int z = FixedPointUtils.toFixedPoint(-1.020053);
78 | assert(vertex.location[VectorUtils.VECTOR_X] == x);
79 | assert(vertex.location[VectorUtils.VECTOR_Y] == y);
80 | assert(vertex.location[VectorUtils.VECTOR_Z] == z);
81 |
82 | vertex = vertices.get(3);
83 | x = FixedPointUtils.toFixedPoint(-6.198290);
84 | y = FixedPointUtils.toFixedPoint(2.563459);
85 | z = FixedPointUtils.toFixedPoint(-1.020054);
86 | assert(vertex.location[VectorUtils.VECTOR_X] == x);
87 | assert(vertex.location[VectorUtils.VECTOR_Y] == y);
88 | assert(vertex.location[VectorUtils.VECTOR_Z] == z);
89 |
90 | vertex = vertices.get(15);
91 | x = FixedPointUtils.toFixedPoint(-1);
92 | y = FixedPointUtils.toFixedPoint(1);
93 | z = FixedPointUtils.toFixedPoint(-1);
94 | assert(vertex.location[VectorUtils.VECTOR_X] == x);
95 | assert(vertex.location[VectorUtils.VECTOR_Y] == y);
96 | assert(vertex.location[VectorUtils.VECTOR_Z] == z);
97 | }
98 |
99 |
100 | @Test
101 | public void parseFaceNormalsTest() throws Exception {
102 | final String data = readResource("TestOBJ.obj");
103 | final List faceNormals = OBJImporter.parseFaceNormals(data);
104 | assert(faceNormals.size() == 12);
105 |
106 | OBJImporter.FaceNormal faceNormal = faceNormals.get(0);
107 | int x = FixedPointUtils.toFixedPoint(0);
108 | int y = FixedPointUtils.toFixedPoint(-1);
109 | int z = FixedPointUtils.toFixedPoint(0);
110 | assert(faceNormal.normal[VectorUtils.VECTOR_X] == x);
111 | assert(faceNormal.normal[VectorUtils.VECTOR_Y] == y);
112 | assert(faceNormal.normal[VectorUtils.VECTOR_Z] == z);
113 |
114 | faceNormal = faceNormals.get(11);
115 | x = FixedPointUtils.toFixedPoint(0);
116 | y = FixedPointUtils.toFixedPoint(0);
117 | z = FixedPointUtils.toFixedPoint(-1);
118 | assert(faceNormal.normal[VectorUtils.VECTOR_X] == x);
119 | assert(faceNormal.normal[VectorUtils.VECTOR_Y] == y);
120 | assert(faceNormal.normal[VectorUtils.VECTOR_Z] == z);
121 | }
122 |
123 | @Test
124 | public void parseUVsTest() throws Exception {
125 | final String data = readResource("TestOBJ.obj");
126 | final List faceUVs = OBJImporter.parseFaceUVs(data);
127 |
128 | OBJImporter.FaceUV faceUV = faceUVs.get(0);
129 | int x = FixedPointUtils.toFixedPoint(1);
130 | int y = FixedPointUtils.toFixedPoint(0);
131 | assert(faceUV.uv[VectorUtils.VECTOR_X] == x);
132 | assert(faceUV.uv[VectorUtils.VECTOR_Y] == y);
133 |
134 | faceUV = faceUVs.get(39);
135 | x = FixedPointUtils.toFixedPoint(1);
136 | y = FixedPointUtils.toFixedPoint(1);
137 | assert(faceUV.uv[VectorUtils.VECTOR_X] == x);
138 | assert(faceUV.uv[VectorUtils.VECTOR_Y] == y);
139 | }
140 |
141 | @Test
142 | public void parseFacesTest() throws Exception {
143 | final String materialData = readResource("TestOBJ.mtl");
144 | final String objectData = readResource("TestOBJ.obj");
145 | final List materials = OBJImporter.parseMaterials(materialData);
146 | final List faces = OBJImporter.parseFaces(objectData, materials);
147 | assert(faces.size() == 24);
148 |
149 | OBJImporter.FaceData face = faces.get(0);
150 | assert(face.index == 0);
151 | assert(face.vertexIndices[0] == 1);
152 | assert(face.vertexIndices[1] == 3);
153 | assert(face.vertexIndices[2] == 0);
154 | assert(face.normalIndices[0] == 0);
155 | assert(face.normalIndices[1] == 0);
156 | assert(face.normalIndices[2] == 0);
157 | assert(face.uvIndices[0] == 0);
158 | assert(face.uvIndices[1] == 1);
159 | assert(face.uvIndices[2] == 2);
160 | assert(face.material == materials.get(1));
161 |
162 | face = faces.get(5);
163 | assert(face.index == 5);
164 | assert(face.vertexIndices[0] == 0);
165 | assert(face.vertexIndices[1] == 7);
166 | assert(face.vertexIndices[2] == 4);
167 | assert(face.normalIndices[0] == 5);
168 | assert(face.normalIndices[1] == 5);
169 | assert(face.normalIndices[2] == 5);
170 | assert(face.uvIndices[0] == 13);
171 | assert(face.uvIndices[1] == 14);
172 | assert(face.uvIndices[2] == 5);
173 | assert(face.material == materials.get(1));
174 |
175 | face = faces.get(23);
176 | assert(face.index == 23);
177 | assert(face.vertexIndices[0] == 8);
178 | assert(face.vertexIndices[1] == 11);
179 | assert(face.vertexIndices[2] == 15);
180 | assert(face.normalIndices[0] == 11);
181 | assert(face.normalIndices[1] == 11);
182 | assert(face.normalIndices[2] == 11);
183 | assert(face.uvIndices[0] == 33);
184 | assert(face.uvIndices[1] == 39);
185 | assert(face.uvIndices[2] == 34);
186 | assert(face.material == materials.get(0));
187 | }
188 |
189 | @Test
190 | public void parseMeshTest() throws Exception {
191 | final String materialData = readResource("TestOBJ.mtl");
192 | final String objectData = readResource("TestOBJ.obj");
193 | final Mesh mesh = OBJImporter.parseMesh(objectData, materialData);
194 |
195 | assert(mesh.getMaterials().length == 2);
196 | assert(mesh.getVertices().length == 16);
197 | assert(mesh.getFaces().length == 24);
198 |
199 | assertNotNull(mesh.getMaterial("Material"));
200 | assertNotNull(mesh.getMaterial("Material.001"));
201 |
202 | Vertex vertex = mesh.getVertex(0);
203 | assert(vertex.getIndex() == 0);
204 | int x = FixedPointUtils.toFixedPoint(-4.198291);
205 | int y = FixedPointUtils.toFixedPoint(2.563459);
206 | int z = FixedPointUtils.toFixedPoint(-1.020053);
207 | assert(vertex.getLocalLocation()[VectorUtils.VECTOR_X] == x);
208 | assert(vertex.getLocalLocation()[VectorUtils.VECTOR_Y] == y);
209 | assert(vertex.getLocalLocation()[VectorUtils.VECTOR_Z] == z);
210 | assert(vertex.getMaterial() == mesh.getMaterial("Material.001"));
211 |
212 | vertex = mesh.getVertex(15);
213 | assert(vertex.getIndex() == 15);
214 | x = FixedPointUtils.toFixedPoint(-1);
215 | y = FixedPointUtils.toFixedPoint(1);
216 | z = FixedPointUtils.toFixedPoint(-1);
217 | assert(vertex.getLocalLocation()[VectorUtils.VECTOR_X] == x);
218 | assert(vertex.getLocalLocation()[VectorUtils.VECTOR_Y] == y);
219 | assert(vertex.getLocalLocation()[VectorUtils.VECTOR_Z] == z);
220 | assert(vertex.getMaterial() == mesh.getMaterial("Material"));
221 |
222 | Face face = mesh.getFace(0);
223 | assert(face.getIndex() == 0);
224 | assert(face.getVertex(0) == mesh.getVertex(1));
225 | assert(face.getVertex(1) == mesh.getVertex(3));
226 | assert(face.getVertex(2) == mesh.getVertex(0));
227 |
228 | x = FixedPointUtils.toFixedPoint(0);
229 | y = FixedPointUtils.toFixedPoint(-1);
230 | z = FixedPointUtils.toFixedPoint(0);
231 | assert(face.getLocalNormal()[VectorUtils.VECTOR_X] == x);
232 | assert(face.getLocalNormal()[VectorUtils.VECTOR_Y] == y);
233 | assert(face.getLocalNormal()[VectorUtils.VECTOR_Z] == z);
234 |
235 | x = FixedPointUtils.toFixedPoint(1);
236 | y = FixedPointUtils.toFixedPoint(0);
237 | assert(face.getUV(0)[VectorUtils.VECTOR_X] == x);
238 | assert(face.getUV(0)[VectorUtils.VECTOR_Y] == y);
239 |
240 | x = FixedPointUtils.toFixedPoint(0);
241 | y = FixedPointUtils.toFixedPoint(1);
242 | assert(face.getUV(1)[VectorUtils.VECTOR_X] == x);
243 | assert(face.getUV(1)[VectorUtils.VECTOR_Y] == y);
244 |
245 | x = FixedPointUtils.toFixedPoint(0);
246 | y = FixedPointUtils.toFixedPoint(0);
247 | assert(face.getUV(2)[VectorUtils.VECTOR_X] == x);
248 | assert(face.getUV(2)[VectorUtils.VECTOR_Y] == y);
249 |
250 | assert(face.getMaterial() == mesh.getMaterial("Material.001"));
251 | }
252 |
253 | private String readResource(String path) throws IOException {
254 | final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
255 | final InputStream inputStream = classLoader.getResourceAsStream(path);
256 | return FileUtils.readStream(inputStream);
257 | }
258 |
259 | }
260 |
--------------------------------------------------------------------------------
/src/test/java/com/johnsproject/jgameengine/util/FixedPointUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.johnsproject.jgameengine.util;
2 |
3 | import org.junit.Test;
4 |
5 | import com.johnsproject.jgameengine.util.FixedPointUtils;
6 |
7 | public class FixedPointUtilsTest {
8 |
9 | // @Test
10 | // public void genLookupTableTest() throws Exception {
11 | // for (int angle = 0; angle < 91; angle++) {
12 | // System.out.print((int)Math.round(Math.sin(Math.toRadians(angle)) * FixedPointUtils.FP_ONE) + ", ");
13 | // }
14 | // }
15 |
16 | // @Test
17 | // public void genLookupTableTest() throws Exception {
18 | // for (int angle = 0; angle < 361; angle++) {
19 | // System.out.print((int)Math.round(Math.cos(Math.toRadians(angle)) * FixedPointUtils.FP_ONE) + ", ");
20 | // }
21 | // }
22 |
23 | @Test
24 | public void toDegreeTest() throws Exception {
25 | for (int i = 1; i < 360; i++) {
26 | double precision = 0.001;
27 | int fpRad = FixedPointUtils.toFixedPoint(Math.toRadians(i));
28 | double fpDegree = FixedPointUtils.toDegrees(fpRad);
29 | fpDegree = FixedPointUtils.toDouble((int)fpDegree);
30 | double mathDegree = i;
31 | assert((fpDegree >= mathDegree - precision) && (fpDegree <= mathDegree + precision));
32 | }
33 | }
34 |
35 | @Test
36 | public void toRadiansTest() throws Exception {
37 | for (int i = 1; i < 360; i++) {
38 | double precision = 0.1;
39 | int fpDegree = FixedPointUtils.toFixedPoint(Math.toDegrees(i));
40 | double fpRad = FixedPointUtils.toRadians(fpDegree);
41 | fpRad = FixedPointUtils.toDouble((int)fpRad);
42 | double mathRad = i;
43 | assert((fpRad >= mathRad - precision) && (fpRad <= mathRad + precision));
44 | }
45 | }
46 |
47 | @Test
48 | public void sinTest() throws Exception {
49 | for (int i = 0; i < 361; i++) {
50 | double precision = 0.0001;
51 | int fpAngle = FixedPointUtils.toFixedPoint(i);
52 | double fpSin = FixedPointUtils.sin(fpAngle);
53 | fpSin = FixedPointUtils.toDouble((int)fpSin);
54 | double sin = Math.sin(Math.toRadians(i));
55 | assert ((fpSin >= sin - precision) && (fpSin <= sin + precision));
56 | }
57 | for (int i = 0; i > -361; i--) {
58 | double precision = 0.0001;
59 | int fpAngle = FixedPointUtils.toFixedPoint(i);
60 | double fpSin = FixedPointUtils.sin(fpAngle);
61 | fpSin = FixedPointUtils.toDouble((int)fpSin);
62 | double sin = Math.sin(Math.toRadians(i));
63 | assert ((fpSin >= sin - precision) && (fpSin <= sin + precision));
64 | }
65 | }
66 |
67 | @Test
68 | public void cosTest() throws Exception {
69 | for (int i = 0; i < 361; i++) {
70 | double precision = 0.0001;
71 | int fpAngle = FixedPointUtils.toFixedPoint(i);
72 | double fpCos = FixedPointUtils.cos(fpAngle);
73 | fpCos = FixedPointUtils.toDouble((int)fpCos);
74 | double cos = Math.cos(Math.toRadians(i));
75 | assert ((fpCos >= cos - precision) && (fpCos <= cos + precision));
76 | }
77 | for (int i = 0; i > -361; i--) {
78 | double precision = 0.0001;
79 | int fpAngle = FixedPointUtils.toFixedPoint(i);
80 | double fpCos = FixedPointUtils.cos(fpAngle);
81 | fpCos = FixedPointUtils.toDouble((int)fpCos);
82 | double cos = Math.cos(Math.toRadians(i));
83 | assert ((fpCos >= cos - precision) && (fpCos <= cos + precision));
84 | }
85 | }
86 |
87 | @Test
88 | public void tanTest() throws Exception {
89 | for (int i = 0; i < 90; i++) {
90 | double precision = 0.1;
91 | int fpAngle = FixedPointUtils.toFixedPoint(i);
92 | double fpTan = FixedPointUtils.tan(fpAngle);
93 | fpTan = FixedPointUtils.toDouble((int)fpTan);
94 | double tan = Math.tan(Math.toRadians(i));
95 | assert ((fpTan >= tan - precision) && (fpTan <= tan + precision));
96 | }
97 | }
98 |
99 | @Test
100 | public void asinTest() throws Exception {
101 | for (int i = 0; i < 361; i++) {
102 | double precision = 0.0001;
103 | double fpAngle = FixedPointUtils.toFixedPoint(i);
104 | int fpSin = FixedPointUtils.sin((int)fpAngle);
105 | fpAngle = FixedPointUtils.asin(fpSin);
106 | fpAngle = FixedPointUtils.toDouble((int)fpAngle);
107 | double sin = Math.sin(Math.toRadians(i));
108 | double angle = Math.toDegrees(Math.asin(sin));
109 | assert ((fpAngle >= angle - precision) && (fpAngle <= angle + precision));
110 | }
111 | for (int i = 0; i > -361; i--) {
112 | double precision = 0.0001;
113 | double fpAngle = FixedPointUtils.toFixedPoint(i);
114 | int fpSin = FixedPointUtils.sin((int)fpAngle);
115 | fpAngle = FixedPointUtils.asin(fpSin);
116 | fpAngle = FixedPointUtils.toDouble((int)fpAngle);
117 | double sin = Math.sin(Math.toRadians(i));
118 | double angle = Math.toDegrees(Math.asin(sin));
119 | assert ((fpAngle >= angle - precision) && (fpAngle <= angle + precision));
120 | }
121 | }
122 |
123 | @Test
124 | public void acosTest() throws Exception {
125 | for (int i = 0; i < 361; i++) {
126 | double precision = 0.0001;
127 | double fpAngle = FixedPointUtils.toFixedPoint(i);
128 | int fpCos = FixedPointUtils.cos((int)fpAngle);
129 | fpAngle = FixedPointUtils.acos(fpCos);
130 | fpAngle = FixedPointUtils.toDouble((int)fpAngle);
131 | double cos = Math.cos(Math.toRadians(i));
132 | double angle = Math.toDegrees(Math.acos(cos));
133 | assert ((fpAngle >= angle - precision) && (fpAngle <= angle + precision));
134 | }
135 | for (int i = 0; i > -361; i--) {
136 | double precision = 0.0001;
137 | double fpAngle = FixedPointUtils.toFixedPoint(i);
138 | int fpCos = FixedPointUtils.cos((int)fpAngle);
139 | fpAngle = FixedPointUtils.acos(fpCos);
140 | fpAngle = FixedPointUtils.toDouble((int)fpAngle);
141 | double cos = Math.cos(Math.toRadians(i));
142 | double angle = Math.toDegrees(Math.acos(cos));
143 | assert ((fpAngle >= angle - precision) && (fpAngle <= angle + precision));
144 | }
145 | }
146 |
147 | @Test
148 | public void basicOperationsTest() throws Exception {
149 | // 255 because 256 * 256 = 65536 and will cause overflow of integer part of fixed point
150 | for (int i = 1; i < 256; i++) {
151 | double precision = 0.000000000000000000000000000000000001;
152 | int fpValue1 = FixedPointUtils.toFixedPoint(i);
153 | int fpValue2 = FixedPointUtils.toFixedPoint(i);
154 | double fpMultiply = FixedPointUtils.multiply(fpValue1, fpValue2);
155 | double fpDivide = FixedPointUtils.divide(fpValue1, fpValue2);
156 | fpMultiply = FixedPointUtils.toDouble((int)fpMultiply);
157 | fpDivide = FixedPointUtils.toDouble((int)fpDivide);
158 | double value1 = i;
159 | double value2 = i;
160 | double mathMultiply = value1 * value2;
161 | double mathDivide = value1 / value2;
162 | assert ((fpMultiply >= mathMultiply - precision) && (fpMultiply <= mathMultiply + precision));
163 | assert ((fpDivide >= mathDivide - precision) && (fpDivide <= mathDivide + precision));
164 | }
165 | }
166 |
167 | @Test
168 | public void normalizeTest() throws Exception {
169 | for (int i = 2; i < FixedPointUtils.FP_ONE; i++) {
170 | int fpMin = FixedPointUtils.toFixedPoint(i);
171 | int fpMax = FixedPointUtils.toFixedPoint(i + 2);
172 | int fpValue = FixedPointUtils.toFixedPoint(i + 5);
173 | int fpNormalizedValue = FixedPointUtils.normalize(fpValue, fpMin, fpMax);
174 | assert(fpNormalizedValue >= fpMin && fpNormalizedValue <= fpMax);
175 | }
176 | }
177 |
178 | @Test
179 | public void clampTest() throws Exception {
180 | for (int i = 0; i < FixedPointUtils.FP_ONE; i++) {
181 | int fpMin = FixedPointUtils.toFixedPoint(i);
182 | int fpMax = FixedPointUtils.toFixedPoint(i + 5);
183 | int fpValue = FixedPointUtils.toFixedPoint(i + 10);
184 | int fpNormalizedValue = FixedPointUtils.clamp(fpValue, fpMin, fpMax);
185 | assert(fpNormalizedValue >= fpMin && fpNormalizedValue <= fpMax);
186 | }
187 | }
188 |
189 | @Test
190 | public void randomTest() throws Exception {
191 | int lastRandomValue = 0;
192 | for (int i = 1; i < FixedPointUtils.FP_ONE; i++) {
193 | int randomValue = FixedPointUtils.random(lastRandomValue);
194 | lastRandomValue = randomValue;
195 | assert(randomValue != FixedPointUtils.random(i));
196 | }
197 | }
198 | @Test
199 | public void minMaxRandomTest() throws Exception {
200 | int lastRandomValue = 0;
201 | for (int i = 1; i < FixedPointUtils.FP_ONE; i++) {
202 | int fpMin = FixedPointUtils.toFixedPoint(0);
203 | int fpMax = FixedPointUtils.toFixedPoint(100);
204 | int randomValue = FixedPointUtils.random(lastRandomValue, fpMin, fpMax);
205 | lastRandomValue = randomValue;
206 | assert(randomValue != FixedPointUtils.random(i, fpMin, fpMax));
207 | assert((randomValue <= fpMax) && (randomValue >= fpMin));
208 | assert((randomValue <= fpMax) && (randomValue >= fpMin));
209 | }
210 | }
211 |
212 | @Test
213 | public void powTest() throws Exception {
214 | // max pow is 15 because because integer part of fixed point has 15 bits
215 | for (int i = 0; i < 16; i++) {
216 | double precision = 0.000000000001;
217 | int value = 2;
218 | int fpValue = FixedPointUtils.toFixedPoint(value);
219 | double fpPow = FixedPointUtils.pow(fpValue, FixedPointUtils.toFixedPoint(i));
220 | fpPow = FixedPointUtils.toDouble((int)fpPow);
221 | double mathPow = Math.pow(value, i);
222 | assert((fpPow >= mathPow - precision) && (fpPow <= mathPow + precision));
223 | }
224 | }
225 |
226 | @Test
227 | public void sqrtTest() throws Exception {
228 | for (int i = 1; i < FixedPointUtils.FP_ONE; i++) {
229 | double precision = 0.01;
230 | double fpSqrt = FixedPointUtils.sqrt(FixedPointUtils.toFixedPoint(i));
231 | fpSqrt = FixedPointUtils.toDouble((int)fpSqrt);
232 | double mathSqrt = Math.sqrt(i);
233 | assert((fpSqrt >= mathSqrt - precision) && (fpSqrt <= mathSqrt + precision));
234 | }
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/test/resources/DefaultTest.mtl:
--------------------------------------------------------------------------------
1 | # Blender MTL File: 'exporterTest.blend'
2 | # Material Count: 5
3 |
4 | newmtl Material
5 | Ns 96.078431
6 | Ka 1.000000 1.000000 1.000000
7 | Kd 0.000000 0.640000 0.004048
8 | Ks 0.500000 0.500000 0.500000
9 | Ke 0.000000 0.000000 0.000000
10 | Ni 1.000000
11 | d 1.000000
12 | illum 2
13 |
14 | newmtl Material.001
15 | Ns 96.078431
16 | Ka 1.000000 1.000000 1.000000
17 | Kd 0.000000 0.026653 0.640000
18 | Ks 0.000000 0.500000 0.126628
19 | Ke 0.000000 0.000000 0.000000
20 | Ni 1.000000
21 | d 1.000000
22 | illum 2
23 |
24 | newmtl Material.002
25 | Ns 17.647059
26 | Ka 1.000000 1.000000 1.000000
27 | Kd 0.640000 0.382606 0.025754
28 | Ks 0.500000 0.500000 0.500000
29 | Ke 0.000000 0.000000 0.000000
30 | Ni 1.000000
31 | d 1.000000
32 | illum 2
33 |
34 | newmtl Material.006
35 | Ns 96.078431
36 | Ka 1.000000 1.000000 1.000000
37 | Kd 0.640000 0.640000 0.640000
38 | Ks 0.500000 0.500000 0.500000
39 | Ke 0.000000 0.000000 0.000000
40 | Ni 1.000000
41 | d 1.000000
42 | illum 2
43 |
44 | newmtl Material.007
45 | Ns 96.078431
46 | Ka 1.000000 1.000000 1.000000
47 | Kd 0.640000 0.002034 0.000000
48 | Ks 0.500000 0.500000 0.500000
49 | Ke 0.000000 0.000000 0.000000
50 | Ni 1.000000
51 | d 1.000000
52 | illum 2
53 |
--------------------------------------------------------------------------------
/src/test/resources/JohnsProjectLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JohnsProject/JGameEngine/58ab2a68198f234b96fc12aa1f8ad9eb73ae8a0e/src/test/resources/JohnsProjectLogo.png
--------------------------------------------------------------------------------
/src/test/resources/SpaceshipGame/Spaceship.mtl:
--------------------------------------------------------------------------------
1 | # Blender MTL File: 'None'
2 | # Material Count: 2
3 |
4 | newmtl Material
5 | Ns 96.078431
6 | Ka 1.000000 1.000000 1.000000
7 | Kd 0.800000 0.800000 0.800000
8 | Ks 0.500000 0.500000 0.500000
9 | Ke 0.000000 0.000000 0.000000
10 | Ni 1.000000
11 | d 1.000000
12 | illum 2
13 |
14 | newmtl Material.001
15 | Ns 96.078431
16 | Ka 1.000000 1.000000 1.000000
17 | Kd 0.000000 0.484832 0.640000
18 | Ks 0.500000 0.500000 0.500000
19 | Ke 0.000000 0.000000 0.000000
20 | Ni 1.000000
21 | d 1.000000
22 | illum 2
23 |
--------------------------------------------------------------------------------
/src/test/resources/SpaceshipGame/SpaceshipFire.mtl:
--------------------------------------------------------------------------------
1 | # Blender MTL File: 'None'
2 | # Material Count: 1
3 |
4 | newmtl Material.002
5 | Ns 96.078431
6 | Ka 1.000000 1.000000 1.000000
7 | Kd 0.800000 0.442716 0.000000
8 | Ks 0.500000 0.500000 0.500000
9 | Ke 0.000000 0.000000 0.000000
10 | Ni 1.000000
11 | d 1.000000
12 | illum 2
13 |
--------------------------------------------------------------------------------
/src/test/resources/SpaceshipGame/SpaceshipFire.obj:
--------------------------------------------------------------------------------
1 | # Blender v2.79 (sub 0) OBJ File: ''
2 | # www.blender.org
3 | mtllib SpaceshipFire.mtl
4 | v -0.410265 -0.410265 1.427912
5 | v -0.410265 0.410265 1.427912
6 | v -1.000000 -1.000000 -1.000000
7 | v -1.000000 1.000000 -1.000000
8 | v 0.410265 -0.410265 1.427912
9 | v 0.410265 0.410265 1.427912
10 | v 1.000000 -1.000000 -1.000000
11 | v 1.000000 1.000000 -1.000000
12 | v -0.281288 0.281288 -2.043880
13 | v -0.281288 -0.281288 -2.043880
14 | v 0.281288 -0.281288 -2.043880
15 | v 0.281288 0.281288 -2.043880
16 | vn -0.9717 0.0000 0.2360
17 | vn -0.8237 0.0000 -0.5671
18 | vn 0.9717 0.0000 0.2360
19 | vn 0.0000 0.0000 1.0000
20 | vn 0.0000 -0.9717 0.2360
21 | vn 0.0000 0.9717 0.2360
22 | vn 0.0000 0.0000 -1.0000
23 | vn 0.8237 0.0000 -0.5671
24 | vn 0.0000 -0.8237 -0.5671
25 | vn 0.0000 0.8237 -0.5671
26 | usemtl Material.002
27 | s off
28 | f 2//1 3//1 1//1
29 | f 4//2 10//2 3//2
30 | f 8//3 5//3 7//3
31 | f 6//4 1//4 5//4
32 | f 7//5 1//5 3//5
33 | f 4//6 6//6 8//6
34 | f 9//7 11//7 10//7
35 | f 7//8 12//8 8//8
36 | f 3//9 11//9 7//9
37 | f 8//10 9//10 4//10
38 | f 2//1 4//1 3//1
39 | f 4//2 9//2 10//2
40 | f 8//3 6//3 5//3
41 | f 6//4 2//4 1//4
42 | f 7//5 5//5 1//5
43 | f 4//6 2//6 6//6
44 | f 9//7 12//7 11//7
45 | f 7//8 11//8 12//8
46 | f 3//9 10//9 11//9
47 | f 8//10 12//10 9//10
48 |
--------------------------------------------------------------------------------
/src/test/resources/SpaceshipGame/Terrain.mtl:
--------------------------------------------------------------------------------
1 | # Blender MTL File: 'None'
2 | # Material Count: 2
3 |
4 | newmtl Material.001
5 | Ns 96.078431
6 | Ka 1.000000 1.000000 1.000000
7 | Kd 0.800000 0.800000 0.800000
8 | Ks 0.500000 0.500000 0.500000
9 | Ke 0.000000 0.000000 0.000000
10 | Ni 1.000000
11 | d 1.000000
12 | illum 2
13 |
14 | newmtl Material.002
15 | Ns 96.078431
16 | Ka 1.000000 1.000000 1.000000
17 | Kd 0.640000 0.640000 0.640000
18 | Ks 0.500000 0.500000 0.500000
19 | Ke 0.000000 0.000000 0.000000
20 | Ni 1.000000
21 | d 1.000000
22 | illum 2
23 |
--------------------------------------------------------------------------------
/src/test/resources/TestOBJ.mtl:
--------------------------------------------------------------------------------
1 | # Blender MTL File: 'None'
2 | # Material Count: 2
3 |
4 | newmtl Material
5 | Ns 17.647059
6 | Ka 1.000000 1.000000 1.000000
7 | Kd 0.800000 0.800000 0.800000
8 | Ks 0.500000 0.500000 0.500000
9 | Ke 0.000000 0.000000 0.000000
10 | Ni 1.000000
11 | d 1.000000
12 | illum 2
13 |
14 | newmtl Material.001
15 | Ns 96.078431
16 | Ka 1.000000 1.000000 1.000000
17 | Kd 0.640000 0.640000 0.640000
18 | Ks 0.500000 0.500000 0.500000
19 | Ke 0.000000 0.000000 0.000000
20 | Ni 1.000000
21 | d 1.000000
22 | illum 2
23 |
--------------------------------------------------------------------------------
/src/test/resources/TestOBJ.obj:
--------------------------------------------------------------------------------
1 | # Blender v2.79 (sub 0) OBJ File: ''
2 | # www.blender.org
3 | mtllib test.mtl
4 | v -4.198291 2.563459 -1.020053
5 | v -4.198291 2.563459 0.979947
6 | v -6.198291 2.563459 0.979947
7 | v -6.198290 2.563459 -1.020054
8 | v -4.198290 4.563459 -1.020053
9 | v -4.198291 4.563459 0.979947
10 | v -6.198291 4.563459 0.979946
11 | v -6.198291 4.563459 -1.020053
12 | vt 1.000000 0.000000
13 | vt 0.000000 1.000000
14 | vt 0.000000 0.000000
15 | vt 1.000000 0.000000
16 | vt 0.000000 1.000000
17 | vt 0.000000 0.000000
18 | vt 1.000000 0.000000
19 | vt 0.000000 1.000000
20 | vt 1.000000 0.000000
21 | vt 0.000000 1.000000
22 | vt 0.000000 0.000000
23 | vt 0.000000 0.000000
24 | vt 1.000000 1.000000
25 | vt 1.000000 0.000000
26 | vt 0.000000 1.000000
27 | vt 1.000000 1.000000
28 | vt 1.000000 1.000000
29 | vt 1.000000 1.000000
30 | vt 1.000000 0.000000
31 | vt 1.000000 1.000000
32 | vn 0.0000 -1.0000 0.0000
33 | vn 0.0000 1.0000 -0.0000
34 | vn 1.0000 0.0000 0.0000
35 | vn -0.0000 -0.0000 1.0000
36 | vn -1.0000 0.0000 -0.0000
37 | vn 0.0000 0.0000 -1.0000
38 | usemtl Material.001
39 | s off
40 | f 2/1/1 4/2/1 1/3/1
41 | f 8/4/2 6/5/2 5/6/2
42 | f 5/7/3 2/8/3 1/3/3
43 | f 6/9/4 3/10/4 2/11/4
44 | f 3/12/5 8/13/5 4/2/5
45 | f 1/14/6 8/15/6 5/6/6
46 | f 2/1/1 3/16/1 4/2/1
47 | f 8/4/2 7/17/2 6/5/2
48 | f 5/7/3 6/18/3 2/8/3
49 | f 6/9/4 7/17/4 3/10/4
50 | f 3/12/5 7/19/5 8/13/5
51 | f 1/14/6 4/20/6 8/15/6
52 | v 1.000000 -1.000000 -1.000000
53 | v 1.000000 -1.000000 1.000000
54 | v -1.000000 -1.000000 1.000000
55 | v -1.000000 -1.000000 -1.000000
56 | v 1.000000 1.000000 -0.999999
57 | v 0.999999 1.000000 1.000001
58 | v -1.000000 1.000000 1.000000
59 | v -1.000000 1.000000 -1.000000
60 | vt 1.000000 0.000000
61 | vt 0.000000 1.000000
62 | vt 0.000000 0.000000
63 | vt 1.000000 0.000000
64 | vt 0.000000 1.000000
65 | vt 0.000000 0.000000
66 | vt 1.000000 0.000000
67 | vt 0.000000 1.000000
68 | vt 1.000000 0.000000
69 | vt 0.000000 1.000000
70 | vt 0.000000 0.000000
71 | vt 0.000000 0.000000
72 | vt 1.000000 1.000000
73 | vt 1.000000 0.000000
74 | vt 0.000000 1.000000
75 | vt 1.000000 1.000000
76 | vt 1.000000 1.000000
77 | vt 1.000000 1.000000
78 | vt 1.000000 0.000000
79 | vt 1.000000 1.000000
80 | vn 0.0000 -1.0000 0.0000
81 | vn 0.0000 1.0000 0.0000
82 | vn 1.0000 -0.0000 0.0000
83 | vn 0.0000 -0.0000 1.0000
84 | vn -1.0000 -0.0000 -0.0000
85 | vn 0.0000 0.0000 -1.0000
86 | usemtl Material
87 | s off
88 | f 10/21/7 12/22/7 9/23/7
89 | f 16/24/8 14/25/8 13/26/8
90 | f 13/27/9 10/28/9 9/23/9
91 | f 14/29/10 11/30/10 10/31/10
92 | f 11/32/11 16/33/11 12/22/11
93 | f 9/34/12 16/35/12 13/26/12
94 | f 10/21/7 11/36/7 12/22/7
95 | f 16/24/8 15/37/8 14/25/8
96 | f 13/27/9 14/38/9 10/28/9
97 | f 14/29/10 15/37/10 11/30/10
98 | f 11/32/11 15/39/11 16/33/11
99 | f 9/34/12 12/40/12 16/35/12
100 |
--------------------------------------------------------------------------------