began working on 3D model loading and textured rendering

This commit is contained in:
Harrison Corlett 2026-05-19 13:33:10 +01:00
parent f9b18a830a
commit 86325c1611
41 changed files with 522 additions and 53 deletions

View file

@ -70,6 +70,8 @@ dependencies {
implementation "org.lwjgl:lwjgl-meshoptimizer::$lwjglNatives"
implementation "org.lwjgl:lwjgl-openal::$lwjglNatives"
implementation "org.lwjgl:lwjgl-rpmalloc::$lwjglNatives"
implementation "org.lwjgl:lwjgl-remotery:${lwjglVersion}"
runtimeOnly "org.lwjgl:lwjgl-remotery:${lwjglVersion}:${lwjglNatives}"
implementation "org.lwjgl:lwjgl-shaderc::$lwjglNatives"
implementation "org.lwjgl:lwjgl-stb::$lwjglNatives"
implementation "org.lwjgl:lwjgl-tootle::$lwjglNatives"
@ -80,4 +82,6 @@ dependencies {
implementation "org.lwjglx:lwjgl3-awt:${lwjgl3awtVersion}"
implementation "com.code-disaster.steamworks4j:steamworks4j:${steamworks4jVersion}"
implementation "com.code-disaster.steamworks4j:steamworks4j-server:${steamworks4jserverVersion}"
implementation 'com.google.code.gson:gson:2.14.0'
implementation "org.jcommander:jcommander:3.0"
}

View file

@ -10,29 +10,31 @@ import static org.lwjgl.stb.STBImage.stbi_load;
public class GLFWImageParser {
private ByteBuffer image;
private int GLFW_Width, GLFW_Height;
private int GLFW_Width, GLFW_Height, ImageChannels;
GLFWImageParser(int width, int height, ByteBuffer image) {
GLFWImageParser(int width, int height, ByteBuffer image, int ImageChannels) {
this.image = image;
this.GLFW_Height = height;
this.GLFW_Width = width;
this.ImageChannels = ImageChannels;
}
public static GLFWImageParser LoadImage(String path) {
ByteBuffer image;
int Width, Height;
int Width, Height, Channels;
try (MemoryStack MemStack = MemoryStack.stackPush()) {
IntBuffer comp = MemStack.mallocInt(1);
IntBuffer channels = MemStack.mallocInt(1);
IntBuffer width = MemStack.mallocInt(1);
IntBuffer height = MemStack.mallocInt(1);
image = stbi_load(path, width, height, comp, 4);
image = stbi_load(path, width, height, channels, 4);
if (image == null) {
System.out.println("Could not load or find image inputted.");
}
Width = width.get();
Height = height.get();
Channels = channels.get(0);
}
return new GLFWImageParser(Width, Height, image);
return new GLFWImageParser(Width, Height, image, Channels);
}
public ByteBuffer GetImage() {
@ -46,4 +48,7 @@ public class GLFWImageParser {
public int GetHeight() {
return GLFW_Height;
}
public int GetChannels() {
return ImageChannels;
}
}

View file

@ -1,9 +1,8 @@
package net.halbear.Terrain4J.EngineCore.Display;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelsCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer;

View file

@ -48,7 +48,12 @@ public class EngineConfig {
private float FOV = 60f;
private float zFarPlane;
private float zNearPlane;
private boolean GPU_SUPPORTS_LOGGING = true;
public void GPU_Does_Not_Support_Logging(){
GPU_SUPPORTS_LOGGING = false;
}
public boolean doesGPUSupportLogging(){return GPU_SUPPORTS_LOGGING;}
private EngineConfig() {
var EngineConfigVar = new Properties();

View file

@ -0,0 +1,220 @@
package net.halbear.Terrain4J.EngineCore.Logic.Rendering;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.GsonBuilder;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MaterialData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MeshData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelBinaryData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.joml.Vector4f;
import org.lwjgl.PointerBuffer;
import org.lwjgl.assimp.*;
import org.lwjgl.system.MemoryStack;
import org.tinylog.Logger;
import java.io.*;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.lwjgl.assimp.Assimp.*;
public class ModelCompiler {
private static final Pattern EMBED_TEXT_ID = Pattern.compile("\\*([0-9)]+)");
private static final int FLAGS = aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices
| aiProcess_Triangulate | aiProcess_FixInfacingNormals | aiProcess_CalcTangentSpace
| aiProcess_PreTransformVertices;
@Parameter(names = "-m", description = "Model Path", required = true)
private String ModelPath;
public static void main(String[] args){
var Main = new ModelCompiler();
var jCommander = JCommander.newBuilder().addObject(Main).build();
try{
jCommander.parse(args);
Main.Process();
} catch(ParameterException exception){
jCommander.usage();
} catch(IOException exception){
Logger.error("Error Compiling model",exception);
}
}
private void Process() throws IOException{
Logger.debug("Loading 3D Model Data from [{}]", ModelPath);
var ModelSourceFile = new File(ModelPath);
if (!ModelSourceFile.exists()) throw new RuntimeException("Model path does not exist->["+ModelPath+"]");
AIScene aiScene = aiImportFile(ModelPath, FLAGS);
if (aiScene == null) throw new RuntimeException("Model ["+ModelPath+"] could not be loaded");
String ModelID = ModelSourceFile.getName();
if(ModelID.contains(".")) ModelID = ModelID.substring(0, ModelID.lastIndexOf('.'));
ModelBinaryData BinaryData = new ModelBinaryData(ModelPath);
int MaterialCount = aiScene.mNumMaterials();
Logger.debug("Number of materials: {}",MaterialCount);
List<MaterialData> MaterialList = new ArrayList<>();
File ParentDir = ModelSourceFile.getParentFile();
for(int i = 0; i < MaterialCount; i++){
var aiMaterial = AIMaterial.create(aiScene.mMaterials().get(i));
MaterialData material = ProcessMaterial(aiScene, aiMaterial, ModelID, ParentDir.getPath(), i);
MaterialList.add(material);
}
int MeshCount = aiScene.mNumMeshes();
PointerBuffer aiMeshes = aiScene.mMeshes();
List<MeshData> meshList = new ArrayList<>();
for(int i = 0; i < MeshCount; i++){
AIMesh aiMesh = AIMesh.create(aiMeshes.get(i));
MeshData newMesh = ProcessMesh(aiMesh, MaterialList, i,BinaryData);
meshList.add(newMesh);
}
var model = new ModelData(ModelID, meshList, BinaryData.GetVertexFilePath(), BinaryData.GetIndexFilePath());
String CreateModelFile = ModelPath.substring(0,ModelPath.lastIndexOf('.')) + ".json";
Writer writer = new FileWriter(CreateModelFile);
var gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).setPrettyPrinting().create();
gson.toJson(model, writer);
writer.flush();
writer.close();
Logger.info("Generated Model File [{}]", CreateModelFile);
String CreateMaterialFile = ModelPath.substring(0,ModelPath.lastIndexOf('.')) + "_mat.json";
writer = new FileWriter(CreateMaterialFile);
gson.toJson(MaterialList,writer);
writer.flush();
writer.close();
BinaryData.Close();
aiReleaseImport(aiScene);
Logger.info("Generated Vertex File [{}] and Index File [{}]",BinaryData.GetVertexFilePath(),BinaryData.GetIndexFilePath());
}
private MaterialData ProcessMaterial(AIScene aiScene, AIMaterial aiMaterial, String ModelName, String BaseDirectory, int Position) throws IOException{
Vector4f diffuse = new Vector4f();
AIColor4D colour = AIColor4D.create();
int Result = aiGetMaterialColor(aiMaterial, AI_MATKEY_COLOR_DIFFUSE, aiTextureType_NONE, 0, colour);
if(Result == aiReturn_SUCCESS){
diffuse.set(colour.r(), colour.g(), colour.b(), colour.a());
}
String DiffuseTexture = ProcessTexture(aiScene, aiMaterial, BaseDirectory, aiTextureType_DIFFUSE);
return new MaterialData(ModelName + "-mat-" + Position,DiffuseTexture,diffuse);
}
private MeshData ProcessMesh(AIMesh aiMesh, List<MaterialData> MaterialList, int MeshPosition, ModelBinaryData BinaryData) throws IOException {
List<Float> vertices = ProcessVertices(aiMesh);
List<Float> textureCoordinates = ProcessTextureCoords(aiMesh);
List<Integer> indices = ProcessIndices(aiMesh);
int VerticesSize = vertices.size();
if(textureCoordinates.isEmpty()){textureCoordinates = Collections.nCopies((VerticesSize/3)*2,0.0f);}
DataOutputStream VerticesOutput = BinaryData.GetVertexOutput();
int Rows = VerticesSize/3;
int VertexIncrement = (VerticesSize + textureCoordinates.size()) * VulkanUtils.FLOAT_SIZE;
for(int row = 0; row < Rows; row++){
int StartPos = row * 3;
int StartTextureCoord = row * 2;
VerticesOutput.writeFloat(vertices.get(StartPos));
VerticesOutput.writeFloat(vertices.get(StartPos + 1));
VerticesOutput.writeFloat(vertices.get(StartPos + 2));
VerticesOutput.writeFloat(textureCoordinates.get(StartTextureCoord));
VerticesOutput.writeFloat(textureCoordinates.get(StartTextureCoord + 1));
}
DataOutputStream IndexOutput = BinaryData.GetIndexOutput();
int IndexSize = indices.size();
int IndexIncrement = IndexSize * VulkanUtils.INT_SIZE;
for(int Index = 0; Index < IndexSize; Index++){
IndexOutput.writeInt(indices.get(Index));
}
String MeshID = aiMesh.mName().dataString() + "_" + MeshPosition;
int MaterialIndex = aiMesh.mMaterialIndex();
String MaterialID = "";
if(MaterialIndex >= 0 && MaterialIndex < MaterialList.size()){
MaterialID = MaterialList.get(MaterialIndex).ID();
}
var meshData = new MeshData(MeshID, MaterialID, BinaryData.GetVertexOffset(), VertexIncrement,BinaryData.GetIndexOffset(), IndexIncrement);
BinaryData.IncrementVertexOffset(VertexIncrement);
BinaryData.IncrementIndexOffset(IndexIncrement);
return meshData;
}
private static List<Float> ProcessTextureCoords(AIMesh aiMesh){
List<Float> TextureCoords = new ArrayList<>();
AIVector3D.Buffer aiTextureCoords = aiMesh.mTextureCoords(0);
int CoordinateCount = aiTextureCoords != null ? aiTextureCoords.remaining() : 0;
for(int i = 0; i < CoordinateCount; i++){
AIVector3D TextureCoord = aiTextureCoords.get();
TextureCoords.add(TextureCoord.x());
TextureCoords.add(1 - TextureCoord.y());
}
return TextureCoords;
}
private List<Integer> ProcessIndices(AIMesh aiMesh){
List<Integer> Indices = new ArrayList<>();
int FaceCount = aiMesh.mNumFaces();
AIFace.Buffer aiFaces = aiMesh.mFaces();
for(int i = 0; i < FaceCount; i++){
AIFace aiFace = aiFaces.get(i);
IntBuffer buffer = aiFace.mIndices();
while(buffer.remaining() > 0){
Indices.add(buffer.get());
}
}
return Indices;
}
private List<Float> ProcessVertices(AIMesh aiMesh){
List<Float> vertices = new ArrayList<>();
AIVector3D.Buffer aiVertices = aiMesh.mVertices();
while(aiVertices.remaining() > 0){
AIVector3D aiVertex = aiVertices.get();
vertices.add(aiVertex.x());
vertices.add(aiVertex.y());
vertices.add(aiVertex.z());
}
return vertices;
}
private String ProcessTexture(AIScene aiScene, AIMaterial aiMaterial, String BaseDirectory,int TextType ) throws IOException{
String TexturePath;
try(var MemStack = MemoryStack.stackPush()){
int EmbeddedTextureCount = aiScene.mNumTextures();
AIString aiTexturePath = AIString.calloc(MemStack);
aiGetMaterialTexture(aiMaterial,TextType,0,aiTexturePath,(IntBuffer) null,null,null,null, null,null);
TexturePath = aiTexturePath.dataString();
if(TexturePath != null && !TexturePath.isEmpty()){
Matcher matcher = EMBED_TEXT_ID.matcher(TexturePath);
int EmbeddedTextureIndex = matcher.matches() && matcher.groupCount() > 0 ? Integer.parseInt(matcher.group(1)) : -1;
if (EmbeddedTextureIndex >= 0 && EmbeddedTextureIndex < EmbeddedTextureCount){
var aiTexture = AITexture.create(aiScene.mTextures().get(EmbeddedTextureIndex));
String BaseFile = aiTexture.mFilename().dataString() + ".png";
TexturePath = BaseDirectory + File.separator + BaseFile;
Logger.info("Dumping Texture File to [{}]",TexturePath);
var Channel = FileChannel.open(Path.of(TexturePath), StandardOpenOption.CREATE,StandardOpenOption.WRITE);
Channel.write(aiTexture.pcDataCompressed());
} else{
TexturePath = BaseDirectory + File.separator + new File(TexturePath).getName();
}
}
return TexturePath;
}
}
}

View file

@ -0,0 +1,44 @@
package net.halbear.Terrain4J.EngineCore.Logic.Rendering;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.GsonBuilder;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.MaterialData;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.ModelData;
import org.tinylog.Logger;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ModelLoader { // Model Utility class kinda like a model version of VulkanUtils
public ModelLoader(){
}
public static List<MaterialData> LoadMaterials(String Path){
Logger.debug("Loading Materials From [{}]",Path);
var Result = new ArrayList<MaterialData>();
try{
String content = new String(Files.readAllBytes(Paths.get(Path)));
var gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
Result.addAll(Arrays.asList(gson.fromJson(content,MaterialData[].class)));
}catch(Exception e){
throw new RuntimeException(e);
}
return Result;
}
public static ModelData LoadModel(String Path){
Logger.debug("Loading Models From [{}]",Path);
ModelData Result;
try{
String content = new String(Files.readAllBytes(Paths.get(Path)));
var gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
Result = gson.fromJson(content,ModelData.class);
}catch(Exception e){
throw new RuntimeException(e);
}
return Result;
}
}

View file

@ -1,6 +1,7 @@
package net.halbear.Terrain4J.EngineCore.Logic;
package net.halbear.Terrain4J.EngineCore.Logic.Rendering;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.PhysicalDevice;

View file

@ -62,17 +62,17 @@ public class GameCore implements GameLogic {
// Back face
7, 6, 4, 7, 4, 5,
};
List<MeshData> meshDataList = new ArrayList<>();
List<ModelData> models = new ArrayList<>();
var modelID = "CubeModel";
MeshData meshData = new MeshData("cube_mesh", positions, textCoords, indices);
List<MeshData> meshDataList = new ArrayList<>();
meshDataList.add(meshData);
ModelData modelData = new ModelData(modelID, meshDataList);
List<ModelData> models = new ArrayList<>();
models.add(modelData);
for(int i = 0; i < 100000; i++) {
for(int i = 0; i < 1000; i++) {
angles.add((float)Math.floor(Math.random() * 360));
Velocities.add(new Vector3f((float)(-2 + (4* Math.random())),(float)(-2 + (4* Math.random())),(float)(-2 + (4* Math.random()))));
CubeActors.add(new Actor("CubeActor", modelID, new Vector3f(-50f + (float)Math.floor(Math.random() *100), -50f + (float)Math.floor(Math.random() * 100), -2f + (float)Math.floor(Math.random() * -100))));
CubeActors.add(new Actor("CubeActor", modelID, new Vector3f(-25f + (float)Math.floor(Math.random() *50), -25f + (float)Math.floor(Math.random() * 50), -2f + (float)Math.floor(Math.random() * -15))));
engineInstance.scene().AddActor(CubeActors.get(i));
}
return new InitData(models);
@ -86,7 +86,7 @@ public class GameCore implements GameLogic {
@Override
public void Update(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
float DeltaTime = (float)(FrameDiffNanoSeconds/16000000.0);
for(int i = 0; i < angles.size(); i++) {
for(int i = 0; i < angles.size()/2; i++) {
angles.set(i, (angles.get(i) + 1.0f* DeltaTime) % 360);
CubeActors.get(i).GetRotation().identity().rotateAxis((float) EngineConfig.DegToRad * angles.get(i), rotatingAngle);
CubeActors.get(i).GetPosition().add((Velocities.get(i).x/100.0f) * DeltaTime,(Velocities.get(i).y/100.0f) * DeltaTime,(Velocities.get(i).z/100.0f) * DeltaTime);
@ -96,6 +96,12 @@ public class GameCore implements GameLogic {
@Override
public void UpdateFastThread(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
float DeltaTime = (float)(FrameDiffNanoSeconds/16000000.0);
for(int i = angles.size()/2; i < angles.size(); i++) {
angles.set(i, (angles.get(i) + 1.0f* DeltaTime) % 360);
CubeActors.get(i).GetRotation().identity().rotateAxis((float) EngineConfig.DegToRad * angles.get(i), rotatingAngle);
CubeActors.get(i).GetPosition().add((Velocities.get(i).x/100.0f) * DeltaTime,(Velocities.get(i).y/100.0f) * DeltaTime,(Velocities.get(i).z/100.0f) * DeltaTime);
CubeActors.get(i).UpdateModelMatrix();
}
}
}

View file

@ -2,7 +2,9 @@ package net.halbear.Terrain4J.EngineCore.Main;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.*;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.Profiling.CPUMonitor;
import net.halbear.Terrain4J.EngineCore.Threads.FastTickThread;
import net.halbear.Terrain4J.EngineCore.Threads.MainThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
@ -24,6 +26,7 @@ public class PrimaryRuntime {
private static long FrameAccuracy;
private static long OriginalFrameAccuracy;
private static boolean CappedToTickRate = false;
public final CPUMonitor cpuProfiler;
public PrimaryRuntime(String windowTitle, GameLogic appLogic) {
window = new Window(windowTitle);
@ -32,6 +35,7 @@ public class PrimaryRuntime {
PrimaryThread = new MainThread(engineInstance, appLogic);
FastThread = new FastTickThread(engineInstance, appLogic);
DrawingThread = new RenderThread(window, engineInstance, initData, appLogic);
cpuProfiler = new CPUMonitor();
Thread.currentThread().setName("Terrain4J Primary Runtime Thread");
}
public static void UpdateFrameAccuracy(){
@ -90,6 +94,7 @@ public class PrimaryRuntime {
EngineConfig.FrameRate_PRIMARYTHREAD = Frames;
EngineConfig.TPS = TickFrames;
Logger.info("Primary Thread FPS: " + Frames + " TPS: " + TickFrames + " Window Draw FPS: " + EngineConfig.RENDERFRAMES);
Logger.info(cpuProfiler.LogCPUUsage() + "\t");
Frames = 0;
TickFrames = 0;
EngineConfig.RENDERFRAMES = 0;

View file

@ -0,0 +1,19 @@
package net.halbear.Terrain4J.EngineCore.Profiling;
import org.tinylog.Logger;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.Method;
public class CPUMonitor {
private final OperatingSystemMXBean OSBean;
public CPUMonitor(){
OSBean = ManagementFactory.getOperatingSystemMXBean();
}
public String LogCPUUsage(){
return "";
}
}

View file

@ -0,0 +1,42 @@
package net.halbear.Terrain4J.EngineCore.Profiling;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkCommandBuffer;
import org.lwjgl.vulkan.VkDevice;
import org.lwjgl.vulkan.VkQueryPoolCreateInfo;
import java.nio.LongBuffer;
import static org.lwjgl.vulkan.VK13.*;
public class GPUProfiler {
private long RemoteryInstance;
public GPUProfiler(){
}
public void Initiate(Device device){
try(var MemStack = MemoryStack.stackPush()){
}
}
public void reset(VkCommandBuffer CommandBuffer){
}
public void StartFrame(VkCommandBuffer CommandBuffer){
}
public void EndFrame(VkCommandBuffer CommandBuffer){
}
public String LogGPUUsage(Device device){
return "";
}
public void CleanUp(Device device){
}
}

View file

@ -3,6 +3,7 @@ package net.halbear.Terrain4J.EngineCore.Threads;
import net.halbear.Terrain4J.EngineCore.Display.Render;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.*;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import org.tinylog.Logger;

View file

@ -0,0 +1,25 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
import net.halbear.Terrain4J.EngineCore.Display.GLFWImageParser;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.ImageSrc;
import org.lwjgl.system.MemoryStack;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import static org.lwjgl.stb.STBImage.stbi_image_free;
import static org.lwjgl.stb.STBImage.stbi_load;
public class GraphUtils {
private GraphUtils(){ }
public static void CleanImageData(ImageSrc imageSrc){
stbi_image_free(imageSrc.data());
}
public static ImageSrc LoadImage(String Filename) throws IOException{
GLFWImageParser image = GLFWImageParser.LoadImage(Filename);
return new ImageSrc(image.GetImage(), image.GetWidth(), image.GetHeight(), image.GetChannels());
}
}

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.ImageView;
import static org.lwjgl.vulkan.VK13.*;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;

View file

@ -0,0 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images;
import java.nio.ByteBuffer;
public record ImageSrc(ByteBuffer data, int Width, int Height, int Channels) {
}

View file

@ -0,0 +1,24 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.ImageView;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer;
public class Texture {
private final String ID;
private final int Height;
private final int Width;
private final ImageView imageView;
private boolean RecordedTransition;
private VulkanBuffer stgBuffer;
public Texture(VulkanContext VkCtx, String ID, ImageSrc imageSrc, int ImageFormat){
this.ID = ID;
RecordedTransition = false;
Width = imageSrc.Width();
Height = imageSrc.Height();
//will continue later
}
}

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;

View file

@ -1,6 +1,5 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkShaderModuleCreateInfo;

View file

@ -0,0 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel;
import org.joml.Vector4f;
public record MaterialData(String ID, String TexturePath, Vector4f DiffuseColour) {
}

View file

@ -1,4 +1,4 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel;
public record MeshData(String ID, float[] Positions, float[] textureCoords, int[] Indices) {
public record MeshData(String ID, String MaterialID, int offsetVertex, int VertexSize, int OffsetIndex, int IndexSize) {
}

View file

@ -0,0 +1,35 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel;
import java.io.*;
public class ModelBinaryData {
private final String IndexFilePath;
private final DataOutputStream IndexOutput;
private final String VertexFilePath;
private final DataOutputStream VertexOutput;
private int IndexOffset;
private int VertexOffset;
public ModelBinaryData(String ModelPath) throws FileNotFoundException{
VertexFilePath = ModelPath.substring(0,ModelPath.lastIndexOf('.')) + ".vtx";
VertexOutput = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(VertexFilePath)));
IndexFilePath= ModelPath.substring(0,ModelPath.lastIndexOf('.')) + ".idx";
IndexOutput = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(IndexFilePath)));
}
public void Close() throws IOException {
VertexOutput.close();
IndexOutput.close();
}
public String GetIndexFilePath(){return IndexFilePath;}
public int GetIndexOffset(){return IndexOffset;}
public DataOutputStream GetIndexOutput(){return IndexOutput;}
public String GetVertexFilePath(){return VertexFilePath;}
public int GetVertexOffset(){return VertexOffset;}
public DataOutputStream GetVertexOutput(){return VertexOutput;}
public void IncrementIndexOffset(int Increment){
IndexOffset+=Increment;
}
public void IncrementVertexOffset(int Increment){VertexOffset+=Increment;}
}

View file

@ -2,7 +2,7 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel;
import java.util.List;
public record ModelData(String ID, List<MeshData> Meshes) {
public record ModelData(String ID, List<MeshData> Meshes, String vertexPath, String indexPath) {
}

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandPool;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation.TransferBuffer;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanBuffer;
public record VulkanMesh(String ID, VulkanBuffer VerticesBuffer,VulkanBuffer IndicesBuffer,int IndicesCount) {

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation.Fence;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Queues.Queue;
import org.lwjgl.PointerBuffer;

View file

@ -1,5 +1,5 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkCommandPoolCreateInfo;
import org.tinylog.Logger;

View file

@ -1,13 +1,12 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.PointerBuffer;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.*;
import org.tinylog.Logger;
import java.awt.*;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
@ -51,6 +50,13 @@ public class Device {
.pQueueCreateInfos(QueueCreationInfoBuffer);
PointerBuffer pp = MemStack.mallocPointer(1);
VkPhysicalDeviceProperties deviceProps = VkPhysicalDeviceProperties.malloc();
vkGetPhysicalDeviceProperties(PhysDevice.GetPhysicalDevice(), deviceProps);
if (!deviceProps.limits().timestampComputeAndGraphics()) {
Logger.warn("GPU does not support timestamp queries!");
}
vkCheck(vkCreateDevice(PhysDevice.GetPhysicalDevice(), DeviceCreateInfo, null, pp), "Failed to create device");
VulkanDevice = new VkDevice(pp.get(0), PhysDevice.GetPhysicalDevice(), DeviceCreateInfo);
}
@ -112,6 +118,7 @@ public class Device {
vkDeviceWaitIdle(VulkanDevice);
}
}

View file

@ -1,9 +1,8 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkFenceCreateInfo;
import org.tinylog.Logger;
import java.nio.LongBuffer;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkSemaphoreCreateInfo;
import org.tinylog.Logger;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.VulkanInstance;
import org.lwjgl.PointerBuffer;
import org.lwjgl.system.MemoryStack;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Queues;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation.Fence;
import org.lwjgl.PointerBuffer;
import org.lwjgl.system.MemoryStack;

View file

@ -1,9 +1,8 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen;
import net.halbear.Terrain4J.EngineCore.Display.Render;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.Attachment;
@ -48,6 +47,8 @@ public class SceneRender {//dynamic rendering
private boolean Rb = false;
private boolean Gb = true;
private boolean Bb = false;
public int Frames = 0;
private static String LastGPULog = "";
private static final String FRAGMENT_SHADER_FILE_GLSL = "resources/shaders/scene_fragment.glsl";
private static final String FRAGMENT_SHADER_FILE_SPV = FRAGMENT_SHADER_FILE_GLSL + ".spv";
@ -155,7 +156,6 @@ public class SceneRender {//dynamic rendering
SwapChain swapChain = vulkanContext.GetSwapChain();
long SwapChainImage = swapChain.GetVulkanImageView(ImageIndex).GetVulkanImage();
VkCommandBuffer CommandHandle = commandBuffer.GetVulkanCommandBuffer();
VulkanUtils.ImageBarrier(MemStack, CommandHandle, SwapChainImage,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
@ -227,7 +227,6 @@ public class SceneRender {//dynamic rendering
VK_IMAGE_ASPECT_COLOR_BIT);
}
}
private void SetPushConstants(VkCommandBuffer CmdHandle, Matrix4f ProjectionMatrix, Matrix4f modelMatrix){
ProjectionMatrix.get(PushConstBuffer);
modelMatrix.get(VulkanUtils.MATRIX4X4_SIZE,PushConstBuffer);

View file

@ -1,7 +1,7 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.PhysicalDevice;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.VulkanInstance;
import org.lwjgl.glfw.GLFWVulkan;

View file

@ -1,7 +1,7 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.GPUSynchronisation.Semaphore;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Queues.Queue;

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Structure;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.PointerBuffer;
import org.lwjgl.glfw.GLFWVulkan;
@ -96,6 +96,7 @@ public class VulkanInstance {
if(CrossPlatform){
InstanceInfo.flags(0x00000001);
}
PointerBuffer PointerInstance = MemStack.mallocPointer(1);
vkCheck(vkCreateInstance(InstanceInfo,null,PointerInstance), "Error Creating Vulkan Instance");
vkInstance = new VkInstance(PointerInstance.get(0),InstanceInfo);

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Util;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.Device;
import org.lwjgl.PointerBuffer;
import org.lwjgl.system.MemoryStack;

View file

@ -1,9 +1,8 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Util;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.*;
import org.tinylog.Logger;
import java.util.Locale;

View file

@ -1,20 +1,38 @@
#Default Window dimensions
display_height=480
display_width=640
#the precision of frames, set this to 16000 and it will limit the whole engine to 64FPS
frame_accuracy=0
#event tick rate for game process
master_tick_rate=120
main_thread_tickrate=120
fast_thread_tickrate=240
cap_main_to_tickrate=false
#Vulkan Debugging toggle
vkValidated=true
vsync=true
#This is the name of the GPU you want the engine to use
PhysicalDeviceName=
#Application Properties
#Title that shows up on app window
Software_Title=Terrain4J Game Engine
Software_Icon=ProgramIcon.png
Software_Icon_Path=/WindowResources/Icon/
#Launch Window dimensions
display_height=480
display_width=640
#Engine Core Settings
#the precision of frames, set this to 16000000, and it will limit the whole engine to 64FPS
frame_accuracy=0
throttle_on_unfocus=true
throttle_accuracy=500000000
#event tick rate for game process
master_tick_rate=120
main_thread_tickrate=120
fast_thread_tickrate=240
cap_master_to_tickrate=false
cap_main_to_tickrate=true
cap_fast_to_tickrate=true
#Renderer Properties
Debug_Shaders = false
ShaderRecompiling = true
#Vulkan Debugging toggle
vkValidated=true
vsync=false
#Vsync images
RequestedImages=3
#This is the name of the GPU you want the engine to use
PhysicalDeviceName=
#3D Projection Properties
field_of_view=60.0f
z_near_plane=1.0f
z_far_plane=100.0f