diff --git a/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java b/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java index 658ed4868..1bcade871 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/DHLevel.java @@ -24,7 +24,6 @@ public class DHLevel extends LodQuadTree { } } - @Override public RenderDataSource getRenderDataSource() { return dataHandler; diff --git a/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java b/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java index 282fda8ad..fd3263eef 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/LodQuadTree.java @@ -51,7 +51,8 @@ public abstract class LodQuadTree { public void tick(DhBlockPos2D playerPos) { for (int detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { - ringLists[detailLevel].move(playerPos.x >> detailLevel, playerPos.z >> detailLevel); + ringLists[detailLevel].move(playerPos.x >> detailLevel, playerPos.z >> detailLevel, + LodSection::immediateDispose); } // First tick pass: update all sections' childCount from bottom level to top level. Step: @@ -73,7 +74,6 @@ public abstract class LodQuadTree { // - set childCount to -1 (Signal that this section will be freed if not rescued) // - If targetLevel <= detail && section == null: // - Parent's childCount++ (Create parent if needed) - for (byte detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { final MovableGridRingList ringList = ringLists[detailLevel]; final MovableGridRingList childRingList = @@ -150,10 +150,8 @@ public abstract class LodQuadTree { // if childCount == -1: // (section can be loaded or unloaded, due to fast movement) // - set this section to null (TODO: Is this needed to be first or last or don't matter for concurrency?) // - If loaded unload section - for (byte detailLevel = 0; detailLevel < maxPossibleDetailLevel; detailLevel++) { - final byte detail = detailLevel; - final MovableGridRingList ringList = ringLists[detail]; + final MovableGridRingList ringList = ringLists[detailLevel]; final MovableGridRingList childRingList = detailLevel == 0 ? null : ringLists[detailLevel - 1]; final MovableGridRingList parentRingList = @@ -182,6 +180,7 @@ public abstract class LodQuadTree { if (section.isLoaded()) { section.unload(); } + section.dispose(); } }); } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java b/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java index b44a4ebec..c80427f9d 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/LodSection.java @@ -1,6 +1,8 @@ package com.seibel.lod.core.objects.a7; import com.seibel.lod.core.objects.a7.pos.DhSectionPos; +import com.seibel.lod.core.objects.a7.render.RenderContainer; +import com.seibel.lod.core.util.LodUtil; public class LodSection { public static final int SUB_REGION_DATA_WIDTH = 16*16; @@ -14,7 +16,7 @@ public class LodSection { private RenderDataContainer levelContainer; - private RenderContainer renderContainer = null; + public RenderContainer renderContainer = null; // Create sub region public LodSection(DhSectionPos pos) { @@ -34,10 +36,21 @@ public class LodSection { } public void unload() { if (!isLoaded()) throw new IllegalStateException("LodSection is not loaded"); + if (renderContainer != null) renderContainer.notifyUnload(); levelContainer = null; } + public void dispose() { + LodUtil.assertTrue(!isLoaded()); + if (renderContainer != null) renderContainer.notifyDispose(); + } + + public void immediateDispose() { + if (isLoaded()) unload(); + if (renderContainer != null) renderContainer.notifyDispose(); + } public boolean isLoaded() { return levelContainer != null; } + } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/RenderContainer.java b/src/main/java/com/seibel/lod/core/objects/a7/RenderContainer.java deleted file mode 100644 index b395ccc7b..000000000 --- a/src/main/java/com/seibel/lod/core/objects/a7/RenderContainer.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.seibel.lod.core.objects.a7; - -public class RenderContainer { -} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/pos/DhSectionPos.java b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhSectionPos.java index 7b187cd55..88f1dd758 100644 --- a/src/main/java/com/seibel/lod/core/objects/a7/pos/DhSectionPos.java +++ b/src/main/java/com/seibel/lod/core/objects/a7/pos/DhSectionPos.java @@ -1,5 +1,6 @@ package com.seibel.lod.core.objects.a7.pos; +import com.seibel.lod.core.enums.LodDirection; import com.seibel.lod.core.objects.a7.DHLevel; import org.lwjgl.system.CallbackI; @@ -50,4 +51,8 @@ public class DhSectionPos { public DhSectionPos getParent(){ return new DhSectionPos((byte) (detail + 1), x / 2, z / 2); } + + public DhSectionPos getAdjacent(LodDirection dir) { + return new DhSectionPos(detail, x + dir.getNormal().x, z + dir.getNormal().z); + } } diff --git a/src/main/java/com/seibel/lod/core/objects/a7/render/ColumnRenderContainer.java b/src/main/java/com/seibel/lod/core/objects/a7/render/ColumnRenderContainer.java new file mode 100644 index 000000000..fd8d4afad --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/render/ColumnRenderContainer.java @@ -0,0 +1,39 @@ +package com.seibel.lod.core.objects.a7.render; + +import com.seibel.lod.core.objects.a7.pos.DhSectionPos; +import com.seibel.lod.core.objects.opengl.RenderBuffer; +import com.seibel.lod.core.objects.opengl.RenderRegion; +import com.seibel.lod.core.util.LodUtil; + +public class ColumnRenderContainer extends RenderContainer { + public static final int columnWidth = DhSectionPos.DATA_WIDTH_PER_SECTION; + public static final int columnCount = LodUtil.pow2(DhSectionPos.DATA_WIDTH_PER_SECTION); + private long[] columnData; + public final int maxColumnHeight; + public final int minWorldHeight; + + public RenderRegion renderRegion = null; + + public ColumnRenderContainer(int maxColumnHeight, int minWorldHeight) { + this.maxColumnHeight = maxColumnHeight; + columnData = new long[columnCount * maxColumnHeight]; + this.minWorldHeight = minWorldHeight; + renderRegion = new RenderRegion(); + } + + @Override + public void notifyUnload() { + + } + + @Override + public void notifyDispose() { + + } + + @Override + public RenderRegion getRenderRegion() { + return null; + } + +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/render/EmptyRenderContainer.java b/src/main/java/com/seibel/lod/core/objects/a7/render/EmptyRenderContainer.java new file mode 100644 index 000000000..95afea72c --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/render/EmptyRenderContainer.java @@ -0,0 +1,18 @@ +package com.seibel.lod.core.objects.a7.render; + +public class EmptyRenderContainer extends RenderContainer { + @Override + public void notifyUnload() { + + } + + @Override + public void notifyDispose() { + + } + + @Override + public boolean render() { + return true; //Always render successfully since there is nothing to render + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/render/RenderContainer.java b/src/main/java/com/seibel/lod/core/objects/a7/render/RenderContainer.java new file mode 100644 index 000000000..1321cbc7f --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/render/RenderContainer.java @@ -0,0 +1,13 @@ +package com.seibel.lod.core.objects.a7.render; + +import com.seibel.lod.core.objects.opengl.RenderBuffer; +import com.seibel.lod.core.objects.opengl.RenderRegion; + +public abstract class RenderContainer { + + public abstract void notifyUnload(); + public abstract void notifyDispose(); + + public abstract RenderRegion getRenderRegion(); + +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/render/RenderSection.java b/src/main/java/com/seibel/lod/core/objects/a7/render/RenderSection.java new file mode 100644 index 000000000..f8b5be3a3 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/render/RenderSection.java @@ -0,0 +1,424 @@ +/* + * This file is part of the Distant Horizons mod (formerly the LOD Mod), + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2022 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.core.objects.a7.render; + +import com.seibel.lod.core.api.internal.ClientApi; +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.CubicLodTemplate; +import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodQuadBuilder; +import com.seibel.lod.core.enums.LodDirection; +import com.seibel.lod.core.enums.config.GpuUploadMethod; +import com.seibel.lod.core.enums.rendering.DebugMode; +import com.seibel.lod.core.enums.rendering.GLProxyContext; +import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler; +import com.seibel.lod.core.objects.*; +import com.seibel.lod.core.objects.a7.LodQuadTree; +import com.seibel.lod.core.objects.a7.LodSection; +import com.seibel.lod.core.objects.lod.LodDimension; +import com.seibel.lod.core.objects.lod.LodRegion; +import com.seibel.lod.core.objects.math.Vec3d; +import com.seibel.lod.core.objects.math.Vec3f; +import com.seibel.lod.core.objects.opengl.RenderBuffer; +import com.seibel.lod.core.objects.opengl.SimpleRenderBuffer; +import com.seibel.lod.core.render.GLProxy; +import com.seibel.lod.core.render.LodRenderProgram; +import com.seibel.lod.core.render.RenderUtil; +import com.seibel.lod.core.util.DataPointUtil; +import com.seibel.lod.core.util.LevelPosUtil; +import com.seibel.lod.core.util.LodUtil; +import com.seibel.lod.core.util.StatsMap; +import com.seibel.lod.core.util.gridList.PosArrayGridList; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; + +import java.util.ConcurrentModificationException; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static com.seibel.lod.core.render.LodRenderer.EVENT_LOGGER; + +public class RenderSection implements AutoCloseable +{ + private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + + /** stores if the region at the given x and z index needs to be regenerated */ + // Use int because I need Tri state: + private final AtomicInteger needRegen = new AtomicInteger(2); + + private enum BackState { + Unused, + Building, + Complete, + } + private enum FrontState { + Unused, + Rendering, + Invalidated, + } + + final LodSection lodSection; + final LodQuadTree quadTree; + RenderBuffer renderBufferBack = null; + AtomicReference backState = + new AtomicReference(BackState.Unused); + AtomicReference frontState = + new AtomicReference(FrontState.Unused); + RenderBuffer renderBufferFront = null; + + public RenderSection(LodSection lodSection, LodQuadTree quadTree) { + this.lodSection = lodSection; + this.quadTree = quadTree; + } + + public void setNeedRegen() { + needRegen.set(2); + } + + public Optional> updateStatus(Executor bufferUploader, Executor bufferBuilder, boolean alwaysRegen, int playerPosX, int playerPosZ, boolean doCaveCulling) { + if (alwaysRegen) setNeedRegen(); + + BackState state = backState.get(); + if (state != BackState.Unused) { + EVENT_LOGGER.trace("{}: UpdateStatus rejected. Cause: BackState is {}", lodSection, state); + return Optional.empty(); + } + if (needRegen.get() == 0) { + EVENT_LOGGER.trace("{}: UpdateStatus rejected. Cause: Region doesn't need regen", lodSection); + return Optional.empty(); + } + if (!backState.compareAndSet(BackState.Unused, BackState.Building)) { + EVENT_LOGGER.trace("{}: UpdateStatus rejected. Cause: CAS on BackState failed: ", backState.get()); + return Optional.empty(); + } + needRegen.decrementAndGet(); + return Optional.of(startBuid(bufferUploader, bufferBuilder, lodSection, playerPosX, playerPosZ, doCaveCulling)); + } + + public boolean render(LodDimension renderDim, + Vec3d cameraPos, DHBlockPos cameraBlockPos, Vec3f cameraDir, + boolean enableDirectionalCulling, LodRenderProgram program) { + if (!frontState.compareAndSet(FrontState.Unused, FrontState.Rendering)) return false; + try { + if (renderDim != lodDim) return false; + if (enableDirectionalCulling && + !RenderUtil.isRegionInViewFrustum(cameraBlockPos, + cameraDir, regionPos.x, regionPos.z)) return false; + BackState state = backState.get(); + if (state == BackState.Complete) { + if (renderBufferBack != null) { + EVENT_LOGGER.debug("RenderRegion swap @ {}", regionPos); + boolean shouldKeep = renderBufferFront != null && renderBufferFront.onSwapToBack(); + RenderBuffer temp = shouldKeep ? renderBufferFront : null; + renderBufferFront = renderBufferBack; + renderBufferBack = temp; + if (renderBufferFront != null) renderBufferFront.onSwapToFront(); + } + if (!backState.compareAndSet(BackState.Complete, BackState.Unused)) { + EVENT_LOGGER.error("RenderRegion.render() got illegal state on swapping buffer!"); + } + } + if (renderBufferFront == null) return false; + program.setModelPos(new Vec3f( + (float) ((regionPos.x * LodUtil.REGION_WIDTH) - cameraPos.x), + (float) (LodBuilder.MIN_WORLD_HEIGHT - cameraPos.y), + (float) ((regionPos.z * LodUtil.REGION_WIDTH) - cameraPos.z))); + + return renderBufferFront.render(program); + } finally { + frontState.compareAndSet(FrontState.Rendering, FrontState.Unused); + } + + } + + private void recreateBuffer(LodQuadBuilder builder) { + if (renderBufferBack != null) throw new RuntimeException("Assert Error"); + boolean useSimpleBuffer = (builder.getCurrentNeededVertexBufferCount() <= 6) || true; + renderBufferBack = useSimpleBuffer ? + new SimpleRenderBuffer() + : null; //new ComplexRenderRegion(regPos); + } + + private CompletableFuture startBuid(Executor bufferUploader, Executor bufferBuilder, LodSection section, int playerPosX, int playerPosZ, boolean doCaveCulling) { + EVENT_LOGGER.trace("RenderSection startBuild @ {}", section); + RenderContainer[] adjSections = new RenderContainer[4]; + try { + if (renderBufferBack != null) renderBufferBack.onReuse(); + for (LodDirection dir : LodDirection.ADJ_DIRECTIONS) { + adjSections[dir.ordinal() - 2] = quadTree.getSection(section.pos.getAdjacent(dir)).renderContainer; + + } + } catch (Throwable t) { + setNeedRegen(); + if (!backState.compareAndSet(BackState.Building, BackState.Unused)) { + EVENT_LOGGER.error("\"Lod Builder Starter\"" + + " encountered error on catching exceptions and fallback on starting build task: ", + new ConcurrentModificationException("RenderRegion Illegal State")); + } + throw t; + } + return CompletableFuture.supplyAsync(() -> { + try { + EVENT_LOGGER.trace("RenderRegion start QuadBuild @ {}", regionPos); + int skyLightCullingBelow = CONFIG.client().graphics().advancedGraphics().getCaveCullingHeight(); + // FIXME: Clamp also to the max world height. + skyLightCullingBelow = Math.max(skyLightCullingBelow, LodBuilder.MIN_WORLD_HEIGHT); + LodQuadBuilder builder = new LodQuadBuilder(doCaveCulling, skyLightCullingBelow); + Runnable buildRun = ()->{ + makeLodRenderData(builder, region, adjRegions, playerPosX, playerPosZ); + }; + if (renderBufferBack != null) + renderBufferBack.build(buildRun); + else + buildRun.run(); + EVENT_LOGGER.trace("RenderRegion end QuadBuild @ {}", regionPos); + return builder; + } catch (Throwable e3) { + EVENT_LOGGER.error("\"LodNodeBufferBuilder\" was unable to build quads: ", e3); + throw e3; + } + }, bufferBuilder) + + .thenAcceptAsync((builder) -> { + try { + EVENT_LOGGER.trace("RenderRegion start Upload @ {}", regionPos); + GLProxy glProxy = GLProxy.getInstance(); + GpuUploadMethod method = GLProxy.getInstance().getGpuUploadMethod(); + GLProxyContext oldContext = glProxy.getGlContext(); + glProxy.setGlContext(GLProxyContext.LOD_BUILDER); + try { + if (renderBufferBack == null) recreateBuffer(builder); + if (!renderBufferBack.tryUploadBuffers(builder, method)) { + renderBufferBack = null; + recreateBuffer(builder); + if (!renderBufferBack.tryUploadBuffers(builder, method)) { + throw new RuntimeException("Newly created renderBuffer " + + "is still returning false on tryUploadBuffers!"); + } + } + } finally { + glProxy.setGlContext(oldContext); + } + EVENT_LOGGER.trace("RenderRegion end Upload @ {}", regionPos); + } catch (Throwable e3) { + EVENT_LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3); + throw e3; + } + }, bufferUploader).handle((v, e) -> { + if (e != null) { + setNeedRegen(); + if (!backState.compareAndSet(BackState.Building, BackState.Unused)) { + EVENT_LOGGER.error("\"LodNodeBufferBuilder\"" + + " encountered error on exit: ", + new ConcurrentModificationException("RenderRegion Illegal State")); + } + } else { + if (!backState.compareAndSet(BackState.Building, BackState.Complete)) { + EVENT_LOGGER.error("\"LodNodeBufferBuilder\"" + + " encountered error on exit: ", + new ConcurrentModificationException("RenderRegion Illegal State")); + } + } + return (Void) null; + }); + } + + private static final int ADJACENT8[][] = { + {-1,-1}, + {-1, 0}, + {-1, 1}, + { 0,-1}, + //{ 0, 0}, + { 0, 1}, + { 1,-1}, + { 1, 0}, + { 1, 1} + }; + + private static void makeLodRenderData(LodQuadBuilder quadBuilder, LodRegion region, LodRegion[] adjRegions, int playerX, + int playerZ) { + byte minDetail = region.getMinDetailLevel(); + + // Variable initialization + DebugMode debugMode = CONFIG.client().advanced().debugging().getDebugMode(); + + // We ask the lod dimension which block we have to render given the player + // position + PosToRenderContainer posToRender = new PosToRenderContainer(minDetail, region.regionPosX, region.regionPosZ); + region.getPosToRender(posToRender, playerX, playerZ); + posToRender.sort(); + PosArrayGridList chunkGrid = ClientApi.renderer.vanillaChunks; + + for (int index = 0; index < posToRender.getNumberOfPos(); index++) { + + byte detailLevel = posToRender.getNthDetailLevel(index); + int posX = posToRender.getNthPosX(index); + int posZ = posToRender.getNthPosZ(index); + + // TODO: In the future, We don't need to ignore rendered chunks! Just build it + // and leave it for the renderer to decide! + // We don't want to render this fake block if + // The block is inside the render distance with, is not bigger than a chunk and + // is positioned in a chunk set as vanilla rendered + + // The block is in the player chunk or in a chunk adjacent to the player + if (detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL) { + int chunkX = LevelPosUtil.getChunkPos(detailLevel, posX); + int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posZ); + // skip any chunks that Minecraft is going to render + if (chunkGrid != null && chunkGrid.get(chunkX, chunkZ) != null) continue; + } + LodDataView posData = region.getDataView(detailLevel, posX, posZ); + if (posData == null || posData.size() == 0 || !DataPointUtil.doesItExist(posData.get(0)) + || DataPointUtil.isVoid(posData.get(0))) + continue; + + LodDataView[][] adjData = new LodDataView[4][]; + boolean[] adjUseBlack = new boolean[4]; + + // We extract the adj data in the four cardinal direction + + // we first reset the adjShadeDisabled. This is used to disable the shade on the + // border when we have transparent block like water or glass + // to avoid having a "darker border" underground + // Arrays.fill(adjShadeDisabled, false); + + // We check every adj block in each direction + + // If the adj block is rendered in the same region and with same detail + // and is positioned in a place that is not going to be rendered by vanilla game + // then we can set this position as adj + // We avoid cases where the adjPosition is in player chunk while the position is + // not + // to always have a wall underwater + for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS) { + try { + int xAdj = posX + lodDirection.getNormal().x; + int zAdj = posZ + lodDirection.getNormal().z; + int chunkXAdj = LevelPosUtil.getChunkPos(detailLevel, xAdj); + int chunkZAdj = LevelPosUtil.getChunkPos(detailLevel, zAdj); + if (chunkGrid != null && chunkGrid.get(chunkXAdj, chunkZAdj)!=null) { + adjUseBlack[lodDirection.ordinal()-2] = true; + } + + boolean isCrossRegionBoundary = LevelPosUtil.getRegion(detailLevel, xAdj) != region.regionPosX || + LevelPosUtil.getRegion(detailLevel, zAdj) != region.regionPosZ; + + LodRegion adjRegion; + byte adjDetail; + int childXAdj = xAdj*2 + (lodDirection.getNormal().x<0 ? 1 : 0); + int childZAdj = zAdj*2 + (lodDirection.getNormal().z<0 ? 1 : 0); + + //we check if the detail of the adjPos is equal to the correct one (region border fix) + //or if the detail is wrong by 1 value (region+circle border fix) + if (isCrossRegionBoundary) { + //we compute at which detail that position should be rendered + adjRegion = adjRegions[lodDirection.ordinal()-2]; + if(adjRegion == null) continue; + adjDetail = adjRegion.getRenderDetailLevelAt(playerX, playerZ, detailLevel, xAdj, zAdj); + } else { + adjRegion = region; + if (posToRender.contains(detailLevel, xAdj, zAdj)) adjDetail = detailLevel; + else if (detailLevel>0 && + posToRender.contains((byte) (detailLevel-1), childXAdj, childZAdj)) + adjDetail = (byte) (detailLevel-1); + else if (detailLevel detailLevel+1) { + continue; + } + + if (adjDetail == detailLevel || adjDetail > detailLevel) { + adjData[lodDirection.ordinal() - 2] = new LodDataView[1]; + adjData[lodDirection.ordinal() - 2][0] = adjRegion.getDataView(adjDetail, + LevelPosUtil.convert(detailLevel, xAdj, adjDetail), + LevelPosUtil.convert(detailLevel, zAdj, adjDetail)); + } else { + adjData[lodDirection.ordinal() - 2] = new LodDataView[2]; + adjData[lodDirection.ordinal() - 2][0] = adjRegion.getDataView(adjDetail, + childXAdj, childZAdj); + adjData[lodDirection.ordinal() - 2][1] = adjRegion.getDataView(adjDetail, + childXAdj + (lodDirection.getAxis()==LodDirection.Axis.X ? 0 : 1), + childZAdj + (lodDirection.getAxis()==LodDirection.Axis.Z ? 0 : 1)); + } + } catch (RuntimeException e) { + EVENT_LOGGER.warn("Failed to get adj data for [{}:{},{}] at [{}]", detailLevel, posX, posZ, lodDirection); + EVENT_LOGGER.warn("Detail exception: ", e); + } + } + + // We render every vertical lod present in this position + // We only stop when we find a block that is void or non-existing block + for (int i = 0; i < posData.size(); i++) { + long data = posData.get(i); + // If the data is not renderable (Void or non-existing) we stop since there is + // no data left in this position + if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data)) + break; + + long adjDataTop = i - 1 >= 0 ? posData.get(i - 1) : DataPointUtil.EMPTY_DATA; + long adjDataBot = i + 1 < posData.size() ? posData.get(i + 1) : DataPointUtil.EMPTY_DATA; + + // We send the call to create the vertices + CubicLodTemplate.addLodToBuffer(data, adjDataTop, adjDataBot, adjData, adjUseBlack, detailLevel, + LevelPosUtil.getRegionModule(detailLevel, posX), + LevelPosUtil.getRegionModule(detailLevel, posZ), quadBuilder, debugMode); + } + + } // for pos to in list to render + // the thread executed successfully + // Merge all quads + quadBuilder.mergeQuads(); + } + + + @Override + public void close() + { + if (renderBufferBack != null) renderBufferBack.close(); + while (frontState.get() != FrontState.Invalidated && !frontState.compareAndSet(FrontState.Unused, FrontState.Invalidated)) { + Thread.yield(); //FIXME: If on java 9, use Thread.onSpinWait(); + } + if (renderBufferFront != null) renderBufferFront.close(); + } + + public void debugDumpStats(StatsMap statsMap) + { + statsMap.incStat("RenderRegions"); + RenderBuffer front = renderBufferFront; + if (front!=null) { + statsMap.incStat("FrontBuffers"); + front.debugDumpStats(statsMap); + } + + RenderBuffer back = renderBufferBack; + if (back!=null) { + statsMap.incStat("BackBuffers"); + back.debugDumpStats(statsMap); + } + + + } +} diff --git a/src/main/java/com/seibel/lod/core/objects/a7/render/column/ColumnRenderBuffer.java b/src/main/java/com/seibel/lod/core/objects/a7/render/column/ColumnRenderBuffer.java new file mode 100644 index 000000000..3eb30a8b1 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/objects/a7/render/column/ColumnRenderBuffer.java @@ -0,0 +1,5 @@ +package com.seibel.lod.core.objects.a7.render.column; + +public class ColumnRenderBuffer { + +}