Compare commits
2 commits
e19ee6d867
...
e7336cbf82
Author | SHA1 | Date | |
---|---|---|---|
e7336cbf82 | |||
2b274fb8c8 |
79 changed files with 2682 additions and 270 deletions
56
.gitignore
vendored
56
.gitignore
vendored
|
@ -1,55 +1 @@
|
||||||
.gradle
|
.idea/
|
||||||
gradle/
|
|
||||||
build/
|
|
||||||
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
|
||||||
/.idea/workspace.xml
|
|
||||||
/.idea/usage.statistics.xml
|
|
||||||
/.idea/uiDesigner.xml
|
|
||||||
*.iws
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
out/
|
|
||||||
!**/src/main/**/out/
|
|
||||||
!**/src/test/**/out/
|
|
||||||
|
|
||||||
### Eclipse ###
|
|
||||||
.apt_generated
|
|
||||||
.classpath
|
|
||||||
.factorypath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.springBeans
|
|
||||||
.sts4-cache
|
|
||||||
bin/
|
|
||||||
!**/src/main/**/bin/
|
|
||||||
!**/src/test/**/bin/
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
/nbproject/private/
|
|
||||||
/nbbuild/
|
|
||||||
/dist/
|
|
||||||
/nbdist/
|
|
||||||
/.nb-gradle/
|
|
||||||
|
|
||||||
### VS Code ###
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
### OTHER ###
|
|
||||||
/wiki
|
|
||||||
.DS_Store
|
|
||||||
*.bat
|
|
||||||
*.conf
|
|
||||||
*.dll
|
|
||||||
*.dylib
|
|
||||||
*.jar
|
|
||||||
*.jpg
|
|
||||||
*.mhr
|
|
||||||
*.ogg
|
|
||||||
*.sh
|
|
||||||
*.so
|
|
||||||
*.ttf
|
|
||||||
*.wav
|
|
||||||
*.zip
|
|
||||||
touch.txt
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<bytecodeTargetLevel target="22" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GradleSettings">
|
|
||||||
<option name="linkedExternalProjectsSettings">
|
|
||||||
<GradleProjectSettings>
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="/var/home/snoweuph/Workspace/GameEngineJava" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</GradleProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RemoteRepositoriesConfiguration">
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="central" />
|
|
||||||
<option name="name" value="Maven Central repository" />
|
|
||||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="jboss.community" />
|
|
||||||
<option name="name" value="JBoss Community repository" />
|
|
||||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="MavenRepo" />
|
|
||||||
<option name="name" value="MavenRepo" />
|
|
||||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
|
||||||
</remote-repository>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ComposerSettings">
|
|
||||||
<execution />
|
|
||||||
</component>
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="22" project-jdk-type="JavaSDK" />
|
|
||||||
</project>
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
0
.gitattributes → Engine/.gitattributes
vendored
0
.gitattributes → Engine/.gitattributes
vendored
7
Engine/.gitignore
vendored
Normal file
7
Engine/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.gradle/
|
||||||
|
gradle/
|
||||||
|
gradlew
|
||||||
|
gradlew.bat
|
||||||
|
build/
|
||||||
|
|
||||||
|
.idea/
|
53
Engine/build.gradle.kts
Normal file
53
Engine/build.gradle.kts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("com.gradleup.shadow") version "8.3.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
apply(plugin = "java")
|
||||||
|
apply(plugin = "com.gradleup.shadow")
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(22)
|
||||||
|
}
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val lwjglVersion = "3.3.4"
|
||||||
|
val jomlVersion = "1.10.7"
|
||||||
|
val jomlPrimitivesVersion = "1.10.0"
|
||||||
|
|
||||||
|
val lwjglNatives = Pair(
|
||||||
|
System.getProperty("os.name")!!,
|
||||||
|
System.getProperty("os.arch")!!
|
||||||
|
).let { (name, arch) ->
|
||||||
|
when {
|
||||||
|
arrayOf("Linux", "SunOS", "Unit").any { name.startsWith(it) } ->
|
||||||
|
if (arrayOf("arm", "aarch64").any { arch.startsWith(it) })
|
||||||
|
"natives-linux${if (arch.contains("64") || arch.startsWith("armv8")) "-arm64" else "-arm32"}"
|
||||||
|
else if (arch.startsWith("ppc"))
|
||||||
|
"natives-linux-ppc64le"
|
||||||
|
else if (arch.startsWith("riscv"))
|
||||||
|
"natives-linux-riscv64"
|
||||||
|
else
|
||||||
|
"natives-linux"
|
||||||
|
|
||||||
|
arrayOf("Mac OS X", "Darwin").any { name.startsWith(it) } ->
|
||||||
|
"natives-macos${if (arch.startsWith("aarch64")) "-arm64" else ""}"
|
||||||
|
|
||||||
|
arrayOf("Windows").any { name.startsWith(it) } ->
|
||||||
|
if (arch.contains("64"))
|
||||||
|
"natives-windows${if (arch.startsWith("aarch64")) "-arm64" else ""}"
|
||||||
|
else
|
||||||
|
"natives-windows-x86"
|
||||||
|
|
||||||
|
else ->
|
||||||
|
throw Error("Unrecognized or unsupported platform. Please set \"lwjglNatives\" manually")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extra["lwjglVersion"] = lwjglVersion
|
||||||
|
extra["jomlVersion"] = jomlVersion
|
||||||
|
extra["jomlPrimitivesVersion"] = jomlPrimitivesVersion
|
||||||
|
extra["lwjglNatives"] = lwjglNatives
|
7
Engine/settings.gradle.kts
Normal file
7
Engine/settings.gradle.kts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
rootProject.name = "Engine"
|
||||||
|
include(":core")
|
||||||
|
project(":core").projectDir = file("src/core")
|
||||||
|
include(":opengl")
|
||||||
|
project(":opengl").projectDir = file("src/opengl")
|
||||||
|
include(":rendering")
|
||||||
|
project(":rendering").projectDir = file("src/rendering")
|
59
Engine/src/core/build.gradle.kts
Normal file
59
Engine/src/core/build.gradle.kts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("com.gradleup.shadow") version "8.3.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
val lwjglVersion: String by rootProject.extra
|
||||||
|
val jomlVersion: String by rootProject.extra
|
||||||
|
val jomlPrimitivesVersion: String by rootProject.extra
|
||||||
|
val lwjglNatives: String by rootProject.extra
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val main by getting {
|
||||||
|
java.srcDir("java")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||||
|
implementation("org.projectlombok:lombok:1.18.34")
|
||||||
|
annotationProcessor("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
implementation("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
|
||||||
|
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))
|
||||||
|
implementation("org.lwjgl", "lwjgl")
|
||||||
|
runtimeOnly("org.lwjgl", "lwjgl", classifier = lwjglNatives)
|
||||||
|
|
||||||
|
implementation("org.lwjgl", "lwjgl-glfw")
|
||||||
|
runtimeOnly("org.lwjgl", "lwjgl-glfw", classifier = lwjglNatives)
|
||||||
|
|
||||||
|
implementation("org.lwjgl", "lwjgl-stb")
|
||||||
|
runtimeOnly("org.lwjgl", "lwjgl-stb", classifier = lwjglNatives)
|
||||||
|
|
||||||
|
implementation("org.joml", "joml", jomlVersion)
|
||||||
|
implementation("org.joml", "joml-primitives", jomlPrimitivesVersion)
|
||||||
|
|
||||||
|
shadow(localGroovy())
|
||||||
|
shadow(gradleApi())
|
||||||
|
|
||||||
|
//implementation("org.lwjgl", "lwjgl-nuklear")
|
||||||
|
//implementation("org.lwjgl", "lwjgl-openal")
|
||||||
|
//implementation("org.lwjgl", "lwjgl-remotery")
|
||||||
|
//runtimeOnly("org.lwjgl", "lwjgl-nuklear", classifier = lwjglNatives)
|
||||||
|
//runtimeOnly("org.lwjgl", "lwjgl-openal", classifier = lwjglNatives)
|
||||||
|
//runtimeOnly("org.lwjgl", "lwjgl-remotery", classifier = lwjglNatives)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<ShadowJar>("shadowJar") {
|
||||||
|
archiveFileName.set("dev.euph.engine.core.jar")
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
shadow
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package dev.euph.engine.core.data;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class Archetype<T> {
|
||||||
|
private final Set<Class<? extends T>> componentClasses;
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public Archetype(Class<? extends T>... componentClasses) {
|
||||||
|
this.componentClasses = new HashSet<>(Arrays.asList(componentClasses));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Archetype(Set<Class<? extends T>> componentClasses) {
|
||||||
|
this.componentClasses = new HashSet<>(componentClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Archetype(List<Class<? extends T>> componentClasses) {
|
||||||
|
this.componentClasses = new HashSet<>(componentClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Class<? extends T> componentClass) {
|
||||||
|
for (Class<? extends T> _componentClass : componentClasses) {
|
||||||
|
if (componentClass.isAssignableFrom(_componentClass)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package dev.euph.engine.core.data;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
import static org.lwjgl.stb.STBImage.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageLoader {
|
||||||
|
public record Image(ByteBuffer data, int width, int height) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Image loadImage(String path) {
|
||||||
|
IntBuffer width = BufferUtils.createIntBuffer(1);
|
||||||
|
IntBuffer height = BufferUtils.createIntBuffer(1);
|
||||||
|
IntBuffer channels = BufferUtils.createIntBuffer(1);
|
||||||
|
ByteBuffer imageBuffer = stbi_load(path, width, height, channels, 0);
|
||||||
|
if(imageBuffer == null) {
|
||||||
|
throw new RuntimeException("Failed to load image: " + stbi_failure_reason());
|
||||||
|
}
|
||||||
|
Image image = new Image(imageBuffer, width.get(0), height.get(0));
|
||||||
|
stbi_image_free(imageBuffer);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package dev.euph.engine.core.data.octree;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Octree<T> {
|
||||||
|
private final OctreeNode<T> root;
|
||||||
|
|
||||||
|
public void insert(Vector3f position, T data) {
|
||||||
|
root.insert(position, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> query(Vector3f position, float radius) {
|
||||||
|
return root.query(position, radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
package dev.euph.engine.core.data.octree;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class OctreeNode<T> {
|
||||||
|
private final int maxCapacity;
|
||||||
|
private final Vector3f center;
|
||||||
|
private final float halfSize;
|
||||||
|
private final List<Vector3f> positions;
|
||||||
|
private final Map<Vector3f, T> dataMap;
|
||||||
|
@Getter
|
||||||
|
private OctreeNode<T>[] children;
|
||||||
|
|
||||||
|
public OctreeNode(int maxCapacity, Vector3f center, float halfSize) {
|
||||||
|
this.maxCapacity = maxCapacity;
|
||||||
|
this.center = center;
|
||||||
|
this.halfSize = halfSize;
|
||||||
|
this.positions = new ArrayList<>();
|
||||||
|
this.dataMap = new HashMap<>();
|
||||||
|
this.children = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(Vector3f position, T data) {
|
||||||
|
if (isOutOfBounds(position)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (children == null) {
|
||||||
|
positions.add(position);
|
||||||
|
dataMap.put(position, data);
|
||||||
|
if (positions.size() > maxCapacity) {
|
||||||
|
subdivide();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (OctreeNode<T> child : children) {
|
||||||
|
child.insert(position, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> query(Vector3f position, float radius) {
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
|
||||||
|
if (isOutOfBounds(position)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Vector3f itemPosition : positions) {
|
||||||
|
if (itemPosition.distance(position) <= radius) {
|
||||||
|
result.add(dataMap.get(itemPosition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (children == null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
for (OctreeNode<T> child : children) {
|
||||||
|
result.addAll(child.query(position, radius));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOutOfBounds(Vector3f position) {
|
||||||
|
float minX = center.x - halfSize;
|
||||||
|
float maxX = center.x + halfSize;
|
||||||
|
float minY = center.y - halfSize;
|
||||||
|
float maxY = center.y + halfSize;
|
||||||
|
float minZ = center.z - halfSize;
|
||||||
|
float maxZ = center.z + halfSize;
|
||||||
|
|
||||||
|
return (
|
||||||
|
position.x < minX ||
|
||||||
|
position.x > maxX ||
|
||||||
|
position.y < minY ||
|
||||||
|
position.y > maxY ||
|
||||||
|
position.z < minZ ||
|
||||||
|
position.z > maxZ
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void subdivide() {
|
||||||
|
OctreeNode<T>[] childNodes = new OctreeNode[8];
|
||||||
|
|
||||||
|
float quarterSize = halfSize / 2.0f;
|
||||||
|
|
||||||
|
Vector3f[] childCenters = new Vector3f[]{
|
||||||
|
new Vector3f(center.x - quarterSize, center.y - quarterSize, center.z - quarterSize),
|
||||||
|
new Vector3f(center.x + quarterSize, center.y - quarterSize, center.z - quarterSize),
|
||||||
|
new Vector3f(center.x - quarterSize, center.y + quarterSize, center.z - quarterSize),
|
||||||
|
new Vector3f(center.x + quarterSize, center.y + quarterSize, center.z - quarterSize),
|
||||||
|
new Vector3f(center.x - quarterSize, center.y - quarterSize, center.z + quarterSize),
|
||||||
|
new Vector3f(center.x + quarterSize, center.y - quarterSize, center.z + quarterSize),
|
||||||
|
new Vector3f(center.x - quarterSize, center.y + quarterSize, center.z + quarterSize),
|
||||||
|
new Vector3f(center.x + quarterSize, center.y + quarterSize, center.z + quarterSize)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
childNodes[i] = new OctreeNode<>(maxCapacity, childCenters[i], quarterSize);
|
||||||
|
}
|
||||||
|
children = childNodes;
|
||||||
|
|
||||||
|
for (int i = positions.size() - 1; i >= 0; i--) {
|
||||||
|
Vector3f position = positions.get(i);
|
||||||
|
T data = dataMap.remove(position);
|
||||||
|
for (OctreeNode<T> child : children) {
|
||||||
|
if (!child.isOutOfBounds(position)) {
|
||||||
|
child.insert(position, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
positions.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Vector3f> getPositions() {
|
||||||
|
List<Vector3f> allPositions = new ArrayList<>(positions);
|
||||||
|
|
||||||
|
if (children != null) {
|
||||||
|
for (OctreeNode<T> child : children) {
|
||||||
|
allPositions.addAll(child.getPositions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allPositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package dev.euph.engine.core.data.pipeline;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class Pipeline<Input, Output> {
|
||||||
|
private final Collection<PipelineStage<?, ?>> pipelineStages;
|
||||||
|
|
||||||
|
public Pipeline() {
|
||||||
|
this.pipelineStages = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pipeline(PipelineStage<Input, Output> pipelineStage) {
|
||||||
|
pipelineStages = Collections.singletonList(pipelineStage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pipeline(Collection<PipelineStage<?, ?>> pipelineStages) {
|
||||||
|
this.pipelineStages = new ArrayList<>(pipelineStages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <NewOutput> Pipeline<Input, NewOutput> addStage(PipelineStage<Output, NewOutput> pipelineStage) {
|
||||||
|
final ArrayList<PipelineStage<?, ?>> newPipelineStages = new ArrayList<>(pipelineStages);
|
||||||
|
newPipelineStages.add(pipelineStage);
|
||||||
|
return new Pipeline<>(newPipelineStages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param input the input of the pipeline
|
||||||
|
* @return the Output of the pipelines execution
|
||||||
|
* @throws PipelineRuntimeException if there is an error durring execution
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public Output execute(Input input) {
|
||||||
|
try {
|
||||||
|
Object output = input;
|
||||||
|
for (final PipelineStage pipelineStage : pipelineStages) {
|
||||||
|
output = pipelineStage.execute(output);
|
||||||
|
}
|
||||||
|
return (Output) output;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new PipelineRuntimeException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package dev.euph.engine.core.data.pipeline;
|
||||||
|
|
||||||
|
public class PipelineRuntimeException extends RuntimeException {
|
||||||
|
public PipelineRuntimeException(String message) {
|
||||||
|
super("Pipeline Runtime Exception: " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PipelineRuntimeException(String message, Throwable cause) {
|
||||||
|
super("Pipeline Runtime Exception: " + message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PipelineRuntimeException(Throwable cause) {
|
||||||
|
super("Pipeline Runtime Exception: " + cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PipelineRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super("Pipeline Runtime Exception: " + message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package dev.euph.engine.core.data.pipeline;
|
||||||
|
|
||||||
|
public interface PipelineStage<Input, Output> {
|
||||||
|
Output execute(Input input);
|
||||||
|
}
|
28
Engine/src/core/java/dev/euph/engine/core/ecs/Component.java
Normal file
28
Engine/src/core/java/dev/euph/engine/core/ecs/Component.java
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package dev.euph.engine.core.ecs;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class Component {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected Entity entity = null;
|
||||||
|
protected List<Class<? extends Component>> requiredComponents = new ArrayList<>();
|
||||||
|
|
||||||
|
public void OnReady() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnUpdate(float deltaTime) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDestroy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean hasRequiredComponents(Entity entity) {
|
||||||
|
return requiredComponents
|
||||||
|
.stream()
|
||||||
|
.allMatch(requiredClass -> entity.getComponent(requiredClass) != null);
|
||||||
|
}
|
||||||
|
}
|
87
Engine/src/core/java/dev/euph/engine/core/ecs/Entity.java
Normal file
87
Engine/src/core/java/dev/euph/engine/core/ecs/Entity.java
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package dev.euph.engine.core.ecs;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Entity {
|
||||||
|
@Getter
|
||||||
|
private final String name;
|
||||||
|
@Getter
|
||||||
|
private final List<Component> components;
|
||||||
|
private Scene scene;
|
||||||
|
|
||||||
|
public Entity(String name) {
|
||||||
|
this.name = name;
|
||||||
|
components = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Component> Entity addComponent(T component) {
|
||||||
|
|
||||||
|
if (!component.hasRequiredComponents(this)) {
|
||||||
|
throw new IllegalArgumentException("Cannot add Component. Missing required Components.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.components.add(component);
|
||||||
|
if (this.scene != null) {
|
||||||
|
this.scene.updateEntityComponents(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.entity != null) {
|
||||||
|
component.entity.removeComponent(component.getClass());
|
||||||
|
}
|
||||||
|
component.entity = this;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Component> T getComponent(Class<T> componentClass) {
|
||||||
|
for (Component c : components) {
|
||||||
|
if (componentClass.isAssignableFrom(c.getClass())) {
|
||||||
|
try {
|
||||||
|
return componentClass.cast(c);
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
assert false : "Error: Casting component.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Component> void removeComponent(Class<T> componentClass) {
|
||||||
|
for (int i = 0; i < components.size(); i++) {
|
||||||
|
if (componentClass.isAssignableFrom(components.get(i).getClass())) {
|
||||||
|
components.remove(i);
|
||||||
|
if (this.scene != null) {
|
||||||
|
this.scene.updateEntityComponents(this);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ready() {
|
||||||
|
for (Component component : components) {
|
||||||
|
component.OnReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(float deltaTime) {
|
||||||
|
for (Component component : components) {
|
||||||
|
component.OnUpdate(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
scene.removeEntity(this);
|
||||||
|
components.forEach(Component::OnDestroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity addToScene(Scene scene) {
|
||||||
|
scene.addEntity(this);
|
||||||
|
this.scene = scene;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
12
Engine/src/core/java/dev/euph/engine/core/ecs/Requires.java
Normal file
12
Engine/src/core/java/dev/euph/engine/core/ecs/Requires.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package dev.euph.engine.core.ecs;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface Requires {
|
||||||
|
Class<? extends Component>[] value();
|
||||||
|
}
|
101
Engine/src/core/java/dev/euph/engine/core/ecs/Scene.java
Normal file
101
Engine/src/core/java/dev/euph/engine/core/ecs/Scene.java
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package dev.euph.engine.core.ecs;
|
||||||
|
|
||||||
|
|
||||||
|
import dev.euph.engine.core.data.Archetype;
|
||||||
|
import dev.euph.engine.core.ecs.storages.ComponentClassesBySuperclassStorage;
|
||||||
|
import dev.euph.engine.core.ecs.storages.ComponentsByTypeStorage;
|
||||||
|
import dev.euph.engine.core.ecs.storages.EntitiesByArchetypeStorage;
|
||||||
|
import dev.euph.engine.core.ecs.storages.EntitiesByNameStorage;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class Scene {
|
||||||
|
@Getter
|
||||||
|
private final Set<Entity> entities = new HashSet<>();
|
||||||
|
private final EntitiesByNameStorage entitiesByName = new EntitiesByNameStorage();
|
||||||
|
private final EntitiesByArchetypeStorage entitiesByArchetype = new EntitiesByArchetypeStorage();
|
||||||
|
private final ComponentClassesBySuperclassStorage componentClassesBySuperclass = new ComponentClassesBySuperclassStorage();
|
||||||
|
private final ComponentsByTypeStorage componentsByType = new ComponentsByTypeStorage();
|
||||||
|
@Getter
|
||||||
|
private boolean isRunning = false;
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
if (isRunning) return;
|
||||||
|
isRunning = true;
|
||||||
|
|
||||||
|
for (Entity entity : entities) {
|
||||||
|
entity.ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(float deltaTime) {
|
||||||
|
entities.forEach(entity -> entity.update(deltaTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeEntity(Entity entity) {
|
||||||
|
entities.remove(entity);
|
||||||
|
entitiesByName.remove(entity);
|
||||||
|
entitiesByArchetype.remove(entity);
|
||||||
|
componentClassesBySuperclass.remove(entity);
|
||||||
|
componentsByType.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateEntityComponents(Entity entity) {
|
||||||
|
Set<Class<? extends Component>> components = entity.getComponents()
|
||||||
|
.stream()
|
||||||
|
.map(Component::getClass)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
entitiesByName.update(entity, components);
|
||||||
|
entitiesByArchetype.update(entity, components);
|
||||||
|
componentClassesBySuperclass.update(entity, components);
|
||||||
|
componentsByType.update(entity, components);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addEntity(Entity entity) {
|
||||||
|
if (entities.contains(entity)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entities.add(entity);
|
||||||
|
|
||||||
|
Set<Class<? extends Component>> components = entity.getComponents()
|
||||||
|
.stream()
|
||||||
|
.map(Component::getClass)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
entitiesByName.add(entity, components);
|
||||||
|
entitiesByArchetype.add(entity, components);
|
||||||
|
componentClassesBySuperclass.add(entity, components);
|
||||||
|
componentsByType.add(entity, components);
|
||||||
|
|
||||||
|
if (!isRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entity.ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public final Set<Entity> getByComponents(boolean exactMatch, Class<? extends Component>... componentClasses) {
|
||||||
|
Set<Entity> filteredEntities = new HashSet<>();
|
||||||
|
|
||||||
|
if (exactMatch) {
|
||||||
|
filteredEntities.addAll(entitiesByArchetype.get(new Archetype<>(componentClasses)));
|
||||||
|
} else {
|
||||||
|
Set<Class<? extends Component>> componentSet = componentClassesBySuperclass.get(Set.of(componentClasses));
|
||||||
|
Set<Component> componentInstances = componentsByType.get(componentSet);
|
||||||
|
if (componentInstances != null) {
|
||||||
|
filteredEntities.addAll(componentInstances.stream()
|
||||||
|
.map(Component::getEntity)
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Set<Entity> getByName(String name) {
|
||||||
|
return entitiesByName.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package dev.euph.engine.core.ecs;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public interface SceneStorage<T, U> {
|
||||||
|
void add(Entity entity, Set<Class<? extends Component>> componentClasses);
|
||||||
|
|
||||||
|
void remove(Entity entity);
|
||||||
|
|
||||||
|
void remove(Set<Entity> entities);
|
||||||
|
|
||||||
|
void update(Entity entity, Set<Class<? extends Component>> newComponentClasses);
|
||||||
|
|
||||||
|
Set<U> get(T identifier);
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package dev.euph.engine.core.ecs.storages;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import dev.euph.engine.core.ecs.Entity;
|
||||||
|
import dev.euph.engine.core.ecs.SceneStorage;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ComponentClassesBySuperclassStorage implements SceneStorage<Set<Class<? extends Component>>, Class<? extends Component>> {
|
||||||
|
private final Map<Class<? extends Component>, Set<Class<? extends Component>>> subclassesByComponent;
|
||||||
|
|
||||||
|
public ComponentClassesBySuperclassStorage() {
|
||||||
|
this.subclassesByComponent = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Entity entity, Set<Class<? extends Component>> componentClasses) {
|
||||||
|
for (Class<? extends Component> componentClass : componentClasses) {
|
||||||
|
Set<Class<? extends Component>> subclasses = getSubclasses(componentClass);
|
||||||
|
subclassesByComponent.put(componentClass, subclasses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Entity entity) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Set<Entity> entities) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Entity entity, Set<Class<? extends Component>> newComponentClasses) {
|
||||||
|
remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Class<? extends Component>> get(Set<Class<? extends Component>> identifier) {
|
||||||
|
Set<Class<? extends Component>> componentSet = new HashSet<>();
|
||||||
|
for (Class<? extends Component> componentClass : identifier) {
|
||||||
|
componentSet.add(componentClass);
|
||||||
|
componentSet.addAll(subclassesByComponent.getOrDefault(componentClass, Collections.emptySet()));
|
||||||
|
}
|
||||||
|
return componentSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Class<? extends Component>> getSubclasses(Class<? extends Component> componentClass) {
|
||||||
|
Set<Class<? extends Component>> subclasses = new HashSet<>();
|
||||||
|
for (Class<? extends Component> clazz : subclassesByComponent.keySet()) {
|
||||||
|
if (componentClass.isAssignableFrom(clazz)) {
|
||||||
|
subclasses.add(clazz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subclasses;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package dev.euph.engine.core.ecs.storages;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import dev.euph.engine.core.ecs.Entity;
|
||||||
|
import dev.euph.engine.core.ecs.SceneStorage;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ComponentsByTypeStorage implements SceneStorage<Set<Class<? extends Component>>, Component> {
|
||||||
|
|
||||||
|
private final Map<Class<? extends Component>, Set<Component>> componentInstancesByType;
|
||||||
|
|
||||||
|
public ComponentsByTypeStorage() {
|
||||||
|
this.componentInstancesByType = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Entity entity, Set<Class<? extends Component>> componentClasses) {
|
||||||
|
for (Class<? extends Component> componentClass : componentClasses) {
|
||||||
|
Set<Component> componentInstances = componentInstancesByType.computeIfAbsent(componentClass, key -> new HashSet<>());
|
||||||
|
componentInstances.add(entity.getComponent(componentClass)); // added
|
||||||
|
|
||||||
|
Class<?> superClass = componentClass.getSuperclass();
|
||||||
|
while (Component.class.isAssignableFrom(superClass)) {
|
||||||
|
if (Component.class.isAssignableFrom(superClass)) {
|
||||||
|
Set<Component> superClassInstances = componentInstancesByType.computeIfAbsent(superClass.asSubclass(Component.class), key -> new HashSet<>());
|
||||||
|
superClassInstances.add(entity.getComponent(componentClass));
|
||||||
|
}
|
||||||
|
superClass = superClass.getSuperclass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Entity entity) {
|
||||||
|
for (Component component : entity.getComponents()) {
|
||||||
|
Class<? extends Component> componentClass = component.getClass();
|
||||||
|
Set<Component> componentInstances = componentInstancesByType.get(componentClass);
|
||||||
|
if (componentInstances != null) {
|
||||||
|
componentInstances.remove(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Set<Entity> entities) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Entity entity, Set<Class<? extends Component>> newComponentClasses) {
|
||||||
|
for (Component component : entity.getComponents()) {
|
||||||
|
Class<? extends Component> componentClass = component.getClass();
|
||||||
|
Set<Component> componentInstances = componentInstancesByType.computeIfAbsent(componentClass, key -> new HashSet<>());
|
||||||
|
componentInstances.add(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Component> get(Set<Class<? extends Component>> identifier) {
|
||||||
|
Set<Component> filteredEntities = new HashSet<>();
|
||||||
|
for (Class<? extends Component> componentClass : identifier) {
|
||||||
|
filteredEntities.addAll(componentInstancesByType.get(componentClass));
|
||||||
|
}
|
||||||
|
return filteredEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package dev.euph.engine.core.ecs.storages;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.data.Archetype;
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import dev.euph.engine.core.ecs.Entity;
|
||||||
|
import dev.euph.engine.core.ecs.SceneStorage;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class EntitiesByArchetypeStorage implements SceneStorage<Archetype<Component>, Entity> {
|
||||||
|
private final Map<Archetype<Component>, Set<Entity>> entitiesByArchetype;
|
||||||
|
|
||||||
|
public EntitiesByArchetypeStorage() {
|
||||||
|
this.entitiesByArchetype = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Entity entity, Set<Class<? extends Component>> componentClasses) {
|
||||||
|
Archetype<Component> type = new Archetype<>(componentClasses);
|
||||||
|
Set<Entity> entitySet = entitiesByArchetype.computeIfAbsent(type, _ -> new HashSet<>());
|
||||||
|
entitySet.add(entity);
|
||||||
|
this.entitiesByArchetype.put(type, entitySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Entity entity) {
|
||||||
|
for (Map.Entry<Archetype<Component>, Set<Entity>> entry : entitiesByArchetype.entrySet()) {
|
||||||
|
Archetype<Component> archetype = entry.getKey();
|
||||||
|
Set<Entity> archetypeEntities = entry.getValue();
|
||||||
|
if (!archetypeEntities.remove(entity)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (archetypeEntities.isEmpty()) {
|
||||||
|
entitiesByArchetype.remove(archetype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Set<Entity> entities) {
|
||||||
|
entities.forEach(this::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Entity entity, Set<Class<? extends Component>> newComponentClasses) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entity> get(Archetype<Component> identifier) {
|
||||||
|
return this.entitiesByArchetype.get(identifier);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package dev.euph.engine.core.ecs.storages;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import dev.euph.engine.core.ecs.Entity;
|
||||||
|
import dev.euph.engine.core.ecs.SceneStorage;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class EntitiesByNameStorage implements SceneStorage<String, Entity> {
|
||||||
|
private final Map<String, Set<Entity>> entitiesByName;
|
||||||
|
|
||||||
|
public EntitiesByNameStorage() {
|
||||||
|
this.entitiesByName = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(Entity entity, Set<Class<? extends Component>> componentClasses) {
|
||||||
|
Set<Entity> entities = this.entitiesByName.computeIfAbsent(entity.getName(), _ -> new HashSet<>());
|
||||||
|
if (entities == null) {
|
||||||
|
entities = new HashSet<>();
|
||||||
|
}
|
||||||
|
entities.add(entity);
|
||||||
|
this.entitiesByName.put(entity.getName(), entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Entity entity) {
|
||||||
|
Set<Entity> entities = this.entitiesByName.get(entity.getName());
|
||||||
|
if (entities == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (entities.size() == 1 && entities.contains(entity)) {
|
||||||
|
this.entitiesByName.remove(entity.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entities.remove(entity);
|
||||||
|
this.entitiesByName.put(entity.getName(), entities);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Set<Entity> entities) {
|
||||||
|
entities.forEach(this::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Entity entity, Set<Class<? extends Component>> newComponentClasses) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Entity> get(String identifier) {
|
||||||
|
return entitiesByName.get(identifier);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
package dev.euph.engine.core.transform;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import static java.lang.Math.toRadians;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Transform extends Component {
|
||||||
|
|
||||||
|
private Vector3f position = new Vector3f();
|
||||||
|
private Vector3f rotation = new Vector3f();
|
||||||
|
private Vector3f scale = new Vector3f(1);
|
||||||
|
private TransformationMatrix transformationMatrix;
|
||||||
|
|
||||||
|
|
||||||
|
public Transform(Vector3f position, Vector3f rotation, Vector3f scale) {
|
||||||
|
this.position = position;
|
||||||
|
this.rotation = rotation;
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform(Transform transform) {
|
||||||
|
this.position = transform.position;
|
||||||
|
this.rotation = transform.rotation;
|
||||||
|
this.scale = transform.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Transform FromPosition(float x, float y, float z) {
|
||||||
|
return Transform.FromPosition(new Vector3f(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Transform FromPosition(Vector3f position) {
|
||||||
|
return new Transform(position, new Vector3f(), new Vector3f(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Transform FromRotation(float x, float y, float z) {
|
||||||
|
return Transform.FromRotation(new Vector3f(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Transform FromRotation(Vector3f rotation) {
|
||||||
|
return new Transform(new Vector3f(), rotation, new Vector3f(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Transform FromScale(float x, float y, float z) {
|
||||||
|
return Transform.FromScale(new Vector3f(x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Transform FromScale(Vector3f scale) {
|
||||||
|
return new Transform(new Vector3f(), new Vector3f(), scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void calculateTransformationMatrix() {
|
||||||
|
transformationMatrix = new TransformationMatrix(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(Vector3f position) {
|
||||||
|
this.position = position;
|
||||||
|
calculateTransformationMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRotation(Vector3f rotation) {
|
||||||
|
this.rotation = rotation;
|
||||||
|
calculateTransformationMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform setScale(float scale) {
|
||||||
|
return setScale(new Vector3f(scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transform setScale(Vector3f scale) {
|
||||||
|
this.scale = scale;
|
||||||
|
calculateTransformationMatrix();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getUp() {
|
||||||
|
Quaternionf q = new Quaternionf().rotateXYZ(
|
||||||
|
(float) toRadians(rotation.x),
|
||||||
|
(float) toRadians(rotation.y),
|
||||||
|
(float) toRadians(rotation.z)
|
||||||
|
);
|
||||||
|
return q.positiveY(new Vector3f());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getDown() {
|
||||||
|
return new Vector3f(getUp()).negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getRight() {
|
||||||
|
Quaternionf q = new Quaternionf().rotateY((float) toRadians(rotation.y));
|
||||||
|
return q.positiveX(new Vector3f());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getLeft() {
|
||||||
|
return new Vector3f(getRight()).negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getForward() {
|
||||||
|
return new Vector3f(getBack()).negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getBack() {
|
||||||
|
Quaternionf q = new Quaternionf().rotateXYZ(
|
||||||
|
(float) toRadians(rotation.x),
|
||||||
|
(float) toRadians(rotation.y),
|
||||||
|
(float) toRadians(rotation.z)
|
||||||
|
);
|
||||||
|
return q.positiveZ(new Vector3f());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package dev.euph.engine.core.transform;
|
||||||
|
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public class TransformationMatrix extends Matrix4f {
|
||||||
|
|
||||||
|
public TransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale) {
|
||||||
|
super();
|
||||||
|
super.identity();
|
||||||
|
super.translate(translation);
|
||||||
|
super.rotate((float) Math.toRadians(rotation.x % 360), new Vector3f(1, 0, 0));
|
||||||
|
super.rotate((float) Math.toRadians(rotation.y % 360), new Vector3f(0, 1, 0));
|
||||||
|
super.rotate((float) Math.toRadians(rotation.z % 360), new Vector3f(0, 0, 1));
|
||||||
|
super.scale(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransformationMatrix(Transform transform) {
|
||||||
|
super();
|
||||||
|
super.identity();
|
||||||
|
super.translate(transform.getPosition());
|
||||||
|
Vector3f rotation = transform.getRotation();
|
||||||
|
super.rotate((float) Math.toRadians(rotation.x % 360), new Vector3f(1, 0, 0));
|
||||||
|
super.rotate((float) Math.toRadians(rotation.y % 360), new Vector3f(0, 1, 0));
|
||||||
|
super.rotate((float) Math.toRadians(rotation.z % 360), new Vector3f(0, 0, 1));
|
||||||
|
super.scale(transform.getScale());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransformationMatrix() {
|
||||||
|
super();
|
||||||
|
super.identity();
|
||||||
|
}
|
||||||
|
}
|
24
Engine/src/core/java/dev/euph/engine/core/util/Time.java
Normal file
24
Engine/src/core/java/dev/euph/engine/core/util/Time.java
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package dev.euph.engine.core.util;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
public class Time {
|
||||||
|
private long lastFrameTime;
|
||||||
|
@Getter
|
||||||
|
private float deltaTime;
|
||||||
|
|
||||||
|
public void startDeltaTime() {
|
||||||
|
lastFrameTime = getCurrentTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateDeltaTime() {
|
||||||
|
long currentFrameTime = getCurrentTime();
|
||||||
|
deltaTime = (currentFrameTime - lastFrameTime) / 1000f;
|
||||||
|
lastFrameTime = currentFrameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getCurrentTime() {
|
||||||
|
return (long) (GLFW.glfwGetTime() * 1000L);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package dev.euph.engine.core.window;
|
||||||
|
|
||||||
|
public interface CloseCallback {
|
||||||
|
void onClose();
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package dev.euph.engine.core.window;
|
||||||
|
|
||||||
|
public interface ResizeCallback {
|
||||||
|
void onResize(int width, int height);
|
||||||
|
}
|
127
Engine/src/core/java/dev/euph/engine/core/window/Window.java
Normal file
127
Engine/src/core/java/dev/euph/engine/core/window/Window.java
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
package dev.euph.engine.core.window;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.util.Time;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||||
|
import org.lwjgl.glfw.GLFWWindowCloseCallback;
|
||||||
|
import org.lwjgl.glfw.GLFWWindowSizeCallback;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
|
||||||
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class Window {
|
||||||
|
|
||||||
|
//@Getter(AccessLevel.NONE)
|
||||||
|
protected final long id;
|
||||||
|
protected final Time time;
|
||||||
|
|
||||||
|
protected final int initialWidth, initialHeight;
|
||||||
|
private final Set<CloseCallback> closeCallbacks = new HashSet<>();
|
||||||
|
private final Set<ResizeCallback> resizeCallbacks = new HashSet<>();
|
||||||
|
protected int width, height;
|
||||||
|
|
||||||
|
public Window(int width, int height, String title) {
|
||||||
|
this(width, height, title, 0, new Time());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Window(int width, int height, String title, int vsyncInterval) {
|
||||||
|
this(width, height, title, vsyncInterval, new Time());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Window(int width, int height, String title, int vsyncInterval, Time time) {
|
||||||
|
this.time = time;
|
||||||
|
this.initialWidth = width;
|
||||||
|
this.width = width;
|
||||||
|
this.initialHeight = height;
|
||||||
|
this.height = height;
|
||||||
|
|
||||||
|
GLFWErrorCallback.createPrint(System.err).set();
|
||||||
|
if (!glfwInit()) {
|
||||||
|
throw new IllegalStateException("Unable to initialize GLFW");
|
||||||
|
}
|
||||||
|
glfwDefaultWindowHints();
|
||||||
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||||
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||||
|
|
||||||
|
this.id = glfwCreateWindow(width, height, title, 0, 0);
|
||||||
|
if (id == 0) {
|
||||||
|
throw new RuntimeException("Failed to create the GLFW window");
|
||||||
|
}
|
||||||
|
setupCallbacks();
|
||||||
|
glfwMakeContextCurrent(id);
|
||||||
|
glfwSwapInterval(vsyncInterval);
|
||||||
|
glfwShowWindow(id);
|
||||||
|
time.startDeltaTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupCallbacks() {
|
||||||
|
GLFWWindowCloseCallback closeCallback = new GLFWWindowCloseCallback() {
|
||||||
|
@Override
|
||||||
|
public void invoke(long window) {
|
||||||
|
closeCallbacks.forEach(CloseCallback::onClose);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
glfwSetWindowCloseCallback(id, closeCallback);
|
||||||
|
GLFWWindowSizeCallback windowSizeCallback = new GLFWWindowSizeCallback() {
|
||||||
|
@Override
|
||||||
|
public void invoke(long window, int newWidth, int newHeight) {
|
||||||
|
Window.this.width = newWidth;
|
||||||
|
Window.this.height = newHeight;
|
||||||
|
resizeCallbacks.forEach(callback -> callback.onResize(newWidth, newHeight));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
glfwSetWindowSizeCallback(id, windowSizeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
glfwSwapBuffers(id);
|
||||||
|
glfwPollEvents();
|
||||||
|
time.updateDeltaTime();
|
||||||
|
onUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
glfwFreeCallbacks(id);
|
||||||
|
glfwDestroyWindow(id);
|
||||||
|
glfwTerminate();
|
||||||
|
Objects.requireNonNull(glfwSetErrorCallback(null)).free();
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resize(int width, int height) {
|
||||||
|
glfwSetWindowSize(id, width, height);
|
||||||
|
resizeCallbacks.forEach(callback -> callback.onResize(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVsync(int interval) {
|
||||||
|
glfwSwapInterval(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addResizeCallback(ResizeCallback resizeCallback) {
|
||||||
|
resizeCallbacks.add(resizeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeResizeCallback(ResizeCallback resizeCallback) {
|
||||||
|
resizeCallbacks.remove(resizeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCloseCallback(CloseCallback closeCallback) {
|
||||||
|
closeCallbacks.add(closeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeCloseCallback(CloseCallback closeCallback) {
|
||||||
|
closeCallbacks.remove(closeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract void onUpdate();
|
||||||
|
|
||||||
|
protected abstract void onDestroy();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
49
Engine/src/opengl/build.gradle.kts
Normal file
49
Engine/src/opengl/build.gradle.kts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("com.gradleup.shadow") version "8.3.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
val lwjglVersion: String by rootProject.extra
|
||||||
|
val jomlVersion: String by rootProject.extra
|
||||||
|
val jomlPrimitivesVersion: String by rootProject.extra
|
||||||
|
val lwjglNatives: String by rootProject.extra
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val main by getting {
|
||||||
|
java.srcDir("java")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||||
|
implementation("org.projectlombok:lombok:1.18.34")
|
||||||
|
annotationProcessor("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
implementation("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
|
||||||
|
compileOnly(project(":core", configuration = "shadow"))
|
||||||
|
compileOnly(project(":rendering", configuration = "shadow"))
|
||||||
|
|
||||||
|
implementation("org.lwjgl", "lwjgl-assimp")
|
||||||
|
runtimeOnly("org.lwjgl", "lwjgl-assimp", classifier = lwjglNatives)
|
||||||
|
|
||||||
|
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))
|
||||||
|
implementation("org.lwjgl", "lwjgl-opengl")
|
||||||
|
runtimeOnly("org.lwjgl", "lwjgl-opengl", classifier = lwjglNatives)
|
||||||
|
|
||||||
|
shadow(localGroovy())
|
||||||
|
shadow(gradleApi())
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<ShadowJar>("shadowJar") {
|
||||||
|
archiveFileName.set("dev.euph.engine.opengl.jar")
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
shadow
|
||||||
|
}
|
44
Engine/src/opengl/java/dev/euph/engine/opengl/GL.java
Normal file
44
Engine/src/opengl/java/dev/euph/engine/opengl/GL.java
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL31;
|
||||||
|
|
||||||
|
public class GL extends GL31 {
|
||||||
|
|
||||||
|
protected static int getTextureIndex(int index) {
|
||||||
|
return switch (index) {
|
||||||
|
case 0 -> GL_TEXTURE0;
|
||||||
|
case 1 -> GL_TEXTURE1;
|
||||||
|
case 2 -> GL_TEXTURE2;
|
||||||
|
case 3 -> GL_TEXTURE3;
|
||||||
|
case 4 -> GL_TEXTURE4;
|
||||||
|
case 5 -> GL_TEXTURE5;
|
||||||
|
case 6 -> GL_TEXTURE6;
|
||||||
|
case 7 -> GL_TEXTURE7;
|
||||||
|
case 8 -> GL_TEXTURE8;
|
||||||
|
case 9 -> GL_TEXTURE9;
|
||||||
|
case 10 -> GL_TEXTURE10;
|
||||||
|
case 11 -> GL_TEXTURE11;
|
||||||
|
case 12 -> GL_TEXTURE12;
|
||||||
|
case 13 -> GL_TEXTURE13;
|
||||||
|
case 14 -> GL_TEXTURE14;
|
||||||
|
case 15 -> GL_TEXTURE15;
|
||||||
|
case 16 -> GL_TEXTURE16;
|
||||||
|
case 17 -> GL_TEXTURE17;
|
||||||
|
case 18 -> GL_TEXTURE18;
|
||||||
|
case 19 -> GL_TEXTURE19;
|
||||||
|
case 20 -> GL_TEXTURE20;
|
||||||
|
case 21 -> GL_TEXTURE21;
|
||||||
|
case 22 -> GL_TEXTURE22;
|
||||||
|
case 23 -> GL_TEXTURE23;
|
||||||
|
case 24 -> GL_TEXTURE24;
|
||||||
|
case 25 -> GL_TEXTURE25;
|
||||||
|
case 26 -> GL_TEXTURE26;
|
||||||
|
case 27 -> GL_TEXTURE27;
|
||||||
|
case 28 -> GL_TEXTURE28;
|
||||||
|
case 29 -> GL_TEXTURE29;
|
||||||
|
case 30 -> GL_TEXTURE30;
|
||||||
|
case 31 -> GL_TEXTURE31;
|
||||||
|
default -> throw new IndexOutOfBoundsException("There are Only 32 GL Textures");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
34
Engine/src/opengl/java/dev/euph/engine/opengl/GLMesh.java
Normal file
34
Engine/src/opengl/java/dev/euph/engine/opengl/GLMesh.java
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import dev.euph.engine.rendering.resources.Mesh;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||||
|
|
||||||
|
public class GLMesh implements Mesh {
|
||||||
|
private final int vaoId;
|
||||||
|
private final int vertexCount;
|
||||||
|
|
||||||
|
public GLMesh(float[] vertices, int[] indices) {
|
||||||
|
this.vertexCount = indices.length;
|
||||||
|
vaoId = GLMeshHelper.getVao();
|
||||||
|
GLMeshHelper.prepareLoad(vaoId);
|
||||||
|
GLMeshHelper.loadVertices(vertices);
|
||||||
|
GLMeshHelper.loadIndices(indices);
|
||||||
|
GLMeshHelper.cleanupLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bind() {
|
||||||
|
glBindVertexArray(vaoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unbind() {
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVertexCount() {
|
||||||
|
return vertexCount;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL11.GL_FLOAT;
|
||||||
|
import static org.lwjgl.opengl.GL15.*;
|
||||||
|
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
|
||||||
|
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||||
|
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
|
||||||
|
|
||||||
|
public class GLMeshHelper {
|
||||||
|
private final static int VERTICE_INDEX = 0;
|
||||||
|
private final static int TEXTURE_COORDINATE_INDEX = 1;
|
||||||
|
|
||||||
|
protected static int getVao() {
|
||||||
|
return glGenVertexArrays();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void prepareLoad(int vaoId) {
|
||||||
|
glBindVertexArray(vaoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void cleanupLoad() {
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadVertices(float[] vertices) {
|
||||||
|
int vboId = glGenBuffers();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vboId);
|
||||||
|
|
||||||
|
FloatBuffer buffer = BufferUtils.createFloatBuffer(vertices.length);
|
||||||
|
buffer.put(vertices).flip();
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(VERTICE_INDEX, 3, GL_FLOAT, false, 0, 0);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadIndices(int[] indices) {
|
||||||
|
int eboId = glGenBuffers();
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
|
||||||
|
|
||||||
|
IntBuffer buffer = BufferUtils.createIntBuffer(indices.length);
|
||||||
|
buffer.put(indices).flip();
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadTextureCoordinates(float[] textureCoordinates) {
|
||||||
|
int vboTextureCoords = glGenBuffers();
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vboTextureCoords);
|
||||||
|
|
||||||
|
FloatBuffer buffer = BufferUtils.createFloatBuffer(textureCoordinates.length);
|
||||||
|
buffer.put(textureCoordinates).flip();
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(TEXTURE_COORDINATE_INDEX, 2, GL_FLOAT, false, 0, 0);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
108
Engine/src/opengl/java/dev/euph/engine/opengl/GLMeshLoader.java
Normal file
108
Engine/src/opengl/java/dev/euph/engine/opengl/GLMeshLoader.java
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import dev.euph.engine.rendering.resources.Mesh;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.lwjgl.assimp.*;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class GLMeshLoader{
|
||||||
|
|
||||||
|
public static Mesh loadMesh(URL modelResource) {
|
||||||
|
try {
|
||||||
|
AIMesh mesh = loadMeshIntoAssimp(modelResource);
|
||||||
|
|
||||||
|
List<Float> verticesList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mesh.mNumVertices(); i++) {
|
||||||
|
loadVertex(mesh, i, verticesList);
|
||||||
|
}
|
||||||
|
float[] vertices = listToFloatArray(verticesList);
|
||||||
|
int[] indices = listToIntArray(loadIndices(mesh));
|
||||||
|
|
||||||
|
return new GLMesh(vertices, indices);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("Error loading mesh: " + modelResource.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Mesh loadTexturedMesh(URL modelResource) {
|
||||||
|
try {
|
||||||
|
AIMesh mesh = loadMeshIntoAssimp(modelResource);
|
||||||
|
|
||||||
|
List<Float> verticesList = new ArrayList<>();
|
||||||
|
List<Float> textureCoordinatesList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < mesh.mNumVertices(); i++) {
|
||||||
|
loadVertex(mesh, i, verticesList);
|
||||||
|
loadTextureCoordinates(mesh, i, textureCoordinatesList);
|
||||||
|
}
|
||||||
|
float[] vertices = listToFloatArray(verticesList);
|
||||||
|
int[] indices = listToIntArray(loadIndices(mesh));
|
||||||
|
float[] texCoords = listToFloatArray(textureCoordinatesList);
|
||||||
|
|
||||||
|
return new GLTexturedMesh(vertices, indices, texCoords);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException("Error loading mesh: " + modelResource.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadTextureCoordinates(AIMesh mesh, int i, List<Float> texCoordsList) {
|
||||||
|
AIVector3D textureCoordinate = mesh.mTextureCoords(0).get(i);
|
||||||
|
texCoordsList.add(textureCoordinate.x());
|
||||||
|
texCoordsList.add(textureCoordinate.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadVertex(AIMesh mesh, int i, List<Float> verticesList) {
|
||||||
|
AIVector3D vertex = mesh.mVertices().get(i);
|
||||||
|
verticesList.add(vertex.x());
|
||||||
|
verticesList.add(vertex.y());
|
||||||
|
verticesList.add(vertex.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Integer> loadIndices(AIMesh mesh) {
|
||||||
|
List<Integer> indicesList = new ArrayList<>();
|
||||||
|
AIFace.Buffer faceBuffer = mesh.mFaces();
|
||||||
|
while (faceBuffer.remaining() > 0) {
|
||||||
|
AIFace face = faceBuffer.get();
|
||||||
|
IntBuffer buffer = face.mIndices();
|
||||||
|
while (buffer.remaining() > 0) {
|
||||||
|
indicesList.add(buffer.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indicesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull AIMesh loadMeshIntoAssimp(URL modelResource) {
|
||||||
|
AIScene scene = Assimp.aiImportFile(modelResource.getPath(), Assimp.aiProcess_Triangulate | Assimp.aiProcess_FlipUVs);
|
||||||
|
|
||||||
|
if (scene == null || scene.mNumMeshes() == 0) {
|
||||||
|
throw new RuntimeException("Failed to load mesh: " + modelResource.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
AIMesh mesh = AIMesh.create(Objects.requireNonNull(scene.mMeshes()).get(0));
|
||||||
|
scene.close();
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float[] listToFloatArray(List<Float> list) {
|
||||||
|
float[] array = new float[list.size()];
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
array[i] = list.get(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] listToIntArray(List<Integer> list) {
|
||||||
|
int[] array = new int[list.size()];
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
array[i] = list.get(i);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.transform.Transform;
|
||||||
|
import dev.euph.engine.core.ecs.Entity;
|
||||||
|
import dev.euph.engine.core.ecs.Scene;
|
||||||
|
import dev.euph.engine.core.transform.TransformationMatrix;
|
||||||
|
import dev.euph.engine.rendering.Material;
|
||||||
|
import dev.euph.engine.rendering.RenderPipeline;
|
||||||
|
import dev.euph.engine.rendering.Shader;
|
||||||
|
import dev.euph.engine.rendering.ShaderManager;
|
||||||
|
import dev.euph.engine.rendering.components.Camera;
|
||||||
|
import dev.euph.engine.rendering.components.MeshRenderer;
|
||||||
|
import dev.euph.engine.rendering.resources.Mesh;
|
||||||
|
import dev.euph.engine.rendering.utils.ProjectionMatrix;
|
||||||
|
import dev.euph.engine.rendering.utils.ViewMatrix;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import static dev.euph.engine.opengl.GL.*;
|
||||||
|
|
||||||
|
public class GLRenderer extends RenderPipeline {
|
||||||
|
private final ShaderManager shaderManager;
|
||||||
|
public Camera activeCamera;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private Scene scene;
|
||||||
|
|
||||||
|
public GLRenderer(Scene scene, ShaderManager shaderManager) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.shaderManager = shaderManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render() {
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
if (activeCamera == null) {
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glClearColor(
|
||||||
|
activeCamera.backgroundColor.getRed(),
|
||||||
|
activeCamera.backgroundColor.getGreen(),
|
||||||
|
activeCamera.backgroundColor.getBlue(),
|
||||||
|
activeCamera.backgroundColor.getAlpha()
|
||||||
|
);
|
||||||
|
|
||||||
|
Shader shader = shaderManager.getShader("default");
|
||||||
|
if (shader == null) return;
|
||||||
|
|
||||||
|
startShader(shader);
|
||||||
|
ViewMatrix viewMatrix = new ViewMatrix(activeCamera);
|
||||||
|
loadMatrix4(shader, getUniformLocation(shader, "viewMatrix"), viewMatrix);
|
||||||
|
ProjectionMatrix projectionMatrix = new ProjectionMatrix(activeCamera);
|
||||||
|
loadMatrix4(shader, getUniformLocation(shader, "projectionMatrix"), projectionMatrix);
|
||||||
|
stopShader(shader);
|
||||||
|
|
||||||
|
for (Entity entity : scene.getByComponents(false, MeshRenderer.class)) {
|
||||||
|
renderEntity(entity, entity.getComponent(MeshRenderer.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderEntity(Entity entity, MeshRenderer meshRenderer) {
|
||||||
|
Mesh mesh = meshRenderer.getMesh();
|
||||||
|
Material material = meshRenderer.getMaterial();
|
||||||
|
Shader shader = material.getShader();
|
||||||
|
|
||||||
|
if (shader == null || mesh == null) return;
|
||||||
|
|
||||||
|
startShader(shader);
|
||||||
|
bindShaderAttributes(shader);
|
||||||
|
|
||||||
|
TransformationMatrix transformationMatrix = entity.getComponent(Transform.class).getTransformationMatrix();
|
||||||
|
loadMatrix4(shader, getUniformLocation(shader, "transformationMatrix"), transformationMatrix);
|
||||||
|
|
||||||
|
material.useMaterial();
|
||||||
|
mesh.bind();
|
||||||
|
enableShaderAttributes(shader);
|
||||||
|
glDrawElements(GL_TRIANGLES, mesh.getVertexCount(), GL_UNSIGNED_INT, 0);
|
||||||
|
disableShaderAttributes(shader);
|
||||||
|
mesh.unbind();
|
||||||
|
stopShader(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
200
Engine/src/opengl/java/dev/euph/engine/opengl/GLShader.java
Normal file
200
Engine/src/opengl/java/dev/euph/engine/opengl/GLShader.java
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import dev.euph.engine.rendering.Shader;
|
||||||
|
import dev.euph.engine.rendering.resources.Texture;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.joml.Vector4f;
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static dev.euph.engine.opengl.GL.*;
|
||||||
|
|
||||||
|
public class GLShader extends Shader {
|
||||||
|
|
||||||
|
private static final FloatBuffer matrixBuffer = BufferUtils.createFloatBuffer(16);
|
||||||
|
private final int programId;
|
||||||
|
private final Map<String, Integer> uniformLocations = new HashMap<>();
|
||||||
|
|
||||||
|
public GLShader(String name, URL vertexShader, URL fragmentShader) throws IOException {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
loadShader(vertexShader.openStream(), GL_VERTEX_SHADER),
|
||||||
|
loadShader(fragmentShader.openStream(), GL_FRAGMENT_SHADER)
|
||||||
|
);
|
||||||
|
programId = glCreateProgram();
|
||||||
|
setupProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GLShader(String name, InputStream vertexShaderStream, InputStream fragmentShaderStream) throws IOException {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
loadShader(vertexShaderStream, GL_VERTEX_SHADER),
|
||||||
|
loadShader(fragmentShaderStream, GL_FRAGMENT_SHADER)
|
||||||
|
);
|
||||||
|
programId = glCreateProgram();
|
||||||
|
setupProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int loadShader(InputStream shaderStream, int type) throws IOException {
|
||||||
|
StringBuilder shaderSource = new StringBuilder();
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(shaderStream))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
shaderSource.append(line).append("\n");
|
||||||
|
}
|
||||||
|
} catch (IOException exception) {
|
||||||
|
System.err.println("Could not read file!");
|
||||||
|
exception.printStackTrace();
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
int shaderId = glCreateShader(type);
|
||||||
|
glShaderSource(shaderId, shaderSource);
|
||||||
|
glCompileShader(shaderId);
|
||||||
|
if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == GL_FALSE) {
|
||||||
|
System.err.println(glGetShaderInfoLog(shaderId, 512));
|
||||||
|
System.err.println("Couldn't compile shader!");
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
return shaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupProgram() {
|
||||||
|
glAttachShader(programId, vertexShaderId);
|
||||||
|
glAttachShader(programId, fragmentShaderId);
|
||||||
|
bindAttributes();
|
||||||
|
glLinkProgram(programId);
|
||||||
|
glValidateProgram(programId);
|
||||||
|
getAllUniformLocations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
glUseProgram(programId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
stop();
|
||||||
|
glDetachShader(programId, vertexShaderId);
|
||||||
|
glDetachShader(programId, fragmentShaderId);
|
||||||
|
glDeleteShader(vertexShaderId);
|
||||||
|
glDeleteShader(fragmentShaderId);
|
||||||
|
glDeleteProgram(programId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getAllUniformLocations() {
|
||||||
|
int numUniforms = glGetProgrami(programId, GL_ACTIVE_UNIFORMS);
|
||||||
|
|
||||||
|
for (int i = 0; i < numUniforms; i++) {
|
||||||
|
String name = glGetActiveUniformName(programId, i);
|
||||||
|
int location = glGetUniformLocation(programId, name);
|
||||||
|
|
||||||
|
uniformLocations.put(name, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getUniformLocation(String uniformName) {
|
||||||
|
if (uniformLocations.containsKey(uniformName)) {
|
||||||
|
return uniformLocations.get(uniformName);
|
||||||
|
}
|
||||||
|
|
||||||
|
int location = glGetUniformLocation(programId, uniformName);
|
||||||
|
uniformLocations.put(uniformName, location);
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void bindAttributes() {
|
||||||
|
glBindAttribLocation(programId, 0, "vertexPositions");
|
||||||
|
glBindAttribLocation(programId, 1, "textureCoords");
|
||||||
|
glBindAttribLocation(programId, 2, "normals");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableAttributes() {
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void disableAttributes() {
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
glDisableVertexAttribArray(1);
|
||||||
|
glDisableVertexAttribArray(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadBoolean(int location, boolean value) {
|
||||||
|
glUniform1f(location, value ? 1f : 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadInt(int location, int value) {
|
||||||
|
glUniform1i(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadFloat(int location, float value) {
|
||||||
|
glUniform1f(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadVector2(int location, Vector2f value) {
|
||||||
|
glUniform2f(location, value.x, value.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadVector3(int location, Vector3f value) {
|
||||||
|
glUniform3f(location, value.x, value.y, value.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadVector4(int location, Vector4f value) {
|
||||||
|
glUniform4f(location, value.x, value.y, value.z, value.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadMatrix4(int location, Matrix4f value) {
|
||||||
|
value.get(matrixBuffer);
|
||||||
|
glUniformMatrix4fv(location, false, matrixBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void deleteTexture(Texture texture) {
|
||||||
|
if(texture == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
glDeleteTextures(texture.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void bindTexture2D(int location, Texture texture, int textureIndex) {
|
||||||
|
loadInt(location, textureIndex);
|
||||||
|
glActiveTexture(getTextureIndex(textureIndex));
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void bindTexture3D(int location, Texture texture, int textureIndex) {
|
||||||
|
loadInt(location, textureIndex);
|
||||||
|
glActiveTexture(getTextureIndex(textureIndex));
|
||||||
|
glBindTexture(GL_TEXTURE_3D, texture.getId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.data.ImageLoader;
|
||||||
|
import dev.euph.engine.rendering.resources.Texture;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static dev.euph.engine.opengl.GL.*;
|
||||||
|
|
||||||
|
public class GLTextureLoader {
|
||||||
|
public static Texture loadTexture(URL imageResource) {
|
||||||
|
int textureID = GL.glGenTextures();
|
||||||
|
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
ImageLoader.Image image = ImageLoader.loadImage(imageResource.getPath());
|
||||||
|
glTexImage2D(
|
||||||
|
GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_RGBA,
|
||||||
|
image.width(), image.height(),
|
||||||
|
0,
|
||||||
|
GL_RGBA,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
image.data()
|
||||||
|
);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
return new Texture(textureID, image.width(), image.height());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import dev.euph.engine.rendering.resources.Mesh;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||||
|
|
||||||
|
public class GLTexturedMesh implements Mesh {
|
||||||
|
private final int vaoId;
|
||||||
|
private final int vertexCount;
|
||||||
|
|
||||||
|
public GLTexturedMesh(float[] vertices, int[] indices, float[] textureCoords) {
|
||||||
|
this.vertexCount = indices.length;
|
||||||
|
vaoId = GLMeshHelper.getVao();
|
||||||
|
GLMeshHelper.prepareLoad(vaoId);
|
||||||
|
GLMeshHelper.loadVertices(vertices);
|
||||||
|
GLMeshHelper.loadIndices(indices);
|
||||||
|
GLMeshHelper.loadTextureCoordinates(textureCoords);
|
||||||
|
GLMeshHelper.cleanupLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bind() {
|
||||||
|
glBindVertexArray(vaoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unbind() {
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVertexCount() {
|
||||||
|
return vertexCount;
|
||||||
|
}
|
||||||
|
}
|
56
Engine/src/opengl/java/dev/euph/engine/opengl/GLWindow.java
Normal file
56
Engine/src/opengl/java/dev/euph/engine/opengl/GLWindow.java
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package dev.euph.engine.opengl;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.window.Window;
|
||||||
|
import org.lwjgl.glfw.GLFWVidMode;
|
||||||
|
import org.lwjgl.opengl.GL;
|
||||||
|
|
||||||
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
import static org.lwjgl.opengl.GL11.glViewport;
|
||||||
|
|
||||||
|
public class GLWindow extends Window {
|
||||||
|
|
||||||
|
public GLWindow(int width, int height, String title) {
|
||||||
|
super(width, height, title);
|
||||||
|
|
||||||
|
glfwSetKeyCallback(id, (window, key, scancode, action, mods) -> {
|
||||||
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
|
||||||
|
glfwSetWindowShouldClose(window, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (glfwGetPlatform() != GLFW_PLATFORM_WAYLAND) {
|
||||||
|
GLFWVidMode videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
assert videoMode != null;
|
||||||
|
glfwSetWindowPos(
|
||||||
|
id,
|
||||||
|
(videoMode.width() - width) / 2,
|
||||||
|
(videoMode.height() - height) / 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addResizeCallback((int newWidth, int newHeight) -> {
|
||||||
|
GLWindow.this.width = newWidth;
|
||||||
|
GLWindow.this.height = newHeight;
|
||||||
|
|
||||||
|
float[] x = new float[1];
|
||||||
|
float[] y = new float[1];
|
||||||
|
glfwGetWindowContentScale(getId(), x, y);
|
||||||
|
|
||||||
|
glViewport(0, 0, (int) (newWidth * x[0]), (int) (newHeight * y[0]));
|
||||||
|
});
|
||||||
|
|
||||||
|
GL.createCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onUpdate() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
42
Engine/src/rendering/build.gradle.kts
Normal file
42
Engine/src/rendering/build.gradle.kts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("com.gradleup.shadow") version "8.3.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
val lwjglVersion: String by rootProject.extra
|
||||||
|
val jomlVersion: String by rootProject.extra
|
||||||
|
val jomlPrimitivesVersion: String by rootProject.extra
|
||||||
|
val lwjglNatives: String by rootProject.extra
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val main by getting {
|
||||||
|
java.srcDir("java")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||||
|
implementation("org.projectlombok:lombok:1.18.34")
|
||||||
|
annotationProcessor("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
implementation("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
|
||||||
|
compileOnly(project(":core", configuration = "shadow"))
|
||||||
|
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))
|
||||||
|
|
||||||
|
shadow(localGroovy())
|
||||||
|
shadow(gradleApi())
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<ShadowJar>("shadowJar") {
|
||||||
|
archiveFileName.set("dev.euph.engine.rendering.jar")
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
shadow
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package dev.euph.engine.rendering;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
public static final int MAX_LIGHTS = 6;
|
||||||
|
public static final int MAX_LIGHT_DISTANCE = 100;
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package dev.euph.engine.rendering;
|
||||||
|
|
||||||
|
import dev.euph.engine.rendering.resources.Texture;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class Material {
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
protected final Shader shader;
|
||||||
|
private Texture albedoTexture;
|
||||||
|
private Color color;
|
||||||
|
|
||||||
|
public Material(Shader shader) {
|
||||||
|
this.shader = shader;
|
||||||
|
this.albedoTexture = null;
|
||||||
|
this.color = Color.WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void useMaterial() {
|
||||||
|
shader.start();
|
||||||
|
prepareColor();
|
||||||
|
prepareAlbedoTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareColor() {
|
||||||
|
int colorLocation = shader.getUniformLocation("color");
|
||||||
|
shader.loadVector4(colorLocation, new org.joml.Vector4f(
|
||||||
|
color.getRed() / 255f,
|
||||||
|
color.getGreen() / 255f,
|
||||||
|
color.getBlue() / 255f,
|
||||||
|
1f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareAlbedoTexture() {
|
||||||
|
int useAlbedoTextureLocation = shader.getUniformLocation("useAlbedoTexture");
|
||||||
|
if (albedoTexture != null) {
|
||||||
|
int albedoTextureLocation = shader.getUniformLocation("albedoTexture");
|
||||||
|
shader.bindTexture2D(albedoTextureLocation, albedoTexture, Texture.ALBEDO_INDEX);
|
||||||
|
shader.loadBoolean(useAlbedoTextureLocation, true);
|
||||||
|
} else {
|
||||||
|
shader.loadBoolean(useAlbedoTextureLocation, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public void cleanup() {
|
||||||
|
shader.deleteTexture(albedoTexture);
|
||||||
|
shader.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package dev.euph.engine.rendering;
|
||||||
|
|
||||||
|
import dev.euph.engine.rendering.resources.Texture;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
public class PBRMaterial extends Material {
|
||||||
|
private Texture normalTexture;
|
||||||
|
private Texture metallicTexture;
|
||||||
|
private Texture roughnessTexture;
|
||||||
|
private Texture aoTexture;
|
||||||
|
|
||||||
|
public PBRMaterial(Shader shader) {
|
||||||
|
super(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void useMaterial() {
|
||||||
|
super.useMaterial();
|
||||||
|
|
||||||
|
if (normalTexture != null) {
|
||||||
|
int normalTextureLocation = shader.getUniformLocation("normalTexture");
|
||||||
|
shader.bindTexture2D(normalTextureLocation, normalTexture, Texture.NORMAL_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metallicTexture != null) {
|
||||||
|
int metallicTextureLocation = shader.getUniformLocation("metallicTexture");
|
||||||
|
shader.bindTexture2D(metallicTextureLocation, metallicTexture, Texture.METALLIC_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roughnessTexture != null) {
|
||||||
|
int roughnessTextureLocation = shader.getUniformLocation("roughnessTexture");
|
||||||
|
shader.bindTexture2D(roughnessTextureLocation, roughnessTexture, Texture.ROUGHNESS_INDEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aoTexture != null) {
|
||||||
|
int aoTextureLocation = shader.getUniformLocation("aoTexture");
|
||||||
|
shader.bindTexture2D(aoTextureLocation, aoTexture, Texture.AMBIENT_OCCLUSION_INDEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() {
|
||||||
|
super.cleanup();
|
||||||
|
shader.deleteTexture(normalTexture);
|
||||||
|
shader.deleteTexture(metallicTexture);
|
||||||
|
shader.deleteTexture(roughnessTexture);
|
||||||
|
shader.deleteTexture(aoTexture);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package dev.euph.engine.rendering;
|
||||||
|
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.joml.Vector4f;
|
||||||
|
|
||||||
|
public abstract class RenderPipeline {
|
||||||
|
protected static void startShader(Shader shader) {
|
||||||
|
shader.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void stopShader(Shader shader) {
|
||||||
|
shader.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void bindShaderAttributes(Shader shader) {
|
||||||
|
shader.bindAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void enableShaderAttributes(Shader shader) {
|
||||||
|
shader.enableAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void disableShaderAttributes(Shader shader) {
|
||||||
|
shader.disableAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadBoolean(Shader shader, int location, boolean value) {
|
||||||
|
shader.loadBoolean(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadInt(Shader shader, int location, int value) {
|
||||||
|
shader.loadInt(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadFloat(Shader shader, int location, float value) {
|
||||||
|
shader.loadFloat(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadVector2(Shader shader, int location, Vector2f value) {
|
||||||
|
shader.loadVector2(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadVector3(Shader shader, int location, Vector3f value) {
|
||||||
|
shader.loadVector3(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadVector4(Shader shader, int location, Vector4f value) {
|
||||||
|
shader.loadVector4(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadMatrix4(Shader shader, int location, Matrix4f value) {
|
||||||
|
shader.loadMatrix4(location, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int getUniformLocation(Shader shader, String name) {
|
||||||
|
return shader.getUniformLocation(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void render();
|
||||||
|
|
||||||
|
public abstract void init();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package dev.euph.engine.rendering;
|
||||||
|
|
||||||
|
import dev.euph.engine.rendering.resources.Texture;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.joml.Vector4f;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
public abstract class Shader implements AutoCloseable {
|
||||||
|
protected final int vertexShaderId;
|
||||||
|
protected final int fragmentShaderId;
|
||||||
|
@Getter
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
protected Shader(String name, int vertexShaderId, int fragmentShaderId) {
|
||||||
|
this.name = name;
|
||||||
|
this.vertexShaderId = vertexShaderId;
|
||||||
|
this.fragmentShaderId = fragmentShaderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void start();
|
||||||
|
|
||||||
|
protected abstract void stop();
|
||||||
|
|
||||||
|
protected abstract void bindAttributes();
|
||||||
|
|
||||||
|
protected abstract void enableAttributes();
|
||||||
|
|
||||||
|
protected abstract void disableAttributes();
|
||||||
|
|
||||||
|
protected abstract int getUniformLocation(String name);
|
||||||
|
|
||||||
|
protected abstract void loadBoolean(int location, boolean value);
|
||||||
|
|
||||||
|
protected abstract void loadInt(int location, int value);
|
||||||
|
|
||||||
|
protected abstract void loadFloat(int location, float value);
|
||||||
|
|
||||||
|
protected abstract void loadVector2(int location, Vector2f value);
|
||||||
|
|
||||||
|
protected abstract void loadVector3(int location, Vector3f value);
|
||||||
|
|
||||||
|
protected abstract void loadVector4(int location, Vector4f value);
|
||||||
|
|
||||||
|
protected abstract void loadMatrix4(int location, Matrix4f value);
|
||||||
|
|
||||||
|
protected abstract void deleteTexture(Texture texture);
|
||||||
|
|
||||||
|
protected abstract void bindTexture2D(int location, Texture texture, int textureIndex);
|
||||||
|
|
||||||
|
protected abstract void bindTexture3D(int location, Texture texture, int textureIndex);
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package dev.euph.engine.rendering;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ShaderManager {
|
||||||
|
|
||||||
|
private final Map<String, Shader> shaders = new HashMap<>();
|
||||||
|
|
||||||
|
public void addShader(Shader shader) {
|
||||||
|
shaders.put(shader.getName(), shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Shader getShader(String shaderName) {
|
||||||
|
return shaders.get(shaderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup() {
|
||||||
|
shaders.values().forEach(shader -> {
|
||||||
|
try {
|
||||||
|
shader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
shaders.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package dev.euph.engine.rendering.components;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.transform.Transform;
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import dev.euph.engine.core.ecs.Requires;
|
||||||
|
import dev.euph.engine.core.window.Window;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
@Requires(Transform.class)
|
||||||
|
public class Camera extends Component {
|
||||||
|
public float fov = 90f;
|
||||||
|
public float nearPlane = 0.1f;
|
||||||
|
public float farPlane = 500f;
|
||||||
|
public Color backgroundColor = new Color(0, 200, 255);
|
||||||
|
@Getter
|
||||||
|
private Window window;
|
||||||
|
|
||||||
|
public Camera(Window window) {
|
||||||
|
this.window = window;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package dev.euph.engine.rendering.components;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.transform.Transform;
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import dev.euph.engine.core.ecs.Requires;
|
||||||
|
import dev.euph.engine.rendering.Material;
|
||||||
|
import dev.euph.engine.rendering.resources.Mesh;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Requires(Transform.class)
|
||||||
|
public class MeshRenderer extends Component {
|
||||||
|
private final Mesh mesh;
|
||||||
|
private final Material material;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package dev.euph.engine.rendering.components.lights;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class DirectionalLight extends LightSource {
|
||||||
|
public DirectionalLight(Color color) {
|
||||||
|
super(color, LightType.Directional);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package dev.euph.engine.rendering.components.lights;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.ecs.Component;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class LightSource extends Component {
|
||||||
|
|
||||||
|
private final int type;
|
||||||
|
@Setter
|
||||||
|
private Color color;
|
||||||
|
public LightSource(Color color, int type) {
|
||||||
|
this.color = color;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LightType {
|
||||||
|
public static final int Directional = 0;
|
||||||
|
public static final int Point = 1;
|
||||||
|
public static final int Spot = 2;
|
||||||
|
public static final int Area = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package dev.euph.engine.rendering.components.lights;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.transform.Transform;
|
||||||
|
import dev.euph.engine.core.ecs.Requires;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
@Requires(Transform.class)
|
||||||
|
public class PointLight extends LightSource {
|
||||||
|
public Vector3f attenuation;
|
||||||
|
|
||||||
|
public PointLight(Color color, Vector3f attenuation) {
|
||||||
|
super(color, LightType.Point);
|
||||||
|
this.attenuation = attenuation;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package dev.euph.engine.rendering.components.lights;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.transform.Transform;
|
||||||
|
import dev.euph.engine.core.ecs.Requires;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
@Requires(Transform.class)
|
||||||
|
public class SpotLight extends LightSource {
|
||||||
|
public float coneAngle;
|
||||||
|
public float hardCutoff;
|
||||||
|
public float softCutoff;
|
||||||
|
public Vector3f attenuation;
|
||||||
|
|
||||||
|
public SpotLight(Color color, Vector3f attenuation, float coneAngle, float hardCutoff, float softCutoff) {
|
||||||
|
super(color, LightType.Spot);
|
||||||
|
this.attenuation = attenuation;
|
||||||
|
this.coneAngle = coneAngle;
|
||||||
|
this.hardCutoff = hardCutoff;
|
||||||
|
this.softCutoff = softCutoff;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.euph.engine.rendering.resources;
|
||||||
|
|
||||||
|
public interface Mesh {
|
||||||
|
void bind();
|
||||||
|
void unbind();
|
||||||
|
int getVertexCount();
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package dev.euph.engine.rendering.resources;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class Texture {
|
||||||
|
|
||||||
|
public final static int ALBEDO_INDEX = 0;
|
||||||
|
public final static int NORMAL_INDEX = 1;
|
||||||
|
public final static int METALLIC_INDEX = 2;
|
||||||
|
public final static int ROUGHNESS_INDEX = 3;
|
||||||
|
public final static int AMBIENT_OCCLUSION_INDEX = 4;
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
private final int height;
|
||||||
|
private final int width;
|
||||||
|
|
||||||
|
public Texture(int id, int width, int height) {
|
||||||
|
this.id = id;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package dev.euph.engine.rendering.utils;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.window.Window;
|
||||||
|
import dev.euph.engine.rendering.components.Camera;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
public class ProjectionMatrix extends Matrix4f {
|
||||||
|
|
||||||
|
public ProjectionMatrix(Camera camera) {
|
||||||
|
super();
|
||||||
|
calculateProjection(camera.getWindow(), camera.fov, camera.nearPlane, camera.farPlane);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateProjection(Window window, float fov, float nearPlane, float farPlane) {
|
||||||
|
float aspectRatio = (float) window.getWidth() / (float) window.getHeight();
|
||||||
|
|
||||||
|
float y_scale = (float) (1f / Math.tan(Math.toRadians(fov / 2f))) * aspectRatio;
|
||||||
|
float x_scale = y_scale / aspectRatio;
|
||||||
|
float frustum_length = farPlane - nearPlane;
|
||||||
|
|
||||||
|
m00(x_scale);
|
||||||
|
m11(y_scale);
|
||||||
|
m22(-((farPlane + nearPlane) / frustum_length));
|
||||||
|
m23(-1);
|
||||||
|
m32(-((2 * nearPlane * farPlane) / frustum_length));
|
||||||
|
m33(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package dev.euph.engine.rendering.utils;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.transform.Transform;
|
||||||
|
import dev.euph.engine.rendering.components.Camera;
|
||||||
|
import org.joml.Math;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public class ViewMatrix extends Matrix4f {
|
||||||
|
|
||||||
|
public ViewMatrix(Camera camera) {
|
||||||
|
super();
|
||||||
|
Transform cameraTransform = camera.getEntity().getComponent(Transform.class);
|
||||||
|
super.identity();
|
||||||
|
super.rotate(Math.toRadians(cameraTransform.getRotation().x), new Vector3f(1, 0, 0));
|
||||||
|
super.rotate(Math.toRadians(cameraTransform.getRotation().y), new Vector3f(0, 1, 0));
|
||||||
|
super.rotate(Math.toRadians(cameraTransform.getRotation().z), new Vector3f(0, 0, 1));
|
||||||
|
Vector3f pos = new Vector3f(cameraTransform.getPosition());
|
||||||
|
super.translate(pos.negate());
|
||||||
|
}
|
||||||
|
}
|
46
Engine/src/resources/build.gradle.kts
Normal file
46
Engine/src/resources/build.gradle.kts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
id("com.gradleup.shadow") version "8.3.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
val lwjglVersion: String by rootProject.extra
|
||||||
|
val jomlVersion: String by rootProject.extra
|
||||||
|
val jomlPrimitivesVersion: String by rootProject.extra
|
||||||
|
val lwjglNatives: String by rootProject.extra
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
val main by getting {
|
||||||
|
java.srcDir("java")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||||
|
implementation("org.projectlombok:lombok:1.18.34")
|
||||||
|
annotationProcessor("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
implementation("javax.annotation:javax.annotation-api:1.3.2")
|
||||||
|
|
||||||
|
compileOnly(project(":core", configuration = "shadow"))
|
||||||
|
implementation(platform("org.lwjgl:lwjgl-bom:$lwjglVersion"))
|
||||||
|
|
||||||
|
implementation("org.lwjgl", "lwjgl-assimp")
|
||||||
|
runtimeOnly("org.lwjgl", "lwjgl-assimp", classifier = lwjglNatives)
|
||||||
|
|
||||||
|
|
||||||
|
shadow(localGroovy())
|
||||||
|
shadow(gradleApi())
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<ShadowJar>("shadowJar") {
|
||||||
|
archiveFileName.set("dev.euph.engine.resources.jar")
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
shadow
|
||||||
|
}
|
7
TestGame/.gitignore
vendored
Normal file
7
TestGame/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.gradle/
|
||||||
|
gradle/
|
||||||
|
gradlew
|
||||||
|
gradlew.bat
|
||||||
|
build/
|
||||||
|
|
||||||
|
.idea/
|
73
TestGame/build.gradle.kts
Normal file
73
TestGame/build.gradle.kts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
plugins {
|
||||||
|
application
|
||||||
|
java
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion.set(JavaLanguageVersion.of(22))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDir("src")
|
||||||
|
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDir("res")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass.set("dev.euph.game.Main")
|
||||||
|
}
|
||||||
|
|
||||||
|
val lwjglVersion = "3.3.4"
|
||||||
|
val jomlVersion = "1.10.7"
|
||||||
|
val jomlPrimitivesVersion = "1.10.0"
|
||||||
|
|
||||||
|
val lwjglNatives = Pair(
|
||||||
|
System.getProperty("os.name")!!,
|
||||||
|
System.getProperty("os.arch")!!
|
||||||
|
).let { (name, arch) ->
|
||||||
|
when {
|
||||||
|
arrayOf("Linux", "SunOS", "Unit").any { name.startsWith(it) } ->
|
||||||
|
if (arrayOf("arm", "aarch64").any { arch.startsWith(it) })
|
||||||
|
"natives-linux${if (arch.contains("64") || arch.startsWith("armv8")) "-arm64" else "-arm32"}"
|
||||||
|
else if (arch.startsWith("ppc"))
|
||||||
|
"natives-linux-ppc64le"
|
||||||
|
else if (arch.startsWith("riscv"))
|
||||||
|
"natives-linux-riscv64"
|
||||||
|
else
|
||||||
|
"natives-linux"
|
||||||
|
|
||||||
|
arrayOf("Mac OS X", "Darwin").any { name.startsWith(it) } ->
|
||||||
|
"natives-macos${if (arch.startsWith("aarch64")) "-arm64" else ""}"
|
||||||
|
|
||||||
|
arrayOf("Windows").any { name.startsWith(it) } ->
|
||||||
|
if (arch.contains("64"))
|
||||||
|
"natives-windows${if (arch.startsWith("aarch64")) "-arm64" else ""}"
|
||||||
|
else
|
||||||
|
"natives-windows-x86"
|
||||||
|
|
||||||
|
else ->
|
||||||
|
throw Error("Unrecognized or unsupported platform. Please set \"lwjglNatives\" manually")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extra["lwjglVersion"] = lwjglVersion
|
||||||
|
extra["jomlVersion"] = jomlVersion
|
||||||
|
extra["jomlPrimitivesVersion"] = jomlPrimitivesVersion
|
||||||
|
extra["lwjglNatives"] = lwjglNatives
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":core", configuration = "shadow"))
|
||||||
|
implementation(project(":rendering", configuration = "shadow"))
|
||||||
|
implementation(project(":opengl", configuration = "shadow"))
|
||||||
|
}
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
8
TestGame/settings.gradle.kts
Normal file
8
TestGame/settings.gradle.kts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
rootProject.name = "Game"
|
||||||
|
|
||||||
|
include(":core")
|
||||||
|
project(":core").projectDir = file("../Engine/src/core")
|
||||||
|
include(":rendering")
|
||||||
|
project(":rendering").projectDir = file("../Engine/src/rendering")
|
||||||
|
include(":opengl")
|
||||||
|
project(":opengl").projectDir = file("../Engine/src/opengl")
|
|
@ -1,16 +1,19 @@
|
||||||
package dev.euph.game;
|
package dev.euph.game;
|
||||||
|
|
||||||
import dev.euph.engine.ecs.Component;
|
import dev.euph.engine.core.transform.Transform;
|
||||||
import dev.euph.engine.ecs.components.Camera;
|
import dev.euph.engine.core.ecs.Component;
|
||||||
import dev.euph.engine.ecs.components.Transform;
|
import dev.euph.engine.core.ecs.Requires;
|
||||||
|
import dev.euph.engine.rendering.components.Camera;
|
||||||
import org.joml.Vector2f;
|
import org.joml.Vector2f;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
import org.lwjgl.glfw.GLFWKeyCallback;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Requires({Transform.class, Camera.class})
|
||||||
public class CameraController extends Component {
|
public class CameraController extends Component {
|
||||||
|
|
||||||
Camera camera;
|
Camera camera;
|
||||||
|
@ -24,11 +27,6 @@ public class CameraController extends Component {
|
||||||
private Vector2f prevMousePos;
|
private Vector2f prevMousePos;
|
||||||
private Vector2f inputVectorWASD = new Vector2f();
|
private Vector2f inputVectorWASD = new Vector2f();
|
||||||
private boolean justCapturedMouse = true;
|
private boolean justCapturedMouse = true;
|
||||||
public CameraController() {
|
|
||||||
super();
|
|
||||||
requireComponent(Transform.class);
|
|
||||||
requireComponent(Camera.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void OnReady() {
|
public void OnReady() {
|
100
TestGame/src/dev/euph/game/Main.java
Normal file
100
TestGame/src/dev/euph/game/Main.java
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package dev.euph.game;
|
||||||
|
|
||||||
|
import dev.euph.engine.core.ecs.Entity;
|
||||||
|
import dev.euph.engine.core.ecs.Scene;
|
||||||
|
import dev.euph.engine.core.transform.Transform;
|
||||||
|
import dev.euph.engine.core.window.Window;
|
||||||
|
import dev.euph.engine.opengl.*;
|
||||||
|
import dev.euph.engine.rendering.Material;
|
||||||
|
import dev.euph.engine.rendering.ShaderManager;
|
||||||
|
import dev.euph.engine.rendering.components.Camera;
|
||||||
|
import dev.euph.engine.rendering.components.MeshRenderer;
|
||||||
|
import dev.euph.engine.rendering.components.lights.DirectionalLight;
|
||||||
|
import dev.euph.engine.rendering.components.lights.PointLight;
|
||||||
|
import dev.euph.engine.rendering.resources.Mesh;
|
||||||
|
import dev.euph.engine.rendering.resources.Texture;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
Window window = new GLWindow(1200, 720, "Test Window");
|
||||||
|
window.setVsync(1);
|
||||||
|
|
||||||
|
ShaderManager shaderManager = loadShaders();
|
||||||
|
Scene scene = loadScene(window, shaderManager);
|
||||||
|
Entity camera = scene.getByName("Camera").stream().findFirst().orElse(null);
|
||||||
|
Entity Model = scene.getByName("Model").stream().findFirst().orElse(null);
|
||||||
|
|
||||||
|
GLRenderer forwardRenderer = new GLRenderer(scene, shaderManager);
|
||||||
|
forwardRenderer.activeCamera = camera.getComponent(Camera.class);
|
||||||
|
forwardRenderer.init();
|
||||||
|
|
||||||
|
float rotationAngle = 0;
|
||||||
|
float rotationSpeed = 20f;
|
||||||
|
scene.start();
|
||||||
|
AtomicBoolean running = new AtomicBoolean(true);
|
||||||
|
window.addCloseCallback(() -> {
|
||||||
|
running.set(false);
|
||||||
|
});
|
||||||
|
while (running.get()) {
|
||||||
|
scene.update(window.getTime().getDeltaTime());
|
||||||
|
|
||||||
|
rotationAngle += rotationSpeed * window.getTime().getDeltaTime();
|
||||||
|
rotationAngle %= 360;
|
||||||
|
Model.getComponent(Transform.class).setRotation(new Vector3f(0, rotationAngle, 0));
|
||||||
|
|
||||||
|
forwardRenderer.render();
|
||||||
|
window.update();
|
||||||
|
}
|
||||||
|
shaderManager.cleanup();
|
||||||
|
window.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ShaderManager loadShaders() throws IOException {
|
||||||
|
ShaderManager shaderManager = new ShaderManager();
|
||||||
|
URL fragmentShader = Main.class.getResource("/dev/euph/game/shader/default.fs.glsl");
|
||||||
|
URL vertexShader = Main.class.getResource("/dev/euph/game/shader/default.vs.glsl");
|
||||||
|
assert vertexShader != null;
|
||||||
|
assert fragmentShader != null;
|
||||||
|
shaderManager.addShader(new GLShader(
|
||||||
|
"default",
|
||||||
|
Main.class.getResourceAsStream("/dev/euph/game/shader/default.vs.glsl"),
|
||||||
|
Main.class.getResourceAsStream("/dev/euph/game/shader/default.fs.glsl")
|
||||||
|
));
|
||||||
|
return shaderManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Scene loadScene(Window window, ShaderManager shaderManager) {
|
||||||
|
Scene scene = new Scene();
|
||||||
|
Entity cameraEntity = new Entity("Camera")
|
||||||
|
.addComponent(Transform.FromPosition(0, 0, 3))
|
||||||
|
.addComponent(new Camera(window))
|
||||||
|
.addComponent(new CameraController())
|
||||||
|
.addToScene(scene);
|
||||||
|
Entity light1 = new Entity("DirectionalLight")
|
||||||
|
.addComponent(Transform.FromRotation(-0.5f, -1.0f, -0.5f))
|
||||||
|
.addComponent(new DirectionalLight(Color.WHITE))
|
||||||
|
.addToScene(scene);
|
||||||
|
Entity light2 = new Entity("PointLight")
|
||||||
|
.addComponent(Transform.FromRotation(-0.5f, -1.0f, -0.5f))
|
||||||
|
.addComponent(new PointLight(Color.red, new Vector3f(10)))
|
||||||
|
.addToScene(scene);
|
||||||
|
|
||||||
|
Mesh modelMesh = GLMeshLoader.loadTexturedMesh(Main.class.getResource("/dev/euph/game/models/human_rigged.obj"));
|
||||||
|
Texture uvTexture = GLTextureLoader.loadTexture(Main.class.getResource("/dev/euph/game/textures/uv.png"));
|
||||||
|
Material modelMaterial = new Material(shaderManager.getShader("default"))
|
||||||
|
.setColor(new Color(7, 77, 255, 255))
|
||||||
|
.setAlbedoTexture(uvTexture);
|
||||||
|
|
||||||
|
Entity modelEntity = new Entity("Model")
|
||||||
|
.addComponent(Transform.FromPosition(0, -4, -6).setScale(3))
|
||||||
|
.addComponent(new MeshRenderer(modelMesh, modelMaterial))
|
||||||
|
.addToScene(scene);
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.euph.game;
|
package dev.euph.game;
|
||||||
|
|
||||||
import dev.euph.engine.datastructs.octree.Octree;
|
import dev.euph.engine.core.data.octree.Octree;
|
||||||
|
import dev.euph.engine.core.data.octree.OctreeNode;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
@ -15,7 +16,11 @@ public class OctreeTest {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// Create an octree with a center at (0, 0, 0) and a half-size of 20 units
|
// Create an octree with a center at (0, 0, 0) and a half-size of 20 units
|
||||||
Octree<String> octree = new Octree<>(8, new Vector3f(0, 0, 0), 20);
|
Octree<String> octree = new Octree<>(new OctreeNode<String>(
|
||||||
|
8,
|
||||||
|
new Vector3f(),
|
||||||
|
20
|
||||||
|
));
|
||||||
|
|
||||||
// Generate clustered noise for more interesting patterns
|
// Generate clustered noise for more interesting patterns
|
||||||
Random random = new Random();
|
Random random = new Random();
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.euph.game;
|
package dev.euph.game;
|
||||||
|
|
||||||
import dev.euph.engine.datastructs.pipeline.Pipeline;
|
import dev.euph.engine.core.data.pipeline.Pipeline;
|
||||||
import dev.euph.engine.datastructs.pipeline.PipelineStage;
|
import dev.euph.engine.core.data.pipeline.PipelineStage;
|
||||||
|
|
||||||
public class PipelineTest {
|
public class PipelineTest {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
|
@ -1,23 +0,0 @@
|
||||||
plugins {
|
|
||||||
application
|
|
||||||
java
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion.set(JavaLanguageVersion.of(22))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
application {
|
|
||||||
mainClass.set("dev.euph.game.Main")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(path = ":engine", configuration = "shadow"))
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
rootProject.name = "game"
|
|
||||||
include(":engine")
|
|
||||||
project(":engine").projectDir = file("../")
|
|
|
@ -1,121 +0,0 @@
|
||||||
package dev.euph.game;
|
|
||||||
|
|
||||||
import dev.euph.engine.ecs.Entity;
|
|
||||||
import dev.euph.engine.ecs.components.Camera;
|
|
||||||
import dev.euph.engine.ecs.components.MeshRenderer;
|
|
||||||
import dev.euph.engine.ecs.components.Transform;
|
|
||||||
import dev.euph.engine.ecs.components.lights.DirectionalLight;
|
|
||||||
import dev.euph.engine.ecs.components.lights.PointLight;
|
|
||||||
import dev.euph.engine.render.ForwardRenderer;
|
|
||||||
import dev.euph.engine.managers.ShaderManager;
|
|
||||||
import dev.euph.engine.managers.Window;
|
|
||||||
import dev.euph.engine.render.Material;
|
|
||||||
import dev.euph.engine.resources.Texture;
|
|
||||||
import dev.euph.engine.resources.TexturedMesh;
|
|
||||||
import dev.euph.engine.resources.loader.MeshLoader;
|
|
||||||
import dev.euph.engine.resources.loader.TextureLoader;
|
|
||||||
import dev.euph.engine.ecs.Scene;
|
|
||||||
import dev.euph.engine.util.Path;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
Window window = new Window(1200, 720, "Test Window");
|
|
||||||
Scene scene = new Scene();
|
|
||||||
|
|
||||||
InputStream fragmentShaderStream = Main.class.getResourceAsStream("/dev/euph/game/shader/default.fs.glsl");
|
|
||||||
InputStream vertexShaderStream = Main.class.getResourceAsStream("/dev/euph/game/shader/default.vs.glsl");
|
|
||||||
ShaderManager shaderManager = new ShaderManager(vertexShaderStream, fragmentShaderStream);
|
|
||||||
|
|
||||||
//Creating a Camera
|
|
||||||
Entity cameraEntity = new Entity("Camera");
|
|
||||||
Transform cameraTransform = new Transform(
|
|
||||||
new Vector3f(0, 0, 3),
|
|
||||||
new Vector3f(0f, 0f , 0),
|
|
||||||
new Vector3f(1)
|
|
||||||
);
|
|
||||||
Camera cameraComponent = new Camera(window);
|
|
||||||
CameraController cameraControllerComponent = new CameraController();
|
|
||||||
|
|
||||||
cameraEntity.addComponent(cameraTransform);
|
|
||||||
cameraEntity.addComponent(cameraComponent);
|
|
||||||
cameraEntity.addComponent(cameraControllerComponent);
|
|
||||||
scene.addEntityToScene(cameraEntity);
|
|
||||||
|
|
||||||
//creating a light
|
|
||||||
Entity lightEntity = new Entity("DirectionalLight");
|
|
||||||
Transform directionalLightTransform = new Transform(
|
|
||||||
new Vector3f(0),
|
|
||||||
new Vector3f(-0.5f, -1.0f, -0.5f),
|
|
||||||
new Vector3f(1)
|
|
||||||
);
|
|
||||||
DirectionalLight directionalLightComponent = new DirectionalLight(Color.WHITE);
|
|
||||||
lightEntity.addComponent(directionalLightTransform);
|
|
||||||
lightEntity.addComponent(directionalLightComponent);
|
|
||||||
scene.addEntityToScene(lightEntity);
|
|
||||||
|
|
||||||
//creating a light
|
|
||||||
Entity lightEntity2 = new Entity("PointLight");
|
|
||||||
Transform pointLightTransform = new Transform(
|
|
||||||
new Vector3f(0),
|
|
||||||
new Vector3f(-0.5f, -1.0f, -0.5f),
|
|
||||||
new Vector3f(1)
|
|
||||||
);
|
|
||||||
PointLight pointLightComponent = new PointLight(Color.red, new Vector3f(10));
|
|
||||||
lightEntity2.addComponent(pointLightTransform);
|
|
||||||
lightEntity2.addComponent(pointLightComponent);
|
|
||||||
scene.addEntityToScene(lightEntity2);
|
|
||||||
|
|
||||||
|
|
||||||
//creating some Mesh Entity
|
|
||||||
Entity modelEntity = new Entity("Model");
|
|
||||||
TexturedMesh modelMesh = MeshLoader.loadTexturedMesh(Path.RES + "human_rigged.obj");
|
|
||||||
Material modelMaterial = new Material(shaderManager.getShader("default"));
|
|
||||||
modelMaterial.setColor(new Color(7, 77, 255, 255));
|
|
||||||
|
|
||||||
Texture uvTexture = TextureLoader.loadTexture("uv.png");
|
|
||||||
modelMaterial.setAlbedoTexture(uvTexture);
|
|
||||||
|
|
||||||
Transform modelTransform = new Transform(
|
|
||||||
new Vector3f(0, -4, -6),
|
|
||||||
new Vector3f(0, 0, 0),
|
|
||||||
new Vector3f(3, 3, 3)
|
|
||||||
);
|
|
||||||
|
|
||||||
MeshRenderer modelMeshRender = new MeshRenderer(modelMesh, modelMaterial);
|
|
||||||
modelEntity.addComponent(modelTransform);
|
|
||||||
modelEntity.addComponent(modelMeshRender);
|
|
||||||
scene.addEntityToScene(modelEntity);
|
|
||||||
|
|
||||||
//initialize renderer
|
|
||||||
ForwardRenderer forwardRenderer = new ForwardRenderer(scene, shaderManager);
|
|
||||||
forwardRenderer.activeCamera = cameraComponent;
|
|
||||||
forwardRenderer.init();
|
|
||||||
|
|
||||||
//logic
|
|
||||||
//float rotationAngle = 0;
|
|
||||||
//float rotationSpeed = 0.5f;
|
|
||||||
|
|
||||||
|
|
||||||
scene.start();
|
|
||||||
|
|
||||||
//Base gameloop
|
|
||||||
while (!window.isCloseRequested()) {
|
|
||||||
scene.update(window.getTime().getDeltaTime());
|
|
||||||
|
|
||||||
//rotationAngle += rotationSpeed;
|
|
||||||
// Keep the rotation angle within a valid range (e.g., 0 to 360 degrees)
|
|
||||||
//rotationAngle %= 360;
|
|
||||||
|
|
||||||
// Update the model's rotation based on the new angle
|
|
||||||
//modelEntity.getComponent(Transform.class).setRotation(new Vector3f(0, rotationAngle, 0));
|
|
||||||
|
|
||||||
forwardRenderer.render();
|
|
||||||
window.updateWindow();
|
|
||||||
}
|
|
||||||
window.destroyWindowy();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue