From c45f9f442ff86b1c541e124a0e0d6d1d6ee2f7c2 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 8 Jul 2024 07:32:29 -0500 Subject: [PATCH] Clean up beacon render handling logic --- .../core/level/AbstractDhLevel.java | 208 +++------------- .../core/level/DhClientLevel.java | 1 + .../core/level/DhClientServerLevel.java | 1 + .../core/level/DhServerLevel.java | 1 + .../distanthorizons/core/level/IDhLevel.java | 1 - .../renderer/generic/BeaconRenderHandler.java | 228 ++++++++++++++++++ .../renderer/generic/CloudRenderHandler.java | 24 +- .../generic/GenericObjectRenderer.java | 11 +- 8 files changed, 288 insertions(+), 187 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index 5dfccea8c..0d2a02812 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -32,6 +32,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler; import com.seibel.distanthorizons.core.render.renderer.generic.CloudRenderHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory; @@ -71,14 +72,10 @@ public abstract class AbstractDhLevel implements IDhLevel /** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */ protected final ConcurrentHashMap> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>(); - protected final IDhApiRenderableBoxGroup beaconBoxGroup; - protected final HashMap beaconRefCountByBlockPos = new HashMap<>(); - - protected boolean beaconGroupBound = false; - /** Will be null if clouds shouldn't be rendered for this level. */ @Nullable protected CloudRenderHandler cloudRenderHandler; + protected BeaconRenderHandler beaconRenderHandler; @@ -89,14 +86,12 @@ public abstract class AbstractDhLevel implements IDhLevel protected AbstractDhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); - - this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(new ArrayList<>(0)); - this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); - this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); - this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); - this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get())); } + /** + * Creating the repos requires access to the level file, which isn't + * available at constructor time. + */ protected void createAndSetSupportingRepos(File databaseFile) { // chunk hash @@ -125,6 +120,28 @@ public abstract class AbstractDhLevel implements IDhLevel this.beaconBeamRepo = newBeaconBeamRepo; } + /** handles any setup that needs the repos to be created */ + protected void runRepoReliantSetup() + { + GenericObjectRenderer genericRenderer = this.getGenericRenderer(); + if (genericRenderer != null) + { + // only add clouds for certain dimension types + if (!this.getLevelWrapper().hasCeiling() + && !this.getLevelWrapper().getDimensionType().isTheEnd()) + { + this.cloudRenderHandler = new CloudRenderHandler(this, genericRenderer); + } + + + // shouldn't happen, but just in case + if (this.beaconBeamRepo != null) + { + this.beaconRenderHandler = new BeaconRenderHandler(this.beaconBeamRepo, genericRenderer); + } + } + } + //=================// @@ -210,184 +227,32 @@ public abstract class AbstractDhLevel implements IDhLevel // beacon handling // //=================// - @Override - public List getAllBeamsForSectionPos(long pos) - { - if (this.beaconBeamRepo != null) - { - return this.beaconBeamRepo.getAllBeamsForPos(pos); - } - else - { - return new ArrayList<>(0); - } - } - - @Override public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList) { - // synchronized to prevent two threads from updating the same chunk at the same time - synchronized (this) + if (this.beaconRenderHandler != null) { - GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer(); - - // should always be non-null, but just in case - if (this.beaconBeamRepo != null - && genericObjectRenderer != null) - { - HashSet allPosSet = new HashSet<>(); - - // sort new beams - HashMap newBeamByPos = new HashMap<>(newBeamList.size()); - for (int i = 0; i < newBeamList.size(); i++) - { - BeaconBeamDTO beam = newBeamList.get(i); - newBeamByPos.put(beam.pos, beam); - allPosSet.add(beam.pos); - } - - // get existing beams - List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos); - HashMap existingBeamByPos = new HashMap<>(existingBeamList.size()); - for (int i = 0; i < existingBeamList.size(); i++) - { - BeaconBeamDTO beam = existingBeamList.get(i); - existingBeamByPos.put(beam.pos, beam); - allPosSet.add(beam.pos); - } - - - - for (DhBlockPos beaconPos : allPosSet) - { - if (!chunkPos.contains(beaconPos)) - { - // don't update beacons outside the updated chunk - continue; - } - - BeaconBeamDTO existingBeam = existingBeamByPos.get(beaconPos); - BeaconBeamDTO newBeam = newBeamByPos.get(beaconPos); - - if (existingBeam != null && newBeam != null) - { - // beam still exists in chunk, do nothing - } - else if (existingBeam == null && newBeam != null) - { - // new beam found, add to DB - this.beaconBeamRepo.save(newBeam); - this.startRenderingBeacon(newBeam); - } - else if (existingBeam != null && newBeam == null) - { - // beam no longer exists at position, remove - this.beaconBeamRepo.deleteWithKey(beaconPos); // TODO broken when updating adjacent chunks - this.stopRenderingBeaconAtPos(beaconPos); - } - - } - - } + this.beaconRenderHandler.setBeaconBeamsForChunk(chunkPos, newBeamList); } } @Override public void loadBeaconBeamsInPos(long pos) { - GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer(); - - // should always be non-null, but just in case - if (this.beaconBeamRepo != null - && genericObjectRenderer != null) + if (this.beaconRenderHandler != null) { - // generic setup is delayed to allow for the main renderer to start up - if (!this.beaconGroupBound) - { - this.beaconGroupBound = true; - genericObjectRenderer.add(this.beaconBoxGroup); - - if (!this.getLevelWrapper().hasCeiling() - && !this.getLevelWrapper().getDimensionType().isTheEnd()) - { - this.cloudRenderHandler = new CloudRenderHandler(this, genericObjectRenderer); - } - } - - - // get beams in pos - List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); - for (int i = 0; i < existingBeamList.size(); i++) - { - BeaconBeamDTO newBeam = existingBeamList.get(i); - this.startRenderingBeacon(newBeam); - } + this.beaconRenderHandler.loadBeaconBeamsInPos(pos); } } - @Override public void unloadBeaconBeamsInPos(long pos) { - GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer(); - - // should always be non-null, but just in case - if (this.beaconBeamRepo != null - && genericObjectRenderer != null) + if (this.beaconRenderHandler != null) { - // get beams in pos - List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); - for (int i = 0; i < existingBeamList.size(); i++) - { - BeaconBeamDTO beam = existingBeamList.get(i); - this.stopRenderingBeaconAtPos(beam.pos); - } + this.beaconRenderHandler.unloadBeaconBeamsInPos(pos); } } - private void startRenderingBeacon(BeaconBeamDTO beacon) - { - this.beaconRefCountByBlockPos.compute(beacon.pos, (beamPos, beaconRefCount) -> - { - if (beaconRefCount == null) { beaconRefCount = new AtomicInteger(); } - if (beaconRefCount.getAndIncrement() == 0) - { - DhApiRenderableBox beaconBox = new DhApiRenderableBox( - new DhApiVec3f(beacon.pos.x, beacon.pos.y+1, beacon.pos.z), - new DhApiVec3f(beacon.pos.x+1, 6_000, beacon.pos.z+1), - // TODO calculate color - beacon.color - ); - - this.beaconBoxGroup.add(beaconBox); - this.beaconBoxGroup.triggerBoxChange(); - } - return beaconRefCount; - }); - } - - private void stopRenderingBeaconAtPos(DhBlockPos beaconPos) - { - this.beaconRefCountByBlockPos.compute(beaconPos, (pos, beaconRefCount) -> - { - if (beaconRefCount != null - && beaconRefCount.decrementAndGet() <= 0) - { - this.beaconBoxGroup.removeIf((box) -> - box.minPos.x == beaconPos.x - && box.minPos.y == beaconPos.y+1 // plus 1 because the beam starts above the beacon - && box.minPos.z == beaconPos.z - ); - this.beaconBoxGroup.triggerBoxChange(); - return null; - } - else - { - return beaconRefCount; - } - }); - } - //================// @@ -400,11 +265,6 @@ public abstract class AbstractDhLevel implements IDhLevel this.chunkToLodBuilder.close(); GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer(); - if (genericObjectRenderer != null) - { - genericObjectRenderer.remove(this.beaconBoxGroup.getId()); - this.beaconGroupBound = false; - } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 15a67565d..f29b35f21 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -66,6 +66,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.clientside = new ClientLevelModule(this); this.createAndSetSupportingRepos(this.dataFileHandler.repo.databaseFile); + this.runRepoReliantSetup(); if (enableRendering) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index f97d4019f..c2f925bad 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -72,6 +72,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev this.serverside = new ServerLevelModule(this, saveStructure); this.clientside = new ClientLevelModule(this); this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); + this.runRepoReliantSetup(); LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 60d8a2fbc..238b50e46 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -54,6 +54,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel this.serverLevelWrapper = serverLevelWrapper; this.serverside = new ServerLevelModule(this, saveStructure); this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); + this.runRepoReliantSetup(); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 00e12ba3d..13b43665e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -49,7 +49,6 @@ public interface IDhLevel extends AutoCloseable void updateChunkAsync(IChunkWrapper chunk); void loadBeaconBeamsInPos(long pos); - List getAllBeamsForSectionPos(long pos); void setBeaconBeamsForChunk(DhChunkPos chunkPos, List beamList); void unloadBeaconBeamsInPos(long pos); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java new file mode 100644 index 000000000..8593f871d --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java @@ -0,0 +1,228 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 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.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class BeaconRenderHandler +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + + /** if this is null then the other handler is probably null too, but just in case */ + private final BeaconBeamRepo beaconBeamRepo; + + private final IDhApiRenderableBoxGroup beaconBoxGroup; + private final HashMap beaconRefCountByBlockPos = new HashMap<>(); + + + + //=============// + // constructor // + //=============// + + public BeaconRenderHandler(@NotNull BeaconBeamRepo beaconBeamRepo, @NotNull GenericObjectRenderer renderer) + { + this.beaconBeamRepo = beaconBeamRepo; + + this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(new ArrayList<>(0)); + this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); + this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get())); + + renderer.add(this.beaconBoxGroup); + } + + + + // + // + // + + public List getAllBeamsForSectionPos(long pos) + { + if (this.beaconBeamRepo != null) + { + return this.beaconBeamRepo.getAllBeamsForPos(pos); + } + else + { + return new ArrayList<>(0); + } + } + + public void setBeaconBeamsForChunk(DhChunkPos chunkPos, java.util.List newBeamList) + { + // TODO move beacon handling to its own BeaconRenderHandler class + + // synchronized to prevent two threads from updating the same chunk at the same time + synchronized (this) + { + HashSet allPosSet = new HashSet<>(); + + // sort new beams + HashMap newBeamByPos = new HashMap<>(newBeamList.size()); + for (int i = 0; i < newBeamList.size(); i++) + { + BeaconBeamDTO beam = newBeamList.get(i); + newBeamByPos.put(beam.pos, beam); + allPosSet.add(beam.pos); + } + + // get existing beams + java.util.List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos); + HashMap existingBeamByPos = new HashMap<>(existingBeamList.size()); + for (int i = 0; i < existingBeamList.size(); i++) + { + BeaconBeamDTO beam = existingBeamList.get(i); + existingBeamByPos.put(beam.pos, beam); + allPosSet.add(beam.pos); + } + + + + for (DhBlockPos beaconPos : allPosSet) + { + if (!chunkPos.contains(beaconPos)) + { + // don't update beacons outside the updated chunk + continue; + } + + BeaconBeamDTO existingBeam = existingBeamByPos.get(beaconPos); + BeaconBeamDTO newBeam = newBeamByPos.get(beaconPos); + + if (existingBeam != null && newBeam != null) + { + // beam still exists in chunk, do nothing + } + else if (existingBeam == null && newBeam != null) + { + // new beam found, add to DB + this.beaconBeamRepo.save(newBeam); + this.startRenderingBeacon(newBeam); + } + else if (existingBeam != null && newBeam == null) + { + // beam no longer exists at position, remove from DB + this.beaconBeamRepo.deleteWithKey(beaconPos); // TODO broken when updating adjacent chunks + this.stopRenderingBeaconAtPos(beaconPos); + } + + } + } + } + + public void loadBeaconBeamsInPos(long pos) + { + // get all beams in pos + List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); + for (int i = 0; i < existingBeamList.size(); i++) + { + BeaconBeamDTO newBeam = existingBeamList.get(i); + this.startRenderingBeacon(newBeam); + } + } + + public void unloadBeaconBeamsInPos(long pos) + { + // get all beams in pos + List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); + for (int i = 0; i < existingBeamList.size(); i++) + { + BeaconBeamDTO beam = existingBeamList.get(i); + this.stopRenderingBeaconAtPos(beam.pos); + } + } + + + + // + // + // + + private void startRenderingBeacon(BeaconBeamDTO beacon) + { + this.beaconRefCountByBlockPos.compute(beacon.pos, (beamPos, beaconRefCount) -> + { + if (beaconRefCount == null) { beaconRefCount = new AtomicInteger(); } + if (beaconRefCount.getAndIncrement() == 0) + { + DhApiRenderableBox beaconBox = new DhApiRenderableBox( + new DhApiVec3f(beacon.pos.x, beacon.pos.y+1, beacon.pos.z), + new DhApiVec3f(beacon.pos.x+1, 6_000, beacon.pos.z+1), + // TODO calculate color + beacon.color + ); + + this.beaconBoxGroup.add(beaconBox); + this.beaconBoxGroup.triggerBoxChange(); + } + return beaconRefCount; + }); + } + + private void stopRenderingBeaconAtPos(DhBlockPos beaconPos) + { + this.beaconRefCountByBlockPos.compute(beaconPos, (pos, beaconRefCount) -> + { + if (beaconRefCount != null + && beaconRefCount.decrementAndGet() <= 0) + { + this.beaconBoxGroup.removeIf((box) -> + box.minPos.x == beaconPos.x + && box.minPos.y == beaconPos.y+1 // plus 1 because the beam starts above the beacon + && box.minPos.z == beaconPos.z + ); + this.beaconBoxGroup.triggerBoxChange(); + return null; + } + else + { + return beaconRefCount; + } + }); + } + + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java index 4933e46c7..adbc8f725 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java @@ -60,8 +60,10 @@ public class CloudRenderHandler private final IDhApiRenderableBoxGroup[][] boxGroupByOffset = new IDhApiRenderableBoxGroup[3][3]; private final IDhLevel level; + private final GenericObjectRenderer renderer; private float moveSpeedInBlocksPerSecond = 3.0f; + private boolean disabledWarningLogged = false; @@ -72,12 +74,7 @@ public class CloudRenderHandler public CloudRenderHandler(IDhLevel level, GenericObjectRenderer renderer) { this.level = level; - - if (!renderer.getUseInstancedRendering()) - { - LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled."); - } - + this.renderer = renderer; @@ -174,11 +171,7 @@ public class CloudRenderHandler CloudParams params = new CloudParams(textureWidth, x, z); boxGroup.setPreRenderFunc((renderParam) -> this.preRender(params)); - // we only stop before adding to the renderer to prevent accidental issues with null pointers and such - if (renderer.getUseInstancedRendering()) - { - renderer.add(boxGroup); - } + renderer.add(boxGroup); this.boxGroupByOffset[x+1][z+1] = boxGroup; } } @@ -195,6 +188,15 @@ public class CloudRenderHandler return; } + if (!this.renderer.getUseInstancedRendering()) + { + if (!this.disabledWarningLogged) + { + this.disabledWarningLogged = true; + LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled."); + } + return; + } //================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java index 090329588..3472f9602 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java @@ -629,7 +629,16 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister // getters // //=========// - public boolean getUseInstancedRendering() { return this.useInstancedRendering; } + /** @throws IllegalStateException if {@link #init()} function hasn't been called yet */ + public boolean getUseInstancedRendering() throws IllegalStateException + { + if (!this.init) + { + throw new IllegalStateException("GL initialization hasn't been completed."); + } + + return this.useInstancedRendering; + }