From 16e37d9b776db4f84b7df7852c080328dfaa9998 Mon Sep 17 00:00:00 2001 From: mcrcortex <{ID}+{username}@users.noreply.github.com> Date: Thu, 27 Jun 2024 08:02:35 +0900 Subject: [PATCH] More work on nodes --- .../rendering/hierarchical/NodeManager2.java | 149 +++++++++++++++++- .../voxy/common/util/HierarchicalBitSet.java | 19 +++ 2 files changed, 163 insertions(+), 5 deletions(-) diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/NodeManager2.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/NodeManager2.java index eac32761..1ce5b4bd 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/NodeManager2.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/NodeManager2.java @@ -3,7 +3,9 @@ package me.cortex.voxy.client.core.rendering.hierarchical; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import me.cortex.voxy.client.core.rendering.building.BuiltSection; import me.cortex.voxy.client.core.rendering.util.MarkedObjectList; +import me.cortex.voxy.common.util.HierarchicalBitSet; import me.cortex.voxy.common.world.WorldEngine; +import org.lwjgl.system.MemoryUtil; import java.util.Arrays; @@ -25,7 +27,7 @@ public class NodeManager2 { public int nodeId; //The mask of what child nodes are required - public byte requiredChildMask; + //public byte requiredChildMask; //The mask of currently supplied child node data public byte currentChildMask; @@ -44,7 +46,7 @@ public class NodeManager2 { public void clear() { this.position = 0; this.nodeId = 0; - this.requiredChildMask = 0; + //this.requiredChildMask = 0; this.currentChildMask = 0; Arrays.fill(this.meshIds, -1); Arrays.fill(this.childPositions, -1); @@ -52,7 +54,7 @@ public class NodeManager2 { //Returns true if the request is satisfied public boolean isSatisfied() { - return (this.requiredChildMask&this.currentChildMask)==this.requiredChildMask; + return this.currentChildMask == -1;//(this.requiredChildMask&this.currentChildMask)==this.requiredChildMask; } public int getMeshId(int inner) { @@ -71,15 +73,33 @@ public class NodeManager2 { this.meshIds[innerId] = mesh; this.childPositions[innerId] = position; } + + public void make(int id, long position) { + this.nodeId = id; + this.position = position; + } + + public byte nonAirMask() { + byte res = 0; + for (int i = 0; i < 8; i++) { + if (this.meshIds[i] != -1) { + res |= (byte) (1<>1)&1), + (WorldEngine.getZ(basePos)<<1)|((addin>>2)&1)); + } + + + //IDEA, since a graph node can be in effectivly only 3 states, if inner node -> may or may not have mesh, and, if leaf node -> has mesh, no children + // the request queue only needs to supply the node id, since if its an inner node, it must be requesting for a mesh, while if its a leaf node, it must be requesting for children + private void processRequestQueue(long ptr, long size) { + int count = MemoryUtil.memGetInt(ptr); ptr += 4; + for (int i = 0; i < count; i++) { + int requestOp = MemoryUtil.memGetInt(ptr + i*4L); + int node = requestOp&NODE_MSK; + + if (this.isLeafNode(node)) { + //If its a leaf node and it has a request, it must need the children + if (this.getNodeMesh(node) == -1) { + throw new IllegalStateException("Leaf node doesnt have mesh"); + } + //Create a new request + int idx = this.leafRequests.allocate(); + var request = this.leafRequests.get(idx); + + { + long nodePos = this.getNodePos(node); + request.make(node, nodePos);//Request all child nodes + int requestIdx = idx|(1<<31);//First bit is set to 1 to indicate a request index instead of a node index + + //Loop over all child positions and insert them into the queue + for (int j = 0; j < 8; j++) { + long child = makeChildPos(nodePos, j); + int prev = this.pos2meshId.putIfAbsent(child, requestIdx); + if (prev != NO_NODE) { + throw new IllegalArgumentException("Node pos already in request map"); + } + //Mark the position as watching and force request an update + this.interactor.watchUpdates(child); + this.interactor.requestMesh(child); + } + } + //NOTE: dont unmark the node yet, as the request hasnt been satisfied + + } else { + //If its not a leaf node, it must be missing the inner mesh so request it + if (this.getNodeMesh(node) != -1) { + //Node already has a mesh, ignore it, but might be a sign that an error has occured + System.err.println("Requested a mesh for node, however the node already has a mesh"); + + //TODO: need to unmark the node that requested it, either that or only clear node data when a mesh has been removed + + } else { + //Put it into the map + watch and request + long pos = this.getNodePos(node); + long prev = this.pos2meshId.putIfAbsent(pos, node); + if (prev != NO_NODE) { + throw new IllegalStateException("Pos already has a node id attached"); + } + this.interactor.watchUpdates(pos); + this.interactor.requestMesh(pos); + } + } + } + } + + + //Tracking for nodes that specifically need meshes, if a node doesnt have or doesnt need a mesh node, it is not in the map // the map should be identical to the currently watched set of sections @@ -125,6 +229,15 @@ public class NodeManager2 { // remove all from the chain + + + + + //TODO: FIXME: CRITICAL: if a section is empty when created, it wont get allocated a slot, however, the section might + // become unempty due to an update!!! THIS IS REALLY BAD. since it doesnt have an allocation + + + //TODO: test and fix the possible race condition of if a section is not empty then becomes empty in the same tick // that is, there is a request that is satisfied bar 1 section, that section is supplied as non emptpty but then becomes empty in the same tick private void meshUpdate(BuiltSection mesh) { @@ -188,19 +301,45 @@ public class NodeManager2 { } else { //The mesh is an update for an existing node - int prevMesh = this.getMeshForNode(id); + + //Sanity check + if (this.getNodePos(id) != mesh.position) { + throw new IllegalStateException("Node position not same as mesh position"); + } + + int prevMesh = this.getNodeMesh(id); // TODO: If the mesh to upload is air, the node should be removed (however i believe this is only true if all the children are air! fuuuuu) if (prevMesh != -1) { //Node has a mesh attached, remove and replace it + this.setMeshId(id, this.meshManager.uploadReplaceMesh(prevMesh, mesh)); } else { //Node didnt have a mesh attached, so just set the current mesh + this.setMeshId(id, this.meshManager.uploadMesh(mesh)); } + //Push the updated node to the gpu + this.pushNode(id); } } private void completeRequest(LeafRequest request) { //TODO: need to actually update all of the pos2meshId of the children to point to there new nodes + int msk = Byte.toUnsignedInt(request.nonAirMask()); + int baseIdx = this.nodeAllocations.allocateNextConsecutiveCounted(Integer.bitCount(msk)); + for (int i = 0; i < 8; i++) { + if ((msk&(1<>18))-1)); + return 0; + } + + public int allocateNextConsecutiveCounted(int count) { + if (this.A==-1) { + return -1; + } + if (this.cnt+count>this.limit) { + return -2;//Limit reached + } + + return 0; + } + + public boolean free(int idx) { long v = this.D[idx>>6]; boolean wasSet = (v&(1L<<(idx&0x3f)))!=0;