Jump to content

Lyinginbedmon

Members
  • Posts

    15
  • Joined

  • Last visited

Everything posted by Lyinginbedmon

  1. Unfortunately the trouble doesn't just end there, as the Listener we have to manage also requires an Advancement object and makes active use of the value (so we can't just feed it a null). One feasible solution would be to creatively generate dummy advancements but that could cause some considerable bloat in the advancement screen. private static final Criterion testCriteria; private static final Listener testListener; /** Add listeners on login */ @SideOnly(Side.SERVER) @SubscribeEvent public void onPlayerLogin(PlayerLoggedInEvent event) { EntityPlayerMP player = (EntityPlayerMP)event.player; PlayerAdvancements advancements = player.getAdvancements(); CriteriaTriggers.PLAYER_KILLED_ENTITY.addListener(advancements, testListener); } /** Remove listeners on logout */ @SideOnly(Side.SERVER) @SubscribeEvent public void onPlayerLogout(PlayerLoggedOutEvent event) { EntityPlayerMP player = (EntityPlayerMP)event.player; PlayerAdvancements advancements = player.getAdvancements(); CriteriaTriggers.PLAYER_KILLED_ENTITY.removeListener(advancements, testListener); } static { testCriteria = PactHandler.getPact(new ResourceLocation(Reference.ModInfo.MOD_ID, "feyWitch")).getTiers()[1].getCriteria().get("vax"); testListener = new ICriterionTrigger.Listener(testCriteria.getCriterionInstance(), null, "vax"); } In this quick test example, the criteria (under the name "vax") being added is to kill 1 pig, retrieved from what I've already assembled for a configurable JSON folder.
  2. This much I've already ascertained, more or less. Criteria Triggers are held as static final values in the CriteriaTriggers class, each as their own class (ImpossibleTrigger, KilledTrigger, TickTrigger, etc.) and not as a generic. The vanilla code then directly triggers them whenever they are needed, and they update a list of attached listeners when they are so updated. Listeners are added to each trigger via the AddListener method specified in ICriterionTrigger (which only declares 3 methods: addListener, removeListener, and removeAllListeners). This method (and all methods in ICriterionTrigger) requires a PlayerAdvancements input value to register the listener in a hashmap. My problem is finding a way to add a listener to the criteria without using advancements, because no other route seems evident, but so far I've not found anything. I could theoretically extend from PlayerAdvancements, but that's about 500 lines of code that I'm not dreadfully sure of the operation.
  3. I can see a number of possible contexts it'd be useful but I ran into this issue trying to make a progression system similar to that of Witchery's vampirism and lycanthropy, but able to configured with the same triggers of advancements. Users provide a config/data pack file with a given "pact" contained within it, it gets broken down into tiers and each is accessed in turn based on given criteria. Just about every step of it I can already implement, but I'm flummoxed by how to interface it with the criteria triggers.
  4. In the vanilla code there's nearly 30 different criteria triggers that are natively updated and managed and almost none of them also connect to events. At a rough estimate, maybe 21 of them could be replicated using Forge events but this would be impractical and laggy in the extreme. However, the criteria triggers will only add listeners from the PlayerAdvancements class. Criteria seem like a really configurable way of adding special effects and such, but is there any way to use them for more than just add new advancements given this hard-coded restriction?
  5. I've managed to analyse the vanilla cave generation code and identify the significance of almost all variables involved, but I've encountered a bizarre bug that seems to occur no matter what I do (suggesting strongly that there's something outside the cave generation code itself responsible). I've added/removed qualifiers, I've imported cave generation code from other mods almost entirely wholesale, rebuilt from scratch twice, and still the same bug. Normal cave generation: My cave generation: In short, what appears to happen is that instead of generating whole snake-like caves as normal, each chunk uses a seed value so distinct from its neighbours that no generated cave successfully exits the chunk it started in. import java.util.Random; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraft.world.chunk.ChunkPrimer; import net.minecraft.world.gen.MapGenBase; public class MapGenCavesWater extends MapGenBase { protected void recursiveGenerate(World worldIn, int chunkX, int chunkZ, int originalX, int originalZ, ChunkPrimer chunkPrimerIn) { int tunnelCount = this.rand.nextInt(this.rand.nextInt(this.rand.nextInt(10) + 1) + 1); if(this.rand.nextInt(5) != 0) tunnelCount = 0; for(int j = 0; j < tunnelCount; ++j) { double xPos = (double)(chunkX * 16 + this.rand.nextInt(16)); double yPos = (double)this.rand.nextInt(255); double zPos = (double)(chunkZ * 16 + this.rand.nextInt(16)); int length = 1; // 1 in 4 chance of adding a room, increment length if(this.rand.nextInt(4) == 0) { this.addRoom(this.rand.nextLong(), originalX, originalZ, chunkPrimerIn, xPos, yPos, zPos); length += this.rand.nextInt(4); } // Generate tunnel to length for(int l = 0; l < length; ++l) { float f = this.rand.nextFloat() * ((float)Math.PI * 2F); float f1 = (this.rand.nextFloat() - 0.5F) * 2.0F / 8.0F; float f2 = this.rand.nextFloat() * 2.0F + this.rand.nextFloat(); this.addTunnel(this.rand.nextLong(), originalX, originalZ, chunkPrimerIn, xPos, yPos, zPos, f2 * 2.0F, f, f1, 0, 0, 0.5D); } } } protected void addRoom(long randSeed, int chunkX, int chunkZ, ChunkPrimer primer, double xPos, double yPos, double zPos) { this.addTunnel(randSeed, chunkX, chunkZ, primer, xPos, yPos, zPos, 1.0F + this.rand.nextFloat() * 6.0F, 0.0F, 0.0F, -1, -1, 0.5D); } protected void addTunnel(long randSeed, int chunkX, int chunkZ, ChunkPrimer primer, double xPos, double yPos, double zPos, float size, float yaw, float pitch, int par11Int, int par12Int, double heightMap) { double chunkMidX = (double)(chunkX * 16 + 8); double chunkMidZ = (double)(chunkZ * 16 + 8); float f = 0.0F; float f1 = 0.0F; Random random = new Random(randSeed); if(par12Int <= 0) { int i = this.range * 16 - 16; par12Int = i - random.nextInt(i / 4); } boolean singleGen = false; if (par11Int == -1) { par11Int = par12Int / 2; singleGen = true; } int j = random.nextInt(par12Int / 2) + par12Int / 4; for(boolean flag = random.nextInt(6) == 0; par11Int < par12Int; ++par11Int) { double d2 = 1.5D + (double)(MathHelper.sin((float)par11Int * (float)Math.PI / (float)par12Int) * size); double d3 = d2 * heightMap; float f2 = MathHelper.cos(pitch); float f3 = MathHelper.sin(pitch); xPos += (double)(MathHelper.cos(yaw) * f2); yPos += (double)f3; zPos += (double)(MathHelper.sin(yaw) * f2); if(flag) pitch = pitch * 0.92F; else pitch = pitch * 0.7F; pitch = pitch + f1 * 0.1F; yaw += f * 0.1F; f1 = f1 * 0.9F; f = f * 0.75F; f1 = f1 + (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 2.0F; f = f + (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 4.0F; if(!singleGen && par11Int == j && size > 1.0F) { this.addTunnel(random.nextLong(), chunkX, chunkZ, primer, xPos, yPos, zPos, random.nextFloat() * 0.5F + 0.5F, yaw - ((float)Math.PI / 2F), pitch / 3.0F, par11Int, par12Int, 1.0D); this.addTunnel(random.nextLong(), chunkX, chunkZ, primer, xPos, yPos, zPos, random.nextFloat() * 0.5F + 0.5F, yaw + ((float)Math.PI / 2F), pitch / 3.0F, par11Int, par12Int, 1.0D); return; } if(singleGen || random.nextInt(4) != 0) { double d4 = xPos - chunkMidX; double d5 = zPos - chunkMidZ; double d6 = (double)(par12Int - par11Int); double d7 = (double)(size + 2.0F + 16.0F); if(d4 * d4 + d5 * d5 - d6 * d6 > d7 * d7) return; if( xPos >= chunkMidX - 16.0D - d2 * 2.0D && zPos >= chunkMidZ - 16.0D - d2 * 2.0D && xPos <= chunkMidX + 16.0D + d2 * 2.0D && zPos <= chunkMidZ + 16.0D + d2 * 2.0D ) { int minX = Math.max(0, MathHelper.floor(xPos - d2) - chunkX * 16 - 1); int maxX = Math.min(16, MathHelper.floor(xPos + d2) - chunkX * 16 + 1); int minY = Math.max(0, MathHelper.floor(yPos - d3) - 1); int maxY = Math.min(255, MathHelper.floor(yPos + d3) + 1); int minZ = Math.max(0, MathHelper.floor(zPos - d2) - chunkZ * 16 - 1); int maxZ = Math.min(16, MathHelper.floor(zPos + d2) - chunkZ * 16 + 1); for(int blockX = minX; blockX < maxX; ++blockX) { double d10 = ((double)(blockX + chunkX * 16) + 0.5D - xPos) / d2; for(int blockZ = minZ; blockZ < maxZ; ++blockZ) { double d8 = ((double)(blockZ + chunkZ * 16) + 0.5D - zPos) / d2; for(int blockY = maxY; blockY > minY; --blockY) { double d9 = ((double)(blockY - 1) + 0.5D - yPos) / d3; if(d9 > -0.7D && d10 * d10 + d9 * d9 + d8 * d8 < 1.0D) { IBlockState blockAtPos = primer.getBlockState(blockX, blockY, blockZ); if(blockAtPos.getBlock() == Blocks.WATER) { primer.setBlockState(blockX, blockY, blockZ, Blocks.SANDSTONE.getDefaultState()); } } } } } if(singleGen) break; } } } } } At this point my only meaningful guess is that there's something to do with chunk generation order resulting in the seeds being so different, but I can't find anywhere to remedy that.
  6. Removing that line does allow the spell to function properly, but that doesn't quite explain why every other instantaneous spell works with it included. Is it purely to do with unloading chunks?
  7. No, and given that there's numerous other spell effects that function near-identically (such as status effect buffs) I don't think it's the setDead call. Suffice it to say that every other spell works fine without issue, every other use-case of the dimension code works fine without issue, it's specifically when the two cross.
  8. As far as I can see it's happening when Entities are being removed (at line 1959, just before tile entities, which start at line 1970). And the only thing removing an entity is the same code that works just fine in blocks and commands. The greater codebase does have 1 reference to ChunkEvent.Unload (no overrides for it in custom tiles, nor any such tiles present in that test world) but that just removes tile entities in the target chunk from a separate registry that keeps track of containers.
  9. Spells are cast through items containing an ID to a specific spell in a registry, those items create a holder entity that references the spell in the registry to perform all of its effects. Several items contain multiple spells that can be selected and cast as necessary. This particular spell (which does its thing in a single game tick and vanishes) could probably be contained solely within an item, but it's one of a large variety and assortment of different spells, many of which have more long-term effects, visuals, and post-casting interaction options. It's easier to create an inert holder entity than it is to implement an entire system of ongoing spell effects that mostly mirrors an entity anyways. As for the crash report, the error doesn't seem to happen inside the the dimension code but specifically as a result of it, and only when used via an entity. ---- Minecraft Crash Report ---- // Hi. I'm Minecraft, and I'm a crashaholic. Time: 8/27/19 9:32 PM Description: Exception ticking world entities java.lang.IndexOutOfBoundsException: Index: 25, Size: 25 at java.util.ArrayList.rangeCheck(ArrayList.java:653) at java.util.ArrayList.remove(ArrayList.java:492) at net.minecraft.world.World.updateEntities(World.java:1959) at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:642) at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:840) at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:741) at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:192) at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:590) at java.lang.Thread.run(Thread.java:745) A detailed walkthrough of the error, its code path and all known details is as follows: --------------------------------------------------------------------------------------- -- Head -- Thread: Server thread Stacktrace: at java.util.ArrayList.rangeCheck(ArrayList.java:653) at java.util.ArrayList.remove(ArrayList.java:492) at net.minecraft.world.World.updateEntities(World.java:1959) at net.minecraft.world.WorldServer.updateEntities(WorldServer.java:642) -- Affected level -- Details: Level name: Testing All players: 0 total; [] Chunk stats: ServerChunkCache: 625 Drop: 369 Level seed: -2446296594359232719 Level generator: ID 01 - flat, ver 0. Features enabled: false Level generator options: 3;minecraft:bedrock,2*minecraft:dirt,minecraft:grass;1;village Level spawn location: World: (-273,4,909), Chunk: (at 15,0,13 in -18,56; contains blocks -288,0,896 to -273,255,911), Region: (-1,1; contains chunks -32,32 to -1,63, blocks -512,0,512 to -1,255,1023) Level time: 8937689 game time, 6000 day time Level dimension: 0 Level storage version: 0x04ABD - Anvil Level weather: Rain time: 0 (now: false), thunder time: 0 (now: false) Level game mode: Game mode: creative (ID 1). Hardcore: false. Cheats: true Stacktrace: at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:840) at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:741) at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:192) at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:590) at java.lang.Thread.run(Thread.java:745) -- System Details -- Details: Minecraft Version: 1.12.2 Operating System: Windows 10 (amd64) version 10.0 Java Version: 1.8.0_101, Oracle Corporation Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation Memory: 1914570600 bytes (1825 MB) / 2392850432 bytes (2282 MB) up to 5652348928 bytes (5390 MB) JVM Flags: 0 total; IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0 FML: MCP 9.42 Powered by Forge 14.23.2.2618 6 mods loaded, 6 mods active States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored | State | ID | Version | Source | Signature | |:--------- |:--------- |:------------ |:-------------------------------- |:--------- | | UCHIJAAAA | minecraft | 1.12.2 | minecraft.jar | None | | UCHIJAAAA | mcp | 9.42 | minecraft.jar | None | | UCHIJAAAA | FML | 8.0.99.99 | forgeSrc-1.12.2-14.23.2.2618.jar | None | | UCHIJAAAA | forge | 14.23.2.2618 | forgeSrc-1.12.2-14.23.2.2618.jar | None | | UCHIJAAAA | varodd | 5.0 | bin | None | | UCHIJAAAA | baubles | 1.5.2 | Baubles-1.12-1.5.2.jar | None | Loaded coremods (and transformers): GL info: ~~ERROR~~ RuntimeException: No OpenGL context found in the current thread. Profiler Position: N/A (disabled) Player Count: 1 / 8; [EntityPlayerMP['Player314'/49, l='Testing', x=-297.95, y=9.00, z=893.80]] Type: Integrated Server (map_client.txt) Is Modded: Definitely; Client brand changed to 'fml,forge'
  10. In theory I can just have the spell place an appropriately-tuned portal block at the head/feet of the target mob, but that raises possible issues of displacing blocks (and the reverse of avoiding doing so presents the issue of unreliable transport), so I'd really prefer a block-free solution to this spell problem.
  11. I'm trying to make a magic spell that, when cast, transports the caster to the Nether. This is accomplished by creating a spell entity that then isolates its caster and changes their dimension. I've managed to create a command for debugging and a block that does the same thing on contact, but whilst all three use the exact same code (down to a single static function in the teleporter) on the exact same side with the same variables attempts to use the effect via the spell entity causes errors in the world that consequently results in a server crash. The command and block work perfectly, but the spell just crashes. Teleporter function public static void teleportToDimension(EntityPlayer player, int dimension, double x, double y, double z) { if(!net.minecraftforge.common.ForgeHooks.onTravelToDimension(player, dimension)) return; int originDim = player.getEntityWorld().provider.getDimension(); EntityPlayerMP playerMP = (EntityPlayerMP) player; MinecraftServer server = player.getEntityWorld().getMinecraftServer(); WorldServer dimWorld = server.getWorld(dimension); player.addExperienceLevel(0); if(dimWorld == null || dimWorld.getMinecraftServer() == null) throw new IllegalArgumentException("Couldn't teleport to dimension "+dimension); dimWorld.getMinecraftServer().getPlayerList().transferPlayerToDimension(playerMP, dimension, new VOTeleporter(dimWorld, x, y, z)); player.setPositionAndUpdate(x, y, z); // End teleport compensation if(originDim == 1) { player.setPositionAndUpdate(x, y, z); dimWorld.spawnEntity(player); dimWorld.updateEntityWithOptionalForce(player, false); } } Spell code public void doEffectStart(Entity sourceEntity, Side onSide) { if(onSide == Side.SERVER) { EntityLivingBase caster = getCaster(sourceEntity); EntityLivingBase target = caster; if(target != null) { int previousDimension = target.dimension; if(target != null && canAffectEntity(sourceEntity, target) && previousDimension == sourceEntity.dimension) { if(target instanceof EntityPlayer) { int transferDimension = previousDimension == -1 ? 0 : -1; VOTeleporter.teleportToDimension((EntityPlayer)target, transferDimension, target.posX, target.posY + 5D, target.posZ); } } } } sourceEntity.setDead(); } Command code public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException { if(args.length < 1) return; String s = args[0]; int dim = 0; boolean dimFound = false; for(WorldServer dimension : DimensionManager.getWorlds()) { if(s.equalsIgnoreCase(dimension.provider.getDimensionType().getName())) { dim = dimension.provider.getDimension(); dimFound = true; break; } } if(!dimFound) { try { dim = Integer.parseInt(s); dimFound = true; } catch (NumberFormatException e) { sender.sendMessage(new TextComponentString(TextFormatting.RED + "Error parsing dimension!")); return; } } if(dimFound && sender instanceof EntityPlayer) { VOTeleporter.teleportToDimension((EntityPlayer)sender, dim, sender.getPositionVector().x, sender.getPositionVector().y, sender.getPositionVector().z); sender.sendMessage(new TextComponentString("Teleporting to dimension "+DimensionManager.getWorld(dim).provider.getDimensionType().getName())); } } Block code public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, IBlockState state, Entity entityIn) { if(!worldIn.isRemote) { VOTeleporter.teleportToDimension((EntityPlayer)entityIn, -1, entityIn.posX, entityIn.posY + 5D, entityIn.posZ); } } Anyone know what's going on here? I'm at my wit's end trying to figure out what is creating the difference in operation.
  12. Failing a solution to this bizarre bug, are there any tutorials or references for multi-fluid implementations that can be recommended?
  13. I managed to successfully implement an acid fluid, but every fluid afterwards has refused to properly accept its model, despite using entirely identical code. Mod fluid registry: public class VOFluids { private static final List<Fluid> FLUIDS = new ArrayList<Fluid>(); public static Fluid ACID; public static Fluid GREASE; public static Fluid PETRIFYING; public static void init() { ACID = addFluid(new FluidAcid()); GREASE = addFluid(new FluidGrease()); PETRIFYING = addFluid(new GasPetrifying()); } private static Fluid addFluid(Fluid fluidIn) { FLUIDS.add(fluidIn); FluidRegistry.registerFluid(fluidIn); return fluidIn; } public static void renderFluids() { ((BlockFluidVO)VOBlocks.ACID).render(); ((BlockFluidVO)VOBlocks.GREASE).render(); ((BlockFluidVO)VOBlocks.PETRIFYING_GAS).render(); } } All of the fluids extend out of Forge's Fluid class, with the exception of GasPetrifying which does a little bit of defaults for gaseous fluids and still experiences the same problem. init() is called by the CommonProxy, renderFluids() is called by the ClientProxy, both during pre-initialisation. Mod fluid block class: public abstract class BlockFluidVO extends BlockFluidClassic { public BlockFluidVO(String nameIn, Fluid fluidIn) { this(nameIn, fluidIn, Material.WATER); } public BlockFluidVO(String nameIn, Fluid fluidIn, Material materialIn) { super(fluidIn, materialIn); setCreativeTab(CreativeTabVO.BLOCKS_TAB); setBlockDefaults(nameIn); } public void setBlockDefaults(String nameIn) { this.setCreativeTab(null); this.setRegistryName(Reference.ModInfo.MOD_ID, nameIn); this.setUnlocalizedName(getRegistryName().toString()); } @SideOnly(Side.CLIENT) public void render() { ModelLoader.setCustomStateMapper(this, new StateMap.Builder().ignore(LEVEL).build()); } } The only meaningful change I've managed to identify is that altering the order of model registration in renderFluids() changes which fluid works fine. All the fluids use essentially identical blockstate files, however only the first registered is accepted, the others complain about missing states for different fluid levels (despite explicitly being told to ignore them). Fluid blockstate: { "forge_marker": 1, "defaults": { "model": "forge:fluid", "transform": "forge:default-item" }, "variants": { "normal": { "model": "forge:fluid", "custom": { "fluid": "varodd:acid" } } } } I've checked for any documentation, tutorials, or other mods that incorporate multiple fluids and I haven't been able to find a reason for this error.
  14. Though certainly helpful in the theory section of things, my problem seems to lie more in the precise implementation I'm afraid.
  15. I'm currently endeavouring to add some visual reference to an already-functional mining laser tool. The player pushes a button, it triggers a boolean in extended data, which sets the mining process in motion where they're looking. What I'm currently struggling with is using the tessellator to draw the actual laser beam for everyone nearby. This occurs in RenderPlayerEvent via my client event bus and I have verified that the following code is being run and returning the correct vector positions. if(data.getIsMining()) { MovingObjectPosition rayTrace = RMHelper.getTargetPosition(thePlayer, theWorld, 16F); if(rayTrace != null && rayTrace.getBlockPos() != null) { /** Origin point of the laser beam */ Vec3 posA = Vec3Helper.getLookVecPos(theEvent.entityPlayer); /** Position being struck by the laser beam */ Vec3 posB = new Vec3(rayTrace.getBlockPos().getX(), rayTrace.getBlockPos().getY(), rayTrace.getBlockPos().getZ()); Tessellator tessellator = Tessellator.getInstance(); WorldRenderer world = tessellator.getWorldRenderer(); /** Code experimentally borrowed from RenderLightningBolt */ GlStateManager.disableTexture2D(); GlStateManager.disableLighting(); GlStateManager.enableBlend(); GlStateManager.blendFunc(770, 1); world.startDrawing(7); world.setColorRGBA_F(1F, 0F, 0F, 1F); Vec3 dirVec = posA.subtract(posB); Vec3 perpVec = (new Vec3(dirVec.zCoord, dirVec.yCoord, dirVec.xCoord)).normalize(); float width = 1F / 16F; Vec3 R1 = new Vec3(posA.xCoord + perpVec.xCoord * width, posA.yCoord - 0.01, posA.zCoord + perpVec.zCoord * width); Vec3 R2 = new Vec3(posA.xCoord - perpVec.xCoord * width, posA.yCoord - 0.01, posA.zCoord + perpVec.zCoord * width); Vec3 R3 = new Vec3(posB.xCoord + perpVec.xCoord * width, posB.yCoord + 0.75, posB.zCoord + perpVec.zCoord * width); Vec3 R4 = new Vec3(posB.xCoord - perpVec.xCoord * width, posB.yCoord + 0.75, posB.zCoord + perpVec.zCoord * width); world.addVertex(R1.xCoord + 0.5, R1.yCoord, R1.zCoord + 0.5); world.addVertex(R3.xCoord + 0.5, R3.yCoord, R3.zCoord + 0.5); world.addVertex(R4.xCoord + 0.5, R4.yCoord, R4.zCoord + 0.5); world.addVertex(R2.xCoord + 0.5, R2.yCoord, R2.zCoord + 0.5); tessellator.draw(); GlStateManager.disableBlend(); GlStateManager.enableLighting(); GlStateManager.enableTexture2D(); } } Currently, nothing appears on screen at all. I've tried switching the direction (b -> a instead of a -> b) and changing the origin point of the vectors, but to no avail. Graphics programming is by far my least level of competence, can anyone help me work this out?
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.