Working towards 3D rendering

This commit is contained in:
Harrison Corlett 2026-05-18 14:55:43 +01:00
parent 71d3cd400b
commit 84020a922b
22 changed files with 486 additions and 42 deletions

View file

@ -16,6 +16,7 @@ import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.SwapCha
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.vulkan.VkCommandBufferSubmitInfo;
import org.lwjgl.vulkan.VkExtent2D;
import org.lwjgl.vulkan.VkSemaphoreSubmitInfo;
import org.tinylog.Logger;
@ -39,6 +40,7 @@ public class Render {
private final SceneRender sceneRender;
private int CurrentFrame;
private final VulkanContext RendererContext;
private boolean Resize = false;
public Render(EngineInstance engineInstance) {
RendererContext = new VulkanContext(engineInstance.window());
@ -62,6 +64,7 @@ public class Render {
}
sceneRender = new SceneRender(RendererContext);
modelsCache = new ModelsCache();
Resize = false;
}
public void Initialise(InitData initData){
@ -105,16 +108,41 @@ public class Render {
var CommandPool = CommandPools[CurrentFrame];
var CommandBuffer = CommandBuffers[CurrentFrame];
RecordingStart(CommandPool, CommandBuffer);
int ImageIndex = swapChain.FetchNextImage(RendererContext.GetDevice(), PresentCompleteSemaphores[CurrentFrame]);
if (ImageIndex < 0){
int ImageIndex;// = swapChain.FetchNextImage(RendererContext.GetDevice(), PresentCompleteSemaphores[CurrentFrame]);
if (Resize || (ImageIndex = swapChain.FetchNextImage(RendererContext.GetDevice(), PresentCompleteSemaphores[CurrentFrame])) < 0){
resize(engineInstance);
return;
}
sceneRender.Render(RendererContext,CommandBuffer, modelsCache,ImageIndex);
sceneRender.Render(engineInstance,RendererContext,CommandBuffer, modelsCache,ImageIndex);
RecordingStop(CommandBuffer);
Submit(CommandBuffer, CurrentFrame, ImageIndex);
swapChain.PresentImage(PresentQueue, RenderCompleteSemaphores[ImageIndex],ImageIndex);
Resize = swapChain.PresentImage(PresentQueue, RenderCompleteSemaphores[ImageIndex],ImageIndex);
CurrentFrame = (CurrentFrame + 1) % VulkanUtils.MAX_IN_FLIGHT;
}
private void resize(EngineInstance engineInstance){
Window window = engineInstance.window();
if(window.getWidth() == 0 && window.getHeight() == 0){
return;
}
Resize = false;
RendererContext.GetDevice().waitIdle();
RendererContext.Resize(window);
Arrays.asList(RenderCompleteSemaphores).forEach(i->i.cleanup(RendererContext));
Arrays.asList(PresentCompleteSemaphores).forEach(i->i.cleanup(RendererContext));
for(int i = 0; i < VulkanUtils.MAX_IN_FLIGHT; i++){
PresentCompleteSemaphores[i] = new Semaphore(RendererContext);
}
for(int i = 0; i < RendererContext.GetSwapChain().GetImageCount(); i++){
RenderCompleteSemaphores[i] = new Semaphore(RendererContext);
}
VkExtent2D extend = RendererContext.GetSwapChain().GetSwapChainExtent();
engineInstance.scene().GetProjection().Resize(extend.width(),extend.height());
sceneRender.Resize(RendererContext);
}
private void ResetFence(int currentFrame){
var fence = Fences[currentFrame];
fence.Reset(RendererContext);

View file

@ -9,6 +9,8 @@ import java.nio.file.Paths;
import java.util.Properties;
public class EngineConfig {
public static final double DegToRad = Math.PI/180.0;
public static final double RadToDeg = 180.0/Math.PI;
private static final long DEFAULT_ACCURACY = 0;
private static final int DEFAULT_TICKRATE = 60;
private static final int DEFAULT_WIDTH = 640;
@ -43,6 +45,10 @@ public class EngineConfig {
private int RequestedImages;
private String IconPath = "/WindowResources/Icon/";
private String IconName = "ProgramIcon.png";
private float FOV = 60f;
private float zFarPlane;
private float zNearPlane;
private EngineConfig() {
var EngineConfigVar = new Properties();
@ -102,6 +108,9 @@ public class EngineConfig {
RequestedImages = Integer.parseInt(EngineConfigVar.getOrDefault("RequestedImages", 3).toString());
ShaderDebug =Boolean.parseBoolean(EngineConfigVar.getOrDefault("Debug_Shaders", false).toString());
RecompileShaders =Boolean.parseBoolean(EngineConfigVar.getOrDefault("ShaderRecompiling", true).toString());
FOV = (float)(DegToRad * Float.parseFloat(EngineConfigVar.getOrDefault("field_of_view", 60.0f).toString()));
zNearPlane = (Float.parseFloat(EngineConfigVar.getOrDefault("z_near_plane", 1.0f).toString()));
zFarPlane = (Float.parseFloat(EngineConfigVar.getOrDefault("z_far_plane", 100.0f).toString()));
Logger.debug("\n\nsuccessfully loaded configuration: \n{}\n\n",EngineConfigVar.toString());
}
else if(!ConfigFile.exists()){
@ -125,6 +134,9 @@ public class EngineConfig {
EngineConfigVar.setProperty("ShaderRecompiling","true");
EngineConfigVar.setProperty("throttle_accuracy","100000");
EngineConfigVar.setProperty("throttle_on_unfocus","true");
EngineConfigVar.setProperty("field_of_view","60");
EngineConfigVar.setProperty("z_near_plane","1");
EngineConfigVar.setProperty("z_far_plane","100");
EngineConfigVar.store(new FileWriter(path.toAbsolutePath().toString() + "/" + FILENAME), "created new properties file");
Logger.debug("Wrote New Config File [{}]", ConfigFile.getAbsolutePath());
} catch (IOException excp2) {
@ -158,7 +170,9 @@ public class EngineConfig {
public boolean RecompileShaders(){
return RecompileShaders;
}
public float GetFOV(){return FOV;}
public float GetZFarPlane(){return zFarPlane;}
public float GetZNearPlane(){return zNearPlane;}
public boolean VkValidated(){
return vkValidated;
}

View file

@ -1,6 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Logic;
import net.halbear.Terrain4J.EngineCore.Main.Scene;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.Display.Window;
public record EngineInstance(Window window, Scene scene) {

View file

@ -28,7 +28,13 @@ public class VulkanContext {
VulkanLayersOpen+=5;
VkPipelineCache = new PipelineCache(device);
}
public void Resize(Window window){
swapChain.cleanup(device);
surface.cleanup(Instance);
var EngCfg = EngineConfig.getInstance();
surface = new Surface(Instance,PhysDevice,window);
swapChain = new SwapChain(window, device, surface, EngCfg.GetRequestedImages(),EngCfg.GetVSYNC());
}
public void cleanup(){
swapChain.cleanup(device);
surface.cleanup(Instance);

View file

@ -2,6 +2,7 @@ package net.halbear.Terrain4J.EngineCore.Main;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.*;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.Threads.FastTickThread;
import net.halbear.Terrain4J.EngineCore.Threads.MainThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;

View file

@ -1,8 +0,0 @@
package net.halbear.Terrain4J.EngineCore.Main;
import net.halbear.Terrain4J.EngineCore.Display.Window;
public class Scene {
public Scene(Window window) {
}
}

View file

@ -0,0 +1,54 @@
package net.halbear.Terrain4J.EngineCore.Main.Scene;
import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
public class Actor{
private final String ID;
private final String ModelID;
private final Matrix4f ModelMatrix;
private final Vector3f Position;
private final Quaternionf Rotation;
private float Scale;
public Actor(String ID, String ModelID, Vector3f Position){
this.ID = ID;
this.ModelID = ModelID;
this.Position = Position;
Scale = 1f;
Rotation = new Quaternionf();
ModelMatrix = new Matrix4f();
UpdateModelMatrix();
}
public void ResetRotation(){
Rotation.x = 0;
Rotation.y = 0;
Rotation.z = 0;
Rotation.w = 1.0f;
}
public final void SetPosition(float x, float y, float z){
Position.x = x;
Position.y = y;
Position.z = z;
UpdateModelMatrix();
}
public void SetScale(float Scale){
this.Scale = Scale;
UpdateModelMatrix();
}
public void UpdateModelMatrix(){
ModelMatrix.translationRotateScale(Position, Rotation, Scale);
}
public String GetID(){return ID;}
public String GetModelID(){return ModelID;}
public Matrix4f GetModelMatrix(){return ModelMatrix;}
public Vector3f GetPosition(){ return Position;}
public Quaternionf GetRotation(){return Rotation;}
public float GetScale(){return Scale;}
}

View file

@ -0,0 +1,27 @@
package net.halbear.Terrain4J.EngineCore.Main.Scene;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.*;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Projection.Project3D;
import java.util.ArrayList;
import java.util.List;
public class Scene {
private final List<Actor> Actors;
private final Project3D Projection;
public Scene(Window window) {
Actors = new ArrayList<>();
var EngConfig = EngineConfig.getInstance();
Projection = new Project3D(EngConfig.GetFOV(),EngConfig.GetZNearPlane(), EngConfig.GetZFarPlane(),
window.getWidth(), window.getHeight());
}
public void AddActor(Actor NewActor){Actors.add(NewActor);}
public List<Actor> GetActors(){return Actors;}
public Project3D GetProjection(){return Projection;}
public void RemoveAllActors(){Actors.clear();}
public void RemoveActor(Actor actor){Actors.removeIf(Actor->Actor.GetID().equals(actor.GetID()));}
}

View file

@ -1,12 +1,9 @@
package net.halbear.Terrain4J.EngineCore.Threads;
import net.halbear.Terrain4J.EngineCore.Display.Window;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Logic.GameLogic;
import net.halbear.Terrain4J.EngineCore.Logic.InitData;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene;
import org.tinylog.Logger;
public class MainThread extends EngineThread {

View file

@ -1,8 +0,0 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering;
public class Image {
public static class ImageData{
}
}

View file

@ -0,0 +1,38 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images;
import net.halbear.Terrain4J.EngineCore.Logic.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DisplayToScreen.ImageView;
import static org.lwjgl.vulkan.VK13.*;
public class Attachment {
private final Image VkImage;
private final ImageView VkImageView;
private boolean DepthAttachment;
public Attachment(VulkanContext VkCtx, int Width, int Height, int Format, int Usage){
var ImageData = new Image.ImageData().Width(Width).Height(Height).Format(Format)
.Usage(Usage | VK_IMAGE_USAGE_SAMPLED_BIT);
VkImage = new Image(VkCtx, ImageData);
int AspectMask = 0;
if((Usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) > 0){
AspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
DepthAttachment = false;
}
if((Usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) > 0){
AspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
DepthAttachment = true;
}
var ImageViewData = new ImageView.ImageViewData().Format(VkImage.GetFormat()).AspectMask(AspectMask);
VkImageView = new ImageView(VkCtx.GetDevice(), VkImage.getVulkanImage(), ImageViewData);
}
public Image GetVkImage(){return VkImage;}
public ImageView GetVkImageView(){return VkImageView;}
public boolean IsDepthAttached(){return DepthAttachment;}
public void CleanUp(VulkanContext VkCtx){
VkImageView.cleanup(VkCtx.GetDevice());
VkImage.CleanUp(VkCtx);
}
}

View file

@ -0,0 +1,116 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images;
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;
import org.lwjgl.vulkan.VkImageCreateInfo;
import org.lwjgl.vulkan.VkMemoryAllocateInfo;
import org.lwjgl.vulkan.VkMemoryRequirements;
import java.nio.LongBuffer;
import static org.lwjgl.vulkan.VK10.*;
import static org.lwjgl.vulkan.VK13.VK_FORMAT_R8G8B8A8_SRGB;
public class Image {
private final int Format;
private final int MipLevels;
private final long VulkanImage;
private final long VulkanMemory;
public Image(VulkanContext VkCtx, ImageData imageData){
try(var MemStack = MemoryStack.stackPush()){
this.Format = imageData.Format;
this.MipLevels = imageData.MipMapLevels;
VkImageCreateInfo vkImageCreateInfo = VkImageCreateInfo.calloc(MemStack)
.sType$Default()
.imageType(VK_IMAGE_TYPE_2D)
.format(Format)
.extent(it -> it
.width(imageData.Width)
.height(imageData.Height)
.depth(1))
.mipLevels(MipLevels)
.arrayLayers(imageData.ArrayLayers)
.samples(imageData.SampleCount)
.initialLayout(VK_IMAGE_LAYOUT_UNDEFINED)
.sharingMode(VK_SHARING_MODE_EXCLUSIVE)
.tiling(VK_IMAGE_TILING_OPTIMAL)
.usage(imageData.Usage);
Device device = VkCtx.GetDevice();
LongBuffer LongPtr = MemStack.mallocLong(1);
VulkanUtils.vkCheck(vkCreateImage(device.FetchVulkanDevice(),vkImageCreateInfo,null,LongPtr),
"Failed to create Vulkan Image");
VulkanImage = LongPtr.get(0);
VkMemoryRequirements memoryRequirements = VkMemoryRequirements.calloc(MemStack);
vkGetImageMemoryRequirements(device.FetchVulkanDevice(), VulkanImage, memoryRequirements);
var MemoryAllocation = VkMemoryAllocateInfo.calloc(MemStack)
.sType$Default()
.allocationSize(memoryRequirements.size())
.memoryTypeIndex(VulkanUtils.MemoryTypeFromProperties(VkCtx, memoryRequirements.memoryTypeBits(),0));
//allocate
VulkanUtils.vkCheck(vkAllocateMemory(device.FetchVulkanDevice(),MemoryAllocation,null,LongPtr)
,"Failed to allocate Memory for Vulkan Image");
VulkanMemory = LongPtr.get(0);
//bind
VulkanUtils.vkCheck(vkBindImageMemory(device.FetchVulkanDevice(),VulkanImage,VulkanMemory,0)
,"Failed to bind memory for Vulkan Image");
}
}
public void CleanUp(VulkanContext VkCtx){
vkDestroyImage(VkCtx.GetDevice().FetchVulkanDevice(), VulkanImage,null);
vkFreeMemory(VkCtx.GetDevice().FetchVulkanDevice(), VulkanMemory,null);
}
public int GetFormat(){return Format;}
public int GetMipLevels(){return MipLevels;}
public long getVulkanImage(){return VulkanImage;}
public static class ImageData{
private int ArrayLayers;
private int Format;
private int Height;
private int Width;
private int MipMapLevels;
private int SampleCount;
private int Usage;
public ImageData(){
Format = VK_FORMAT_R8G8B8A8_SRGB;
MipMapLevels = 1;
SampleCount = 1;
ArrayLayers = 1;
}
public ImageData ArrayLayers(int ArrayLayers){
this.ArrayLayers = ArrayLayers;
return this;
}
public ImageData Format(int Format){
this.Format = Format;
return this;
}
public ImageData Height(int Height){
this.Height = Height;
return this;
}
public ImageData Width(int Width){
this.Width = Width;
return this;
}
public ImageData MipMapLevels(int MipLevel){
this.MipMapLevels = MipLevel;
return this;
}
public ImageData SampleCount(int Samples){
this.SampleCount = Samples;
return this;
}
public ImageData Usage(int Usage){
this.Usage = Usage;
return this;
}
}
}

View file

@ -67,12 +67,42 @@ public class Pipeline {
.pAttachments(BlendAttributeState);
IntBuffer ColourFormats = MemStack.mallocInt(1);
ColourFormats.put(0,BuildInfo.GetColourFormat());
VkPipelineDepthStencilStateCreateInfo DepthStencil = null;
if(BuildInfo.GetDepthFormat() != VK_FORMAT_UNDEFINED){
DepthStencil = VkPipelineDepthStencilStateCreateInfo.calloc(MemStack)
.sType$Default()
.depthTestEnable(true)
.depthWriteEnable(true)
.depthCompareOp(VK_COMPARE_OP_LESS_OR_EQUAL)
.depthBoundsTestEnable(false)
.stencilTestEnable(false);
}
var RendererCreateInfo = VkPipelineRenderingCreateInfo.calloc(MemStack)
.sType$Default()
.colorAttachmentCount(1)
.pColorAttachmentFormats(ColourFormats);
if(DepthStencil != null){RendererCreateInfo.depthAttachmentFormat(BuildInfo.GetDepthFormat());}
VkPushConstantRange.Buffer VkPushConstRangeBuffer = null;
PushConstantsRange[] PushConstRanges = BuildInfo.GetPushConstantRanges();
int PushConstCount = PushConstRanges.length != 0 ? PushConstRanges.length : 0;
if(PushConstCount > 0){
VkPushConstRangeBuffer = VkPushConstantRange.calloc(PushConstCount,MemStack);
for(int i = 0; i < PushConstCount; i++){
PushConstantsRange pushConstantsRange = PushConstRanges[i];
VkPushConstRangeBuffer.get(i)
.stageFlags(pushConstantsRange.Stage())
.offset(pushConstantsRange.Offset())
.size(pushConstantsRange.Size());
}
}
var PipelineLayoutCreateInfoPtr = VkPipelineLayoutCreateInfo.calloc(MemStack)
.sType$Default();
.sType$Default()
.pPushConstantRanges(VkPushConstRangeBuffer);
VulkanUtils.vkCheck(vkCreatePipelineLayout(device.FetchVulkanDevice(), PipelineLayoutCreateInfoPtr,null,longPtr)
,"Unable to create new pipeline layout");
VulkanPipelineLayout = longPtr.get(0);
@ -90,6 +120,9 @@ public class Pipeline {
.pDynamicState(VulkanPipelineDynamicStateCreateInfo)
.layout(VulkanPipelineLayout)
.pNext(RendererCreateInfo);
if(DepthStencil != null){PipelineCreateInfo.pDepthStencilState(DepthStencil);}
VulkanUtils.vkCheck(vkCreateGraphicsPipelines(device.FetchVulkanDevice(),
VkCtx.GetVkPipelineCache().GetVkPipelineCache(), PipelineCreateInfo,
null, longPtr),"Could not create new pipeline");

View file

@ -2,16 +2,35 @@ package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule;
import org.lwjgl.vulkan.VkPipelineVertexInputStateCreateInfo;
import org.lwjgl.vulkan.VkPushConstantRange;
import static org.lwjgl.vulkan.VK10.VK_FORMAT_UNDEFINED;
public class PipelineBuildInfo {
private final int ColourFormat;
private final ShaderModule[] ShaderModules;
private final VkPipelineVertexInputStateCreateInfo VertexInput;
private int DepthFormat;
private PushConstantsRange[] PushConstRanges;
public PipelineBuildInfo(ShaderModule[] shaderModules, VkPipelineVertexInputStateCreateInfo VertexInput, int ColourFormat){
this.ColourFormat = ColourFormat;
this.ShaderModules = shaderModules;
this.VertexInput = VertexInput;
DepthFormat = VK_FORMAT_UNDEFINED;
}
public PipelineBuildInfo SetDepthFormat(int DepthFormat){
this.DepthFormat = DepthFormat;
return this;
}
public PipelineBuildInfo SetPushConstantRanges(PushConstantsRange[] PushConstRanges){
this.PushConstRanges = PushConstRanges;
return this;
}
public PushConstantsRange[] GetPushConstantRanges(){return PushConstRanges;}
public int GetDepthFormat(){return DepthFormat;}
public int GetColourFormat(){return ColourFormat;}
public ShaderModule[] GetShaderModules(){return ShaderModules;}
public VkPipelineVertexInputStateCreateInfo GetVertexInputStateCreateInfo(){return VertexInput;}

View file

@ -0,0 +1,4 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline;
public record PushConstantsRange(int Stage, int Offset, int Size) {
}

View file

@ -0,0 +1,33 @@
package net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Projection;
import org.joml.Matrix4f;
public class Project3D {
private final float FOV; // IT IS IN RADIANS!! REMEEEEMMMBER RADIANS!!!! PI/180!!!!!
// there is a universal DegToRad conversion variable one can use in EngineConfig if they need to convert it
private final Matrix4f ProjectionMatrix;
private final float ZFarPlane;
private final float ZNearPlane;
public Project3D(float FOV, float zNear, float zFar, int Width, int Height){
this.FOV = FOV;
this.ZFarPlane = zFar;
this.ZNearPlane = zNear;
ProjectionMatrix = new Matrix4f();
Resize(Width, Height);
}
public float GetFOV(){
return FOV;
}
public float GetFarZ(){
return ZFarPlane;
}
public float GetNearZ(){
return ZNearPlane;
}
public void Resize(int Width, int Height){
ProjectionMatrix.identity();
ProjectionMatrix.perspective(FOV, (float)Width/(float)Height, ZNearPlane, ZFarPlane, true);
}
}

View file

@ -9,7 +9,8 @@ import static org.lwjgl.vulkan.VK13.VK_FORMAT_R32G32B32_SFLOAT;
import static org.lwjgl.vulkan.VK13.VK_VERTEX_INPUT_RATE_VERTEX;
public class VertexBufferStructure {
private final int NUMBER_OF_ATTRIBUTES = 1;
public static final int TEXT_COORD_COMPONENTS = 2;
private static final int NUMBER_OF_ATTRIBUTES = 2;
private final int POSITION_COMPONENTS = 3;
private final VkPipelineVertexInputStateCreateInfo VertexInput;
@ -23,14 +24,25 @@ public class VertexBufferStructure {
int i =0;
int Offset = 0;
//vertex position
VertexInputAttributes.get(i)
.binding(0)
.location(i)
.format(VK_FORMAT_R32G32B32_SFLOAT)
.offset(Offset);
i++;
Offset += POSITION_COMPONENTS * VulkanUtils.FLOAT_SIZE;
//texture coordinates
VertexInputAttributes.get(i)
.binding(0)
.location(i)
.format(VK_FORMAT_R32G32B32_SFLOAT)
.offset(Offset);
int Stride = Offset + TEXT_COORD_COMPONENTS * VulkanUtils.FLOAT_SIZE;
VertexInputBindings.get(0)
.binding(0)
.stride(POSITION_COMPONENTS * VulkanUtils.FLOAT_SIZE)
.stride(Stride)
.inputRate(VK_VERTEX_INPUT_RATE_VERTEX);
VertexInput
.sType$Default()

View file

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

View file

@ -54,7 +54,11 @@ public class ModelsCache {
private static TransferBuffer CreateVerticesBuffer(VulkanContext VkCtx, MeshData meshData){
float[] Positions = meshData.Positions();
int ElementCount = Positions.length;
float[] TextureCoords = meshData.textureCoords();
if(TextureCoords == null || TextureCoords.length == 0){
TextureCoords = new float[(Positions.length/3) * 2];
}
int ElementCount = Positions.length + TextureCoords.length;
int BufferSize = ElementCount * VulkanUtils.FLOAT_SIZE;
var SrcBuffer = new VulkanBuffer(VkCtx,BufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
@ -66,9 +70,12 @@ public class ModelsCache {
int Rows = Positions.length / 3;
for(int Row = 0; Row < Rows; Row++){
int StartPos = Row * 3;
int StartTexCoordPos = Row * 2;
data.put(Positions[StartPos]);
data.put(Positions[StartPos+1]);
data.put(Positions[StartPos+2]);
data.put(TextureCoords[StartTexCoordPos]);
data.put(TextureCoords[StartTexCoordPos + 1]);
}
SrcBuffer.UnMapMemory(VkCtx);
return new TransferBuffer(SrcBuffer,DstBuffer);

View file

@ -1,11 +1,11 @@
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.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Images.Attachment;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.Pipeline;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineBuildInfo;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PipelineCache;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Pipeline.PushConstantsRange;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderCompiler;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.Shader.ShaderModule;
import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VertexBufferStructure;
@ -15,10 +15,11 @@ import net.halbear.Terrain4J.EngineCore.Vulkan.Rendering.VkModel.VulkanModel;
import net.halbear.Terrain4J.EngineCore.Vulkan.Structure.DeviceLayers.CommandBuffer;
import net.halbear.Terrain4J.EngineCore.Vulkan.Util.VulkanUtils;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.util.shaderc.Shaderc;
import org.lwjgl.vulkan.*;
import org.tinylog.Logger;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.util.Arrays;
@ -26,7 +27,12 @@ import static org.lwjgl.vulkan.KHRSwapchain.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
import static org.lwjgl.vulkan.KHRSynchronization2.VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR;
import static org.lwjgl.vulkan.VK13.*;
public class SceneRender { //dynamic rendering;
public class SceneRender {//dynamic rendering
private static final int DEPTH_FORMAT = VK_FORMAT_D16_UNORM;
private final VkClearValue ClearValueDepth;
private final ByteBuffer PushConstBuffer;
private Attachment[] AttDepth;
private VkRenderingAttachmentInfo[] AttInfoDepth;
private VkClearValue ClearValueColour;
private VkRenderingAttachmentInfo.Buffer[] AttachmentInfoColour;
private VkRenderingInfo[] RenderInfo;
@ -44,14 +50,47 @@ public class SceneRender { //dynamic rendering;
private final Pipeline VkPipeline;
public SceneRender(VulkanContext vulkanContext){
ClearValueDepth = VkClearValue.calloc().color(c->c.float32(0,1.0f));
AttDepth = createDepthAttachments(vulkanContext);
AttInfoDepth = createDepthAttachmentsInfo(vulkanContext, AttDepth, ClearValueDepth);
ClearValueColour = VkClearValue.calloc().color(c->c.float32(0,R).float32(1,G).float32(2,B).float32(3,1.0f));
AttachmentInfoColour = CreateColourAttachmentsInfo(vulkanContext,ClearValueColour);
RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour);
RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour, AttInfoDepth);
PushConstBuffer = MemoryUtil.memAlloc(VulkanUtils.MATRIX4X4_SIZE * 2);
ShaderModule[] shaderModules = SceneRender.CreateShaderModules(vulkanContext);
VkPipeline = CreatePipeline(vulkanContext, shaderModules);
Arrays.asList(shaderModules).forEach(shaderModule -> shaderModule.CleanUp(vulkanContext));
}
private static Attachment[] createDepthAttachments(VulkanContext VkCtx){
SwapChain swapChain = VkCtx.GetSwapChain();
int ImageCount = swapChain.GetImageCount();
VkExtent2D swapChainExtent = swapChain.GetSwapChainExtent();
Attachment[] DepthAttachments = new Attachment[ImageCount];
for(int i = 0; i < ImageCount; i++){
DepthAttachments[i] = new Attachment(VkCtx, swapChainExtent.width(), swapChainExtent.height(),
DEPTH_FORMAT,VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
}
return DepthAttachments;
}
private static VkRenderingAttachmentInfo[] createDepthAttachmentsInfo(VulkanContext VkCtx, Attachment[] DepthAttachments, VkClearValue clearValue){
SwapChain swapChain = VkCtx.GetSwapChain();
int ImageCount = swapChain.GetImageCount();
var Result = new VkRenderingAttachmentInfo[ImageCount];
for(int i = 0; i < ImageCount; i++){
var Attachments = VkRenderingAttachmentInfo.calloc()
.sType$Default()
.imageView(DepthAttachments[i].GetVkImageView().GetVulkanImageView())
.imageLayout(VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL)
.loadOp(VK_ATTACHMENT_LOAD_OP_CLEAR)
.storeOp(VK_ATTACHMENT_STORE_OP_DONT_CARE)
.clearValue(clearValue);
Result[i] = Attachments;
}
return Result;
}
private static ShaderModule[] CreateShaderModules(VulkanContext VkCtx){
if(EngineConfig.getInstance().RecompileShaders()){
ShaderCompiler.CompileGLSLShaderOnChange(VERTEX_SHADER_FILE_GLSL, Shaderc.shaderc_glsl_vertex_shader);
@ -62,14 +101,21 @@ public class SceneRender { //dynamic rendering;
new ShaderModule(VkCtx, VK_SHADER_STAGE_FRAGMENT_BIT, FRAGMENT_SHADER_FILE_SPV)
};
}
private static Pipeline CreatePipeline(VulkanContext VkCtx, ShaderModule[] ShaderModules){
var vertexBufferStructure = new VertexBufferStructure();
var BuildInfo = new PipelineBuildInfo(ShaderModules,vertexBufferStructure.getVertexInput(),
VkCtx.GetSurface().GetSurfaceFormat().ImageFormat());
VkCtx.GetSurface().GetSurfaceFormat().ImageFormat())
.SetDepthFormat(DEPTH_FORMAT)
.SetPushConstantRanges(
new PushConstantsRange[]{
new PushConstantsRange(VK_SHADER_STAGE_VERTEX_BIT,0,VulkanUtils.MATRIX4X4_SIZE * 2)
});
var pipeline = new Pipeline(VkCtx, BuildInfo);
vertexBufferStructure.cleanup();
return pipeline;
}
public void UpdateColours(VulkanContext vulkanContext){
if (Gb){
if (G < 1.0f && Math.random() <0.5) G+=0.0005f;
@ -94,7 +140,7 @@ public class SceneRender { //dynamic rendering;
}
ClearValueColour = VkClearValue.calloc().color(c->c.float32(0,R).float32(1,G).float32(2,B).float32(3,1.0f));
AttachmentInfoColour = CreateColourAttachmentsInfo(vulkanContext,ClearValueColour);
RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour);
RenderInfo = CreateRenderInfo(vulkanContext, AttachmentInfoColour, AttInfoDepth);
}
public void Render(VulkanContext vulkanContext, CommandBuffer commandBuffer,ModelsCache modelsCache, int ImageIndex){
@ -108,7 +154,15 @@ public class SceneRender { //dynamic rendering;
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,
VK_ACCESS_2_NONE, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
VulkanUtils.ImageBarrier(MemStack, CommandHandle, AttDepth[ImageIndex].GetVkImage().getVulkanImage(),
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
VK_IMAGE_ASPECT_DEPTH_BIT);
vkCmdBeginRendering(CommandHandle, RenderInfo[ImageIndex]);
vkCmdBindPipeline(CommandHandle, VK_PIPELINE_BIND_POINT_GRAPHICS, VkPipeline.GetVulkanPipeline());
VkExtent2D swapChainExtent = swapChain.GetSwapChainExtent();
@ -129,8 +183,11 @@ public class SceneRender { //dynamic rendering;
LongBuffer offsets = MemStack.mallocLong(1).put(0,0L);
LongBuffer vertexBuffer = MemStack.mallocLong(1);
//Fetch Actor Code to be written Later
var VulkanModels = modelsCache.GetModelMap().values();
for(VulkanModel VkModel : VulkanModels){
for(VulkanMesh mesh : VkModel.GetVkMeshList()){
vertexBuffer.put(0,mesh.VerticesBuffer().GetBuffer());
vkCmdBindVertexBuffers(CommandHandle,0,vertexBuffer,offsets);
@ -148,19 +205,22 @@ public class SceneRender { //dynamic rendering;
}
}
private static VkRenderingInfo[] CreateRenderInfo(VulkanContext vulkanContext, VkRenderingAttachmentInfo.Buffer[] ColourAttachments){
private static VkRenderingInfo[] CreateRenderInfo(VulkanContext vulkanContext, VkRenderingAttachmentInfo.Buffer[] ColourAttachments, VkRenderingAttachmentInfo[] DepthAttachments){
SwapChain swapChain = vulkanContext.GetSwapChain();
int ImageCount = swapChain.GetImageCount();
var Result = new VkRenderingInfo[ImageCount];
try(var MemStack = MemoryStack.stackPush()){
VkExtent2D Extent = swapChain.GetSwapChainExtent();
var RenderArea = VkRect2D.calloc(MemStack).extent(Extent);
for(int i = 0; i < ImageCount; i++){
var RenderInfo = VkRenderingInfo.calloc()
.sType$Default()
.renderArea(RenderArea)
.layerCount(1)
.pColorAttachments(ColourAttachments[i]);
.pColorAttachments(ColourAttachments[i])
.pDepthAttachment(DepthAttachments[i]);
Result[i] = RenderInfo;
}
}
@ -185,6 +245,11 @@ public class SceneRender { //dynamic rendering;
VkPipeline.CleanUp(VkCtx);
Arrays.asList(RenderInfo).forEach(VkRenderingInfo::free);
Arrays.asList(AttachmentInfoColour).forEach(VkRenderingAttachmentInfo.Buffer::free);
Arrays.asList(AttInfoDepth).forEach(VkRenderingAttachmentInfo::free);
Arrays.asList(AttachmentInfoColour).forEach(VkRenderingAttachmentInfo.Buffer::free);
Arrays.asList(AttDepth).forEach(Attachment->Attachment.CleanUp(VkCtx));
MemoryUtil.memFree(PushConstBuffer);
ClearValueDepth.free();
ClearValueColour.free();
}

View file

@ -15,6 +15,7 @@ public class VulkanUtils {
public static final int FLOAT_SIZE = 4;
public static final int INT_SIZE = 4;
public static final int MATRIX4X4_SIZE = 16 * FLOAT_SIZE;
public static int MemoryTypeFromProperties(VulkanContext vulkanContext, int TypeBits, int ReqMask){
int result = -1;

View file

@ -8,7 +8,7 @@ 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
#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
@ -31,4 +31,9 @@ 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