diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/FullDataArrayAccessor.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/FullDataArrayAccessor.java index 1829d59f4..db8eec620 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/FullDataArrayAccessor.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/FullDataArrayAccessor.java @@ -114,14 +114,18 @@ public class FullDataArrayAccessor implements IFullDataAccessor { for (int z = 0; z < this.width; z++) { - long[] sourceData = this.dataArrays[this.offset + x * this.dataWidth + z]; - long[] newData = new long[sourceData.length]; - for (int dataPointIndex = 0; dataPointIndex < newData.length; dataPointIndex++) + long[] currentData = this.dataArrays[this.offset + x * this.dataWidth + z]; + // may be null if no data exists for this column yet + if (currentData != null) { - newData[dataPointIndex] = FullDataPointUtil.remap(remappedIds, sourceData[dataPointIndex]); + long[] newData = new long[currentData.length]; // TODO what to do if null? + for (int dataPointIndex = 0; dataPointIndex < newData.length; dataPointIndex++) + { + newData[dataPointIndex] = FullDataPointUtil.remap(remappedIds, currentData[dataPointIndex]); + } + + target.dataArrays[target.offset + x * target.dataWidth + z] = newData; } - - target.dataArrays[target.offset + x * target.dataWidth + z] = newData; } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/AbstractFullDataSourceLoader.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/AbstractFullDataSourceLoader.java index d7246e80f..ec112323e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/AbstractFullDataSourceLoader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/AbstractFullDataSourceLoader.java @@ -28,8 +28,7 @@ import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStrea import java.io.IOException; import java.util.*; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; public abstract class AbstractFullDataSourceLoader { @@ -46,7 +45,7 @@ public abstract class AbstractFullDataSourceLoader /** used when pooling data sources */ private final ArrayList cachedSources = new ArrayList<>(); - private final ReadWriteLock cacheReadWriteLock = new ReentrantReadWriteLock(); + private final ReentrantLock cacheLock = new ReentrantLock(); @@ -159,7 +158,7 @@ public abstract class AbstractFullDataSourceLoader { try { - this.cacheReadWriteLock.readLock().lock(); + this.cacheLock.lock(); int index = this.cachedSources.size() - 1; if (index == -1) @@ -173,7 +172,7 @@ public abstract class AbstractFullDataSourceLoader } finally { - this.cacheReadWriteLock.readLock().unlock(); + this.cacheLock.unlock(); } } @@ -198,12 +197,12 @@ public abstract class AbstractFullDataSourceLoader try { - this.cacheReadWriteLock.writeLock().lock(); + this.cacheLock.lock(); this.cachedSources.add(dataSource); } finally { - this.cacheReadWriteLock.writeLock().unlock(); + this.cacheLock.unlock(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 7cb73cc2a..a2f410bbe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -297,6 +297,8 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu @Override public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) { return this.get(relativeX, relativeZ); } + @Override + public SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ) { return this.get(relativeX, relativeZ); } @Override public void update(ChunkSizedFullDataAccessor chunkDataView) @@ -461,6 +463,7 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu @Override public boolean isEmpty() { return this.isEmpty; } + @Override public void markNotEmpty() { this.isEmpty = false; } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java index b4035e3b6..c4237c733 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java @@ -410,18 +410,31 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo // data // //======// - public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) + @Override + public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, false); } + @Override + public SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, true); } + private SingleColumnFullDataAccessor tryGetOrCreate(int relativeX, int relativeZ, boolean createIfMissing) { LodUtil.assertTrue(relativeX >= 0 && relativeX < SECTION_SIZE && relativeZ >= 0 && relativeZ < SECTION_SIZE); int chunkX = relativeX / this.dataPointsPerSection; int chunkZ = relativeZ / this.dataPointsPerSection; - FullDataArrayAccessor chunk = this.sparseData[chunkX * this.sectionCount + chunkZ]; - if (chunk == null) + FullDataArrayAccessor accessor = this.sparseData[chunkX * this.sectionCount + chunkZ]; + if (accessor == null) { - return null; + if (createIfMissing) + { + // create the missing data so the following get() will succeed + accessor = new FullDataArrayAccessor(this.mapping, new long[this.dataPointsPerSection * this.dataPointsPerSection][], this.dataPointsPerSection); + this.sparseData[chunkX * this.sectionCount + chunkZ] = accessor; + } + else + { + return null; + } } - return chunk.get(relativeX % this.dataPointsPerSection, relativeZ % this.dataPointsPerSection); + return accessor.get(relativeX % this.dataPointsPerSection, relativeZ % this.dataPointsPerSection); } @@ -470,6 +483,8 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo @Override public boolean isEmpty() { return this.isEmpty; } + @Override + public void markNotEmpty() { this.isEmpty = false; } @Override public int getWidthInDataPoints() { return SECTION_SIZE; } @@ -520,121 +535,6 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo // data sampling // - @Override - public void sampleFrom(IFullDataSource fullDataSource) - { - DhSectionPos pos = fullDataSource.getSectionPos(); - LodUtil.assertTrue(pos.getDetailLevel() < this.sectionPos.getDetailLevel()); - LodUtil.assertTrue(pos.overlapsExactly(this.sectionPos)); - if (fullDataSource.isEmpty()) - { - return; - } - - - if (fullDataSource instanceof CompleteFullDataSource) - { - this.sampleFrom((CompleteFullDataSource) fullDataSource); - } - else if (fullDataSource instanceof HighDetailIncompleteFullDataSource) - { - this.sampleFrom((HighDetailIncompleteFullDataSource) fullDataSource); - } - else if (fullDataSource instanceof LowDetailIncompleteFullDataSource) - { -// this.sampleFrom((LowDetailIncompleteFullDataSource) fullDataSource); - LodUtil.assertNotReach("SampleFrom not implemented for [" + IFullDataSource.class.getSimpleName() + "] with class [" + fullDataSource.getClass().getSimpleName() + "]."); - } - else - { - LodUtil.assertNotReach("SampleFrom not implemented for [" + IFullDataSource.class.getSimpleName() + "] with class [" + fullDataSource.getClass().getSimpleName() + "]."); - } - } - - private void sampleFrom(CompleteFullDataSource completeDataSource) - { - DhSectionPos pos = completeDataSource.getSectionPos(); - this.isEmpty = false; - - DhLodPos basePos = this.sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); - DhLodPos dataPos = pos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); - - int coveredChunks = pos.getWidthCountForLowerDetailedSection(SPARSE_UNIT_DETAIL); - int sourceDataPerChunk = SPARSE_UNIT_SIZE >>> completeDataSource.getDataDetailLevel(); - LodUtil.assertTrue((coveredChunks * sourceDataPerChunk) == CompleteFullDataSource.WIDTH); - - int xDataOffset = dataPos.x - basePos.x; - int zDataOffset = dataPos.z - basePos.z; - LodUtil.assertTrue(xDataOffset >= 0 && xDataOffset < this.sectionCount && zDataOffset >= 0 && zDataOffset < this.sectionCount); - - for (int xOffset = 0; xOffset < coveredChunks; xOffset++) - { - for (int zOffset = 0; zOffset < coveredChunks; zOffset++) - { - FullDataArrayAccessor sourceChunk = completeDataSource.subView(sourceDataPerChunk, xOffset * sourceDataPerChunk, zOffset * sourceDataPerChunk); - FullDataArrayAccessor newFullDataAccessor = new FullDataArrayAccessor(this.mapping, new long[this.dataPointsPerSection * this.dataPointsPerSection][], this.dataPointsPerSection); - newFullDataAccessor.downsampleFrom(sourceChunk); - this.sparseData[(xOffset + xDataOffset) * this.sectionCount + (zOffset + zDataOffset)] = newFullDataAccessor; - } - } - } - private void sampleFrom(HighDetailIncompleteFullDataSource sparseDataSource) - { - DhSectionPos pos = sparseDataSource.getSectionPos(); - this.isEmpty = false; - - DhLodPos basePos = this.sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); - DhLodPos dataPos = pos.getMinCornerLodPos(SPARSE_UNIT_DETAIL); - - int offsetX = dataPos.x - basePos.x; - int offsetZ = dataPos.z - basePos.z; - LodUtil.assertTrue(offsetX >= 0 && offsetX < this.sectionCount && offsetZ >= 0 && offsetZ < this.sectionCount); - - for (int xOffset = 0; xOffset < sparseDataSource.sectionCount; xOffset++) - { - for (int zOffset = 0; zOffset < sparseDataSource.sectionCount; zOffset++) - { - FullDataArrayAccessor sourceChunk = sparseDataSource.sparseData[xOffset * sparseDataSource.sectionCount + zOffset]; - if (sourceChunk != null) - { - FullDataArrayAccessor newFullDataAccessor = new FullDataArrayAccessor(this.mapping, new long[this.dataPointsPerSection * this.dataPointsPerSection][], this.dataPointsPerSection); - newFullDataAccessor.downsampleFrom(sourceChunk); - this.sparseData[(xOffset + offsetX) * this.sectionCount + (zOffset + offsetZ)] = newFullDataAccessor; - } - } - } - } - private void sampleFrom(LowDetailIncompleteFullDataSource spottyDataSource) - { - // TODO implement - -// DhSectionPos pos = spottyDataSource.getSectionPos(); -// this.isEmpty = false; -// -// DhLodPos basePos = this.sectionPos.getCorner(SPARSE_UNIT_DETAIL); -// DhLodPos dataPos = pos.getCorner(SPARSE_UNIT_DETAIL); -// -// int coveredChunks = pos.getWidth(SPARSE_UNIT_DETAIL).numberOfLodSectionsWide; -// int sourceDataPerChunk = SPARSE_UNIT_SIZE >>> spottyDataSource.getDataDetailLevel(); -// LodUtil.assertTrue((coveredChunks * sourceDataPerChunk) == CompleteFullDataSource.WIDTH); -// -// int xDataOffset = dataPos.x - basePos.x; -// int zDataOffset = dataPos.z - basePos.z; -// LodUtil.assertTrue(xDataOffset >= 0 && xDataOffset < this.sectionCount && zDataOffset >= 0 && zDataOffset < this.sectionCount); -// -// for (int xOffset = 0; xOffset < coveredChunks; xOffset++) -// { -// for (int zOffset = 0; zOffset < coveredChunks; zOffset++) -// { -// FullDataArrayAccessor sourceChunk = spottyDataSource.subView(sourceDataPerChunk, xOffset * sourceDataPerChunk, zOffset * sourceDataPerChunk); -// FullDataArrayAccessor newFullDataAccessor = new FullDataArrayAccessor(this.mapping, new long[this.dataPointsPerSection * this.dataPointsPerSection][], this.dataPointsPerSection); -// newFullDataAccessor.downsampleFrom(sourceChunk); -// this.sparseData[(xOffset + xDataOffset) * this.sectionCount + (zOffset + zDataOffset)] = newFullDataAccessor; -// } -// } - } - - private void applyToFullDataSource(CompleteFullDataSource dataSource) { LodUtil.assertTrue(dataSource.getSectionPos().equals(this.sectionPos)); @@ -670,16 +570,13 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo return this; } } - isPromoted = true; + this.isPromoted = true; CompleteFullDataSource fullDataSource = CompleteFullDataSource.createEmpty(this.sectionPos); this.applyToFullDataSource(fullDataSource); return fullDataSource; } @Override - public boolean hasBeenPromoted() - { - return isPromoted; - } + public boolean hasBeenPromoted() { return this.isPromoted; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java index 537797155..10f307c8d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java @@ -284,7 +284,23 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp //======// @Override - public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) { return this.isColumnNotEmpty.get(relativeX * WIDTH + relativeZ) ? this.get(relativeX, relativeZ) : null; } + public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, false); } + @Override + public SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, true); } + private SingleColumnFullDataAccessor tryGetOrCreate(int relativeX, int relativeZ, boolean createIfMissing) + { + int notEmptyIndex = relativeX * WIDTH + relativeZ; + boolean columnEmpty = this.isColumnNotEmpty.get(notEmptyIndex); + + // "create" the missing column if necessary + if (columnEmpty && createIfMissing) + { + this.isColumnNotEmpty.set(notEmptyIndex, true); + columnEmpty = false; + } + + return !columnEmpty ? this.get(relativeX, relativeZ) : null; + } @@ -314,6 +330,7 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp @Override public boolean isEmpty() { return this.isEmpty; } + @Override public void markNotEmpty() { this.isEmpty = false; } @Override @@ -359,170 +376,6 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp } - @Override - public void sampleFrom(IFullDataSource fullDataSource) - { - DhSectionPos pos = fullDataSource.getSectionPos(); - LodUtil.assertTrue(pos.getDetailLevel() < this.sectionPos.getDetailLevel()); - LodUtil.assertTrue(pos.overlapsExactly(this.sectionPos)); - - if (fullDataSource.isEmpty()) - { - return; - } - - - if (fullDataSource instanceof CompleteFullDataSource) - { - this.sampleFrom((CompleteFullDataSource) fullDataSource); - } - else if (fullDataSource instanceof HighDetailIncompleteFullDataSource) - { - this.sampleFrom((HighDetailIncompleteFullDataSource) fullDataSource); - } - else if (fullDataSource instanceof LowDetailIncompleteFullDataSource) - { - this.sampleFrom((LowDetailIncompleteFullDataSource) fullDataSource); -// LodUtil.assertNotReach("SampleFrom not implemented for ["+IFullDataSource.class.getSimpleName()+"] with class ["+fullDataSource.getClass().getSimpleName()+"]."); - } - else - { - // TODO implement - LodUtil.assertNotReach("SampleFrom not implemented for [" + this.getClass().getSimpleName() + "] with class [" + fullDataSource.getClass().getSimpleName() + "]."); - } - } - - private void sampleFrom(HighDetailIncompleteFullDataSource sparseSource) - { - DhLodPos thisLodPos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); - DhSectionPos pos = sparseSource.getSectionPos(); - - this.isEmpty = false; - - if (this.getDataDetailLevel() > this.sectionPos.getDetailLevel()) - { - DhLodPos dataLodPos = pos.getMinCornerLodPos(this.getDataDetailLevel()); - - int offsetX = dataLodPos.x - thisLodPos.x; - int offsetZ = dataLodPos.z - thisLodPos.z; - LodUtil.assertTrue(offsetX >= 0 && offsetX < WIDTH && offsetZ >= 0 && offsetZ < WIDTH); - - int chunksPerData = 1 << (this.getDataDetailLevel() - HighDetailIncompleteFullDataSource.SPARSE_UNIT_DETAIL); - int dataSpan = this.sectionPos.getWidthCountForLowerDetailedSection(this.getDataDetailLevel()); - - for (int xOffset = 0; xOffset < dataSpan; xOffset++) - { - for (int zOffset = 0; zOffset < dataSpan; zOffset++) - { - SingleColumnFullDataAccessor column = sparseSource.tryGet( - xOffset * chunksPerData * sparseSource.dataPointsPerSection, - zOffset * chunksPerData * sparseSource.dataPointsPerSection); - - if (column != null) - { - column.deepCopyTo(this.get(offsetX + xOffset, offsetZ + zOffset)); - this.isColumnNotEmpty.set((offsetX + xOffset) * WIDTH + offsetZ + zOffset, true); - } - } - } - } - else - { - DhLodPos dataLodPos = pos.getSectionBBoxPos(); - int lowerSectionsPerData = this.sectionPos.getWidthCountForLowerDetailedSection(dataLodPos.detailLevel); - if (dataLodPos.x % lowerSectionsPerData != 0 || dataLodPos.z % lowerSectionsPerData != 0) - { - return; - } - - - dataLodPos = dataLodPos.convertToDetailLevel(this.getDataDetailLevel()); - int offsetX = dataLodPos.x - thisLodPos.x; - int offsetZ = dataLodPos.z - thisLodPos.z; - - SingleColumnFullDataAccessor column = sparseSource.tryGet(0, 0); - if (column != null) - { - column.deepCopyTo(this.get(offsetX, offsetZ)); - this.isColumnNotEmpty.set(offsetX * WIDTH + offsetZ, true); - } - } - } - - private void sampleFrom(CompleteFullDataSource inputSource) - { - DhSectionPos inputPos = inputSource.getSectionPos(); - this.isEmpty = false; - - - DhLodPos baseOffset = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); - DhSectionPos inputOffset = inputPos.convertNewToDetailLevel(this.getDataDetailLevel()); - int offsetX = inputOffset.getX() - baseOffset.x; - int offsetZ = inputOffset.getZ() - baseOffset.z; - - - int numberOfDataPointsToUpdate = WIDTH / this.sectionPos.getWidthCountForLowerDetailedSection(inputSource.getSectionPos().getDetailLevel()); // can be 0 if the input source is significantly smaller than this data source - // should be 1 at minimum, to prevent divide by zero errors (and because trying to get 0 data points doesn't make any sense) - numberOfDataPointsToUpdate = Math.max(1, numberOfDataPointsToUpdate); - - - int inputFractionWidth = inputSource.width() / numberOfDataPointsToUpdate; - for (int x = 0; x < numberOfDataPointsToUpdate; x++) - { - for (int z = 0; z < numberOfDataPointsToUpdate; z++) - { - SingleColumnFullDataAccessor thisDataColumn = this.get(offsetX + x, offsetZ + z); - SingleColumnFullDataAccessor inputDataColumn = inputSource.get(inputFractionWidth * x, inputFractionWidth * z); - inputDataColumn.deepCopyTo(thisDataColumn); - - int notEmptyIndex = (offsetX + x) * WIDTH + (offsetZ + z); - this.isColumnNotEmpty.set(notEmptyIndex, true); - } - } - } - - private void sampleFrom(LowDetailIncompleteFullDataSource spottySource) - { - DhSectionPos pos = spottySource.getSectionPos(); - this.isEmpty = false; - this.downsampleFrom(spottySource); - - - if (this.getDataDetailLevel() > this.sectionPos.getDetailLevel()) - { - DhLodPos thisLodPos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); - DhLodPos dataLodPos = pos.getMinCornerLodPos(this.getDataDetailLevel()); - - int offsetX = dataLodPos.x - thisLodPos.x; - int offsetZ = dataLodPos.z - thisLodPos.z; - int dataWidth = this.sectionPos.getWidthCountForLowerDetailedSection(this.getDataDetailLevel()); - - for (int xOffset = 0; xOffset < dataWidth; xOffset++) - { - for (int zOffset = 0; zOffset < dataWidth; zOffset++) - { - this.isColumnNotEmpty.set((offsetX + xOffset) * WIDTH + offsetZ + zOffset, true); - } - } - } - else - { - DhLodPos dataPos = pos.getSectionBBoxPos(); - int lowerSectionsPerData = this.sectionPos.getWidthCountForLowerDetailedSection(dataPos.detailLevel); - if (dataPos.x % lowerSectionsPerData != 0 || dataPos.z % lowerSectionsPerData != 0) - { - return; - } - - - DhLodPos basePos = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel()); - dataPos = dataPos.convertToDetailLevel(this.getDataDetailLevel()); - int offsetX = dataPos.x - basePos.x; - int offsetZ = dataPos.z - basePos.z; - this.isColumnNotEmpty.set(offsetX * WIDTH + offsetZ, true); - } - } - @Override public IFullDataSource tryPromotingToCompleteDataSource() { @@ -535,15 +388,13 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp { return this; } - isPromoted = true; + this.isPromoted = true; return new CompleteFullDataSource(this.sectionPos, this.mapping, this.dataArrays); } @Override - public boolean hasBeenPromoted() - { - return isPromoted; - } + public boolean hasBeenPromoted() { return this.isPromoted; } + //================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java index b7e0770e8..401b4e152 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java @@ -69,6 +69,7 @@ public interface IFullDataSource void update(ChunkSizedFullDataAccessor data); boolean isEmpty(); + void markNotEmpty(); /** AKA; the max relative position that {@link IFullDataSource#tryGet(int, int)} can accept for either X or Z */ int getWidthInDataPoints(); @@ -85,6 +86,11 @@ public interface IFullDataSource * @return null if the data doesn't exist */ SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ); + /** + * Attempts to get the data column for the given relative x and z position.
+ * If no data exists yet an empty data column will be created. + */ + SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ); FullDataPointIdMap getMapping(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java index 76e6cb606..0b794d08f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java @@ -19,7 +19,11 @@ package com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces; +import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.pos.DhLodPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.util.LodUtil; public interface IIncompleteFullDataSource extends IFullDataSource { @@ -28,7 +32,47 @@ public interface IIncompleteFullDataSource extends IFullDataSource * * This can be used to either merge same sized data sources or downsample to */ - void sampleFrom(IFullDataSource fullDataSource); + default void sampleFrom(IFullDataSource inputSource) + { + DhSectionPos inputPos = inputSource.getSectionPos(); + DhSectionPos thisPos = this.getSectionPos(); + LodUtil.assertTrue(inputPos.getDetailLevel() < thisPos.getDetailLevel()); + LodUtil.assertTrue(inputPos.overlapsExactly(this.getSectionPos())); + + if (inputSource.isEmpty()) + { + return; + } + + + this.markNotEmpty(); + + DhLodPos baseOffset = thisPos.getMinCornerLodPos(this.getDataDetailLevel()); + DhSectionPos inputOffset = inputPos.convertNewToDetailLevel(this.getDataDetailLevel()); + int offsetX = inputOffset.getX() - baseOffset.x; + int offsetZ = inputOffset.getZ() - baseOffset.z; + + + int numberOfDataPointsToUpdate = this.getWidthInDataPoints() / thisPos.getWidthCountForLowerDetailedSection(inputSource.getSectionPos().getDetailLevel()); // can be 0 if the input source is significantly smaller than this data source + // should be 1 at minimum, to prevent divide by zero errors (and because trying to get 0 or a fractional data point doesn't make any sense) + numberOfDataPointsToUpdate = Math.max(1, numberOfDataPointsToUpdate); + + + int inputFractionWidth = inputSource.getWidthInDataPoints() / numberOfDataPointsToUpdate; + for (int x = 0; x < numberOfDataPointsToUpdate; x++) + { + for (int z = 0; z < numberOfDataPointsToUpdate; z++) + { + SingleColumnFullDataAccessor thisDataColumn = this.getOrCreate(offsetX + x, offsetZ + z); + SingleColumnFullDataAccessor inputDataColumn = inputSource.tryGet(inputFractionWidth * x, inputFractionWidth * z); + + if (inputDataColumn != null) + { + inputDataColumn.deepCopyTo(thisDataColumn); + } + } + } + } /** * Attempts to convert this {@link IIncompleteFullDataSource} into a {@link CompleteFullDataSource}. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 539db7fa4..59d9561b9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -386,7 +386,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider FullDataMetaFile existingFile = existingFiles.get(i); - CompletableFuture loadFileFuture = usePooledDataSources ? existingFile.getOrLoadCachedDataSourceAsync() : existingFile.getDataSourceWithoutCachingAsync(); + CompletableFuture loadFileFuture = usePooledDataSources ? existingFile.getDataSourceWithoutCachingAsync() : existingFile.getOrLoadCachedDataSourceAsync(); CompletableFuture sampleSourceFuture = loadFileFuture.whenComplete((existingFullDataSource, ex) -> { @@ -415,7 +415,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider } // pooling temporary data sources massively reduces garbage collector overhead when just sampling (going from ~8 GB/sec to ~90 MB/sec) - if (!usePooledDataSources && !existingFile.cacheLoadingDataSource) + if (usePooledDataSources && !existingFile.cacheLoadingDataSource) { existingFile.clearCachedDataSource(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index 53dee52a2..72ae6e373 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -277,7 +277,7 @@ public class LodQuadTree extends QuadTree implements AutoClose { // this is the detail level we want to render // // prepare this section for rendering - renderSection.loadRenderSource(this.renderSourceProvider, this.level); + renderSection.loadRenderSource(this.renderSourceProvider, this.level); // TODO this should fire for the lowest detail level first, wait for it to finish then fire the next highest to prevent waiting forever for 2 million chunk section to finish sampling everything // wait for the parent to disable before enabling this section, so we don't overdraw/overlap render sections if (!parentRenderSectionIsEnabled && renderSection.canRenderNow()) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index b4b5ce6b4..a596a8650 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -333,7 +333,7 @@ "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Server Folder Mode", - "distanthorizons.config.client.multiplayer.serverFolderNameMode.@tooltip": + "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": "Determines the folder format for local multiplayer data.\n\n§6Name Only:§r\nUses the server browser name. Ex: \"Minecraft Server\"\n§6Name IP:§r\n\"Minecraft Server, IP 192.168.1.40\"\n§6Name, IP, Port:§r\n\"Minecraft Server, IP 192.168.1.40:25565\"\n§6Name, IP, Port, MC Version:§r\n\"Minecraft Server, IP 192.168.1.40:25565, GameVersion 1.18.1\"\n\n§c§lCaution:§r changing while connected to a multiplayer server may cause glitches.", "distanthorizons.config.client.advanced.multiplayer.multiverseSimilarityRequiredPercent": "Multiverse Required Similarity %", diff --git a/core/src/main/resources/assets/distanthorizons/lang/ru_ru.json b/core/src/main/resources/assets/distanthorizons/lang/ru_ru.json index 9f3a0e78a..3508d1c84 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/ru_ru.json +++ b/core/src/main/resources/assets/distanthorizons/lang/ru_ru.json @@ -1,94 +1,62 @@ { "distanthorizons.title": - "Distant Horizons", + "Distant Horizons", - "distanthorizons.general.true": - "Вкл.", - "distanthorizons.general.false": - "Выкл.", - "distanthorizons.general.yes": - "Да", - "distanthorizons.general.no": - "Нет", - "distanthorizons.general.back": - "Назад", - "distanthorizons.general.next": - "Следующий", - "distanthorizons.general.done": - "Готово", - "distanthorizons.general.cancel": - "Отмена", - "distanthorizons.general.reset": - "Сброc", + "distanthorizons.general.true": "Вкл.", + "distanthorizons.general.false": "Выкл.", + "distanthorizons.general.reset": "Сброc", + "distanthorizons.general.back": "Назад", + "distanthorizons.general.next": "Следующий", + "distanthorizons.general.done": "Готово", + "distanthorizons.general.cancel": "Отмена", + "distanthorizons.general.yes": "Да", + "distanthorizons.general.no": "Нет", - "distanthorizons.updater.title": - "Автоматическое обновление Distant Horizons", - "distanthorizons.updater.text1": - "§lДоступно новое обновление!", - "distanthorizons.updater.text2": - "§fВы хотите перейти с %s§f на %s§f?", - "distanthorizons.updater.later": - "Не сейчас", - "distanthorizons.updater.never": - "Не показывать снова", - "distanthorizons.updater.update": - "Обновить", - "distanthorizons.updater.update.@tooltip": - "Обновить мод 1 раз\n(обновляется при закрытии игры)", - "distanthorizons.updater.silent": - "Всегда тихое обновление", - "distanthorizons.updater.silent.@tooltip": - "Каждый раз, когда доступно обновление, оно будет обновляться\n(§6ПРЕДУПРЕЖДЕНИЕ§r: Оно не будет запрашивать у пользователя разрешение на обновление, но будет поддерживать мод в актуальном состоянии)", - "distanthorizons.updater.waitingForClose": - "Обновление Distant Horizons завершится после перезапуска игры", + "distanthorizons.updater.title": "Автоматическое обновление Distant Horizons", + "distanthorizons.updater.text1": "§lДоступно новое обновление!", + "distanthorizons.updater.text2": "§fВы хотите перейти с %s§f на %s§f?", + "distanthorizons.updater.later": "Не сейчас", + "distanthorizons.updater.never": "Не показывать снова", + "distanthorizons.updater.update": "Обновить", + "distanthorizons.updater.update.@tooltip": "Обновить мод 1 раз\n(обновляется при закрытии игры)", + "distanthorizons.updater.silent": "Всегда тихое обновление", + "distanthorizons.updater.silent.@tooltip": "Каждый раз, когда доступно обновление, оно будет обновляться\n(§6ПРЕДУПРЕЖДЕНИЕ§r: Оно не будет запрашивать у пользователя разрешение на обновление, но будет поддерживать мод в актуальном состоянии)", + "distanthorizons.updater.waitingForClose": "Обновление Distant Horizons завершится после перезапуска игры", - "distanthorizons.config.title": - "Конфигурация Distant Horizons", - "distanthorizons.config.client": - "Клиент", + "distanthorizons.config.title": "Конфигурация Distant Horizons", - "distanthorizons.config.client.quickEnableRendering": - "Включить рендеринг", - "distanthorizons.config.client.quickEnableRendering.@tooltip": - "Определяет, отображает ли Distant Horizons LODs.", - - "distanthorizons.config.client.qualityPresetSetting": - "Предустановленное качество", - "distanthorizons.config.client.qualityPresetSetting.@tooltip": - "Изменяет ряд графических настроек для быстрого изменения качества рендеринга Distant Horizons. \n\nУменьшите этот параметр, если ваш графический процессор максимально загружен или у вас возникли проблемы с частотой кадров.", - "distanthorizons.config.client.threadPresetSetting": - "Загруженность процессора", - "distanthorizons.config.client.threadPresetSetting.@tooltip": - "Изменяет количество потоков, которые будут использоваться в Distant Horizons. \n\nУвеличение этого параметра улучшит скорость удаленного генератора (Distant Generator) и скорость загрузки LOD, \nно также увеличит использование процессора/памяти и может привести к заиканию. \n\nПримечание: на процессорах с 4 ядрами или меньше эти настройки будут менее эффективными, \nи некоторые настройки дадут аналогичные результаты.", - - "distanthorizons.config.client.optionsButton": - "Отобразить кнопку Параметров", - "distanthorizons.config.client.optionsButton.@tooltip": - "Показать кнопку Параметров слева от кнопки FOV.", + "distanthorizons.config.client": "Клиент", + + "distanthorizons.config.client.quickEnableRendering": "Включить рендеринг", + "distanthorizons.config.client.quickEnableRendering.@tooltip": "Определяет, отображает ли Distant Horizons LODs.", + "distanthorizons.config.client.qualityPresetSetting": "Предустановленное качество", + "distanthorizons.config.client.qualityPresetSetting.@tooltip": "Изменяет ряд графических настроек для быстрого изменения качества рендеринга Distant Horizons. \n\nУменьшите этот параметр, если ваш графический процессор максимально загружен или у вас возникли проблемы с частотой кадров.", + "distanthorizons.config.client.threadPresetSetting": "Загруженность процессора", + "distanthorizons.config.client.threadPresetSetting.@tooltip": "Изменяет количество потоков, которые будут использоваться в Distant Horizons. \n\nУвеличение этого параметра улучшит скорость удаленного генератора (Distant Generator) и скорость загрузки LOD, \nно также увеличит использование процессора/памяти и может привести к заиканию. \n\nПримечание: на процессорах с 4 ядрами или меньше эти настройки будут менее эффективными, \nи некоторые настройки дадут аналогичные результаты.", + "distanthorizons.config.client.optionsButton": "Отобразить кнопку Параметров", + "distanthorizons.config.client.optionsButton.@tooltip": "Показать кнопку Параметров слева от кнопки FOV.", - "distanthorizons.config.client.advanced": - "Расширенные параметры", + "distanthorizons.config.client.advanced": "Расширенные параметры", - "distanthorizons.config.client.advanced.graphics": - "Графика", - "distanthorizons.config.client.advanced.graphics.quality": - "Качество рендеринга", + "distanthorizons.config.client.advanced.graphics": "Графика", + + "distanthorizons.config.client.advanced.graphics.quality": "Качество рендеринга", "distanthorizons.config.client.advanced.graphics.quality.maxHorizontalResolution": "Максимальное горизонтальное разрешение", "distanthorizons.config.client.advanced.graphics.quality.maxHorizontalResolution.@tooltip": "Изображения с максимальной детализацией отображаются на.\n\n§6Самый быстрый:§r Чанк\n§6Самый красивый:§r Блок", "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistance": "Расстояние рендеринга LOD", "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistance.@tooltip": "Расстояние рендеринга Distant Horizons, измеряемое в чанках. \n\nПримечание: это максимально возможное число. \nДальность рендеринга может быть выше или ниже этого числа \nв зависимости от других графических настроек.", "distanthorizons.config.client.advanced.graphics.quality.verticalQuality": "Вертикальное качество", - "distanthorizons.config.client.advanced.graphics.quality.verticalQuality.@tooltip": "На сколько хорошо LOD (уровень детализации) отображает выступы, пещеры, обрывы и т.д.\n\nБолее высокие параметры будут увеличивать использование памяти и GPU.", + "distanthorizons.config.client.advanced.graphics.quality.verticalQuality.@tooltip": "На сколько хорошо LOD отображает выступы, пещеры, обрывы и т.д.\n\nБолее высокие параметры будут увеличивать использование памяти и GPU.", "distanthorizons.config.client.advanced.graphics.quality.horizontalScale": "Горизонтальная шкала", "distanthorizons.config.client.advanced.graphics.quality.horizontalScale.@tooltip": "На сколько быстро уровни детализации (LOD) снижают качество.\n\nБольшие значения улучшат вид дальней местности, \nно увеличат использование памяти и GPU.", "distanthorizons.config.client.advanced.graphics.quality.horizontalQuality": "Горизонтальное качество", @@ -105,11 +73,11 @@ "distanthorizons.config.client.advanced.graphics.fog": "Туман", "distanthorizons.config.client.advanced.graphics.fog.drawMode": "Режим отображения тумана", - "distanthorizons.config.client.advanced.graphics.fog.drawMode.@tooltip": "Когда будет отрисовываться туман на LODs (уровнях детализации).", + "distanthorizons.config.client.advanced.graphics.fog.drawMode.@tooltip": "Когда будет отрисовываться туман на LOD-ах (уровнях детализации).", "distanthorizons.config.client.advanced.graphics.fog.distance": "Дальность тумана", - "distanthorizons.config.client.advanced.graphics.fog.distance.@tooltip": "Дальность(и), на которой туман будет отрисовываться на LODs.", + "distanthorizons.config.client.advanced.graphics.fog.distance.@tooltip": "Дальность(и), на которой туман будет отрисовываться на LOD-ах.", "distanthorizons.config.client.advanced.graphics.fog.colorMode": "Режим цвета тумана", - "distanthorizons.config.client.advanced.graphics.fog.colorMode.@tooltip": "Цвет тумана на LODs.", + "distanthorizons.config.client.advanced.graphics.fog.colorMode.@tooltip": "Цвет тумана на LOD-ах.", "distanthorizons.config.client.advanced.graphics.fog.disableVanillaFog": "Отключить ванильный туман", "distanthorizons.config.client.advanced.graphics.fog.disableVanillaFog.@tooltip": "§6Вкл.:§r отключает туман Minecraft на ванильных чанках. \n§6Выкл.:§r Minecraft отрисовывает туман как обычно. \n\nМожет вызывать проблемы с другими модами, изменяющими туман. \nОтключите, если ванильные чанки полностью покрыты туманом.", "distanthorizons.config.client.advanced.graphics.fog.advancedFog": "Дополнительные настройки тумана", @@ -127,12 +95,12 @@ "distanthorizons.config.client.advanced.graphics.fog.advancedFog.farFogDensity.@tooltip": "Какая должна быть плотность дальнего тумана? ", - "distanthorizons.config.client.advanced.graphics.ssao": "Окружающая окклюзия (Ambient Occlusion)", + "distanthorizons.config.client.advanced.graphics.ssao": "Окружающая окклюзия", "distanthorizons.config.client.advanced.graphics.ssao.enabled": "Включить окружающую окклюзию (Ambient Occlusion)", "distanthorizons.config.client.advanced.graphics.ssao.enabled.@tooltip": "Окружающая окклюзия добавляет глубину к освещению блоков.", "distanthorizons.config.client.advanced.graphics.ssao.sampleCount": "Количество образцов", - "distanthorizons.config.client.advanced.graphics.ssao.sampleCount.@tooltip": "Определяет, сколько точек в пространстве отбирается для теста окклюзии. \nБольшее количество улучшит качество и уменьшит полосирование, но увеличит нагрузку на ГПУ.", + "distanthorizons.config.client.advanced.graphics.ssao.sampleCount.@tooltip": "Определяет, сколько точек в пространстве отбирается для теста окклюзии. \nБольшее количество улучшит качество и уменьшит полосирование, но увеличит нагрузку на графический процессор.", "distanthorizons.config.client.advanced.graphics.ssao.radius": "Радиус", "distanthorizons.config.client.advanced.graphics.ssao.radius.@tooltip": "Определяет радиус применения эффекта окружающей окклюзии в блоках.", "distanthorizons.config.client.advanced.graphics.ssao.strength": "Сила", @@ -232,25 +200,20 @@ "distanthorizons.config.client.advanced.multiThreading.numberOfWorldGenerationThreads": "Количество потоков генерации мира", "distanthorizons.config.client.advanced.multiThreading.numberOfWorldGenerationThreads.@tooltip": "Сколько потоков следует использовать при генерации LODs вне обычной зоны отрисовки? \n\nЕсли это число меньше 1, оно будет рассматриваться как процент времени, в течение которого один поток может работать, прежде чем перейти в режим ожидания. \n\nЕсли у вас есть проблемы с прерываниями при генерации дальних LODs, уменьшите это число. Если вы хотите увеличить скорость генерации LOD, увеличьте это число. \n\nКоличество потоков для генерации LOD и количество потоков для построения буферов независимы друг от друга. Если их сумма больше количества ядер вашего процессора, это нормально.", "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForWorldGenerationThreads": "Процент времени выполнения для потоков генерации мира", - "distanthorizons.config.client.advanced.multiThreading.numberOfBufferBuilderThreads": "Количество потоков построения буферов", "distanthorizons.config.client.advanced.multiThreading.numberOfBufferBuilderThreads.@tooltip": "Количество потоков, используемых при построении данных геометрии. \nМожет быть только от 1 до количества процессорных ядер вашего CPU.", "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForBufferBuilderThreads": "Процент времени выполнения для потоков построения буферов", - "distanthorizons.config.client.advanced.multiThreading.numberOfFileHandlerThreads": "Количество потоков обработки файлов", "distanthorizons.config.client.advanced.multiThreading.numberOfFileHandlerThreads.@tooltip": "Количество потоков, используемых при построении буферов вершин (вещей, отправляемых на ваш GPU для отрисовки LODs). \nМожет быть только от 1 до количества процессорных ядер вашего CPU.", "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForFileHandlerThreads": "Процент времени выполнения для потоков обработки файлов", - "distanthorizons.config.client.advanced.multiThreading.numberOfDataTransformerThreads": "Количество потоков преобразования данных", "distanthorizons.config.client.advanced.multiThreading.numberOfDataTransformerThreads.@tooltip": "Количество потоков, используемых при преобразовании данных ID в данные, подходящие для отрисовки. \n(Это, как правило, происходит при генерации нового ландшафта или изменении настроек графики). \nМожет быть только от 1 до количества процессорных ядер вашего CPU.", "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForDataTransformerThreads": "Процент времени выполнения для потоков преобразования данных", - "distanthorizons.config.client.advanced.multiThreading.numberOfChunkLodConverterThreads": "Количество потоков преобразования LOD чанков", "distanthorizons.config.client.advanced.multiThreading.numberOfChunkLodConverterThreads.@tooltip": "Сколько потоков следует использовать для преобразования чанков Minecraft в данные LOD? \nЭти потоки работают как при генерации мира, так и при загрузке, выгрузке и модификации чанков.", "distanthorizons.config.client.advanced.multiThreading.runTimeRatioForChunkLodConverterThreads": "Процент времени выполнения для потоков преобразования LOD чанков", - "distanthorizons.config.client.advanced.debugging": "Отладка", "distanthorizons.config.client.advanced.debugging.rendererMode": "Режим рендера", "distanthorizons.config.client.advanced.debugging.debugRendering": "Отладка рендера", @@ -331,7 +294,6 @@ "distanthorizons.config.client.advanced.debugging.debugWireframe.showRenderDataFileStatus": "Показать статус файла данных отрисовки", "distanthorizons.config.client.resetSettingsConfirmation": "Сбросить все настройки?", - "distanthorizons.config.client.resetSettingsConfirmation.resetConfirmationNote": "Вы уверены? Это действие нельзя отменить!", "distanthorizons.config.client.resetSettingsConfirmation.resetAllSettings": "Сбросить все настройки", @@ -470,31 +432,19 @@ "distanthorizons.config.enum.ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE": "Файл: Информация, Чат: Предупреждения", "distanthorizons.config.enum.ELoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE": "Файл: Информация, Чат: Ошибки", - "distanthorizons.config.enum.EGpuUploadMethod.AUTO": - "Авто", - "distanthorizons.config.enum.EGpuUploadMethod.BUFFER_STORAGE": - "Хранение буфера", - "distanthorizons.config.enum.EGpuUploadMethod.SUB_DATA": - "Данные SUB", - "distanthorizons.config.enum.EGpuUploadMethod.BUFFER_MAPPING": - "Отображение буфера", - "distanthorizons.config.enum.EGpuUploadMethod.DATA": - "Данные", + "distanthorizons.config.enum.EGpuUploadMethod.AUTO": "Авто", + "distanthorizons.config.enum.EGpuUploadMethod.BUFFER_STORAGE": "Хранение буфера", + "distanthorizons.config.enum.EGpuUploadMethod.SUB_DATA": "Данные SUB", + "distanthorizons.config.enum.EGpuUploadMethod.BUFFER_MAPPING": "Отображение буфера", + "distanthorizons.config.enum.EGpuUploadMethod.DATA": "Данные", - "distanthorizons.config.enum.EBufferRebuildTimes.CONSTANT": - "Постоянно", - "distanthorizons.config.enum.EBufferRebuildTimes.FREQUENT": - "Часто", - "distanthorizons.config.enum.EBufferRebuildTimes.NORMAL": - "Обычно", - "distanthorizons.config.enum.EBufferRebuildTimes.RARE": - "Редко", + "distanthorizons.config.enum.EBufferRebuildTimes.CONSTANT": "Постоянно", + "distanthorizons.config.enum.EBufferRebuildTimes.FREQUENT": "Часто", + "distanthorizons.config.enum.EBufferRebuildTimes.NORMAL": "Обычно", + "distanthorizons.config.enum.EBufferRebuildTimes.RARE": "Редко", - "distanthorizons.config.enum.ELodShading.MINECRAFT": - "Майнкрафт-овское", - "distanthorizons.config.enum.ELodShading.OLD_LIGHTING": - "Старое освещение", - "distanthorizons.config.enum.ELodShading.NONE": - "Ничего" + "distanthorizons.config.enum.ELodShading.MINECRAFT": "Майнкрафт-овское", + "distanthorizons.config.enum.ELodShading.OLD_LIGHTING": "Старое освещение", + "distanthorizons.config.enum.ELodShading.NONE": "Ничего" }