Skip to content

Commit

Permalink
Added getPrimary/SecondaryTexture to the ModelPart api
Browse files Browse the repository at this point in the history
  • Loading branch information
omoflop authored and UnlikePaladin committed Sep 17, 2023
1 parent b9ddd1e commit 090054f
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 33 deletions.
23 changes: 23 additions & 0 deletions common/src/main/java/org/figuramc/figura/avatar/Avatar.java
Expand Up @@ -3,6 +3,7 @@
import com.mojang.blaze3d.audio.OggAudioStream;
import com.mojang.blaze3d.audio.SoundBuffer;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.datafixers.util.Pair;
Expand Down Expand Up @@ -34,6 +35,7 @@
import org.figuramc.figura.config.Configs;
import org.figuramc.figura.lua.FiguraLuaPrinter;
import org.figuramc.figura.lua.FiguraLuaRuntime;
import org.figuramc.figura.lua.api.TextureAPI;
import org.figuramc.figura.lua.api.entity.EntityAPI;
import org.figuramc.figura.lua.api.particle.ParticleAPI;
import org.figuramc.figura.lua.api.ping.PingArg;
Expand All @@ -51,6 +53,7 @@
import org.figuramc.figura.model.rendering.EntityRenderMode;
import org.figuramc.figura.model.rendering.ImmediateAvatarRenderer;
import org.figuramc.figura.model.rendering.PartFilterScheme;
import org.figuramc.figura.model.rendering.texture.FiguraTexture;
import org.figuramc.figura.permissions.PermissionManager;
import org.figuramc.figura.permissions.PermissionPack;
import org.figuramc.figura.permissions.Permissions;
Expand All @@ -61,6 +64,7 @@
import org.figuramc.figura.utils.ui.UIHelper;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;

Expand Down Expand Up @@ -1016,6 +1020,25 @@ public void loadSound(String name, byte[] data) throws Exception {
}
}

public FiguraTexture registerTexture(String name, NativeImage image, boolean ignoreSize) {
int max = permissions.get(Permissions.TEXTURE_SIZE);
if (!ignoreSize && (image.getWidth() > max || image.getHeight() > max)) {
noPermissions.add(Permissions.TEXTURE_SIZE);
throw new LuaError("Texture exceeded max size of " + max + " x " + max + " resolution, got " + image.getWidth() + " x " + image.getHeight());
}

FiguraTexture oldText = renderer.customTextures.get(name);
if (oldText != null)
oldText.close();

if (renderer.customTextures.size() > TextureAPI.TEXTURE_LIMIT)
throw new LuaError("Maximum amount of textures reached!");

FiguraTexture texture = new FiguraTexture(this, name, image);
renderer.customTextures.put(name, texture);
return texture;
}

public static class Instructions {

public int max, remaining;
Expand Down
20 changes: 2 additions & 18 deletions common/src/main/java/org/figuramc/figura/lua/api/TextureAPI.java
Expand Up @@ -32,8 +32,7 @@
value = "textures"
)
public class TextureAPI {

private static final int TEXTURE_LIMIT = 128;
public static final int TEXTURE_LIMIT = 128;

private final Avatar owner;

Expand All @@ -47,22 +46,7 @@ private void check() {
}

public FiguraTexture register(String name, NativeImage image, boolean ignoreSize) {
int max = owner.permissions.get(Permissions.TEXTURE_SIZE);
if (!ignoreSize && (image.getWidth() > max || image.getHeight() > max)) {
owner.noPermissions.add(Permissions.TEXTURE_SIZE);
throw new LuaError("Texture exceeded max size of " + max + " x " + max + " resolution, got " + image.getWidth() + " x " + image.getHeight());
}

FiguraTexture oldText = get(name);
if (oldText != null)
oldText.close();

if (owner.renderer.customTextures.size() > TEXTURE_LIMIT)
throw new LuaError("Maximum amount of textures reached!");

FiguraTexture texture = new FiguraTexture(owner, name, image);
owner.renderer.customTextures.put(name, texture);
return texture;
return owner.registerTexture(name, image, ignoreSize);
}

@LuaWhitelist
Expand Down
Expand Up @@ -24,6 +24,8 @@
import org.figuramc.figura.utils.ui.UIHelper;
import org.luaj.vm2.LuaError;
import org.luaj.vm2.LuaFunction;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;

import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -691,6 +693,40 @@ public FiguraModelPart secondaryRenderType(String type) {
return setSecondaryRenderType(type);
}

@LuaWhitelist
@LuaMethodDoc("model_part.get_primary_texture")
public Object getPrimaryTexture(Integer value) {
if (customization.primaryTexture == null) {
LuaTable tbl = new LuaTable();
FiguraTexture[] arr = this.textures.get(value).textures;
tbl.set("main", arr[0] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[0]));
tbl.set("emissive", arr[1] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[1]));
tbl.set("specular", arr[2] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[2]));
tbl.set("normal", arr[3] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[3]));
return tbl;
}
return new LuaTable() {{
set("main", LuaValue.userdataOf(customization.primaryTexture.getTexture(owner, textures.get(value == null ? 0 : value))));
}};
}

@LuaWhitelist
@LuaMethodDoc("model_part.get_secondary_texture")
public Object getSecondaryTexture(Integer value) {
if (customization.secondaryTexture == null) {
LuaTable tbl = new LuaTable();
FiguraTexture[] arr = this.textures.get(value).textures;
tbl.set("main", arr[0] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[0]));
tbl.set("emissive", arr[1] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[1]));
tbl.set("specular", arr[2] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[2]));
tbl.set("normal", arr[3] == null ? LuaValue.NIL : LuaValue.userdataOf(arr[3]));
return tbl;
}
return new LuaTable() {{
set("main", LuaValue.userdataOf(customization.secondaryTexture.getTexture(owner, textures.get(value == null ? 0 : value))));
}};
}

@LuaWhitelist
@LuaMethodDoc(
overloads = {
Expand All @@ -712,7 +748,7 @@ public FiguraModelPart secondaryRenderType(String type) {
)
public FiguraModelPart setPrimaryTexture(String type, Object x) {
try {
this.customization.primaryTexture = type == null ? null : Pair.of(FiguraTextureSet.OverrideType.valueOf(type.toUpperCase()), x);
this.customization.primaryTexture = type == null ? null : new TextureCustomization(FiguraTextureSet.OverrideType.valueOf(type.toUpperCase()), x);
return this;
} catch (Exception ignored) {
throw new LuaError("Invalid texture override type: " + type);
Expand Down Expand Up @@ -740,7 +776,7 @@ public FiguraModelPart setPrimaryTexture(String type, Object x) {
)
public FiguraModelPart setSecondaryTexture(String type, Object x) {
try {
this.customization.secondaryTexture = type == null ? null : Pair.of(FiguraTextureSet.OverrideType.valueOf(type.toUpperCase()), x);
this.customization.secondaryTexture = type == null ? null : new TextureCustomization(FiguraTextureSet.OverrideType.valueOf(type.toUpperCase()), x);
return this;
} catch (Exception ignored) {
throw new LuaError("Invalid texture override type: " + type);
Expand Down
Expand Up @@ -52,7 +52,7 @@ public class PartCustomization {
public Integer overlay = null;

private RenderTypes primaryRenderType, secondaryRenderType;
public Pair<FiguraTextureSet.OverrideType, Object> primaryTexture, secondaryTexture;
public TextureCustomization primaryTexture, secondaryTexture;

public void applyToStack(PoseStack stack) {
stack.mulPoseMatrix(positionMatrix.toMatrix4f());
Expand Down
@@ -0,0 +1,69 @@
package org.figuramc.figura.model;

import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import org.figuramc.figura.FiguraMod;
import org.figuramc.figura.avatar.Avatar;
import org.figuramc.figura.lua.api.TextureAPI;
import org.figuramc.figura.mixin.render.MissingTextureAtlasSpriteAccessor;
import org.figuramc.figura.mixin.render.TextureAtlasAccessor;
import org.figuramc.figura.model.rendering.ImmediateAvatarRenderer;
import org.figuramc.figura.model.rendering.texture.FiguraTexture;
import org.figuramc.figura.model.rendering.texture.FiguraTextureSet;
import org.figuramc.figura.model.rendering.texture.RenderTypes;
import org.figuramc.figura.utils.LuaUtils;
import org.luaj.vm2.LuaError;

import java.util.Optional;

public class TextureCustomization {

private final FiguraTextureSet.OverrideType first;
private final Object second;

public TextureCustomization(FiguraTextureSet.OverrideType first, Object second) {
this.first = first;
this.second = second;
}

public FiguraTextureSet.OverrideType getOverrideType() {
return first;
}

public Object getValue() {
return second;
}

public FiguraTexture getTexture(Avatar avatar, FiguraTextureSet textureSet) {
if (avatar.render == null) return null;

ResourceLocation resourceLocation = textureSet.getOverrideTexture(avatar.owner, this);
String name = resourceLocation.toString();
if (avatar.renderer.customTextures.containsKey(name)) {
return avatar.renderer.customTextures.get(name);
}

// is there a way to check if an atlas exists without getAtlas? cause that is the only thing that will cause an error, and try catch blocks can be pricy
try {
TextureAtlas atlas = Minecraft.getInstance().getModelManager().getAtlas(resourceLocation);
atlas.bind();
TextureAtlasAccessor atlasAccessor = (TextureAtlasAccessor) atlas;
NativeImage nativeImage = new NativeImage(atlasAccessor.getWidth(), atlasAccessor.getHeight(), false);
nativeImage.downloadTexture(0, false);
return avatar.registerTexture(name, nativeImage, false);
} catch (Exception ignored) {}
try {
Optional<Resource> resource = Minecraft.getInstance().getResourceManager().getResource(resourceLocation);
// if the string is a valid resourceLocation but does not point to a valid resource, missingno
NativeImage image = resource.isPresent() ? NativeImage.read(resource.get().open()) : MissingTextureAtlasSpriteAccessor.generateImage(16, 16);
return avatar.registerTexture(name, image, false);
} catch (Exception e) {
// spit an error if the player inputs a resource location that does point to a thing, but not to an image
throw new LuaError(e.getMessage());
}
}
}
Expand Up @@ -19,10 +19,7 @@
import org.figuramc.figura.math.matrix.FiguraMat4;
import org.figuramc.figura.math.vector.FiguraVec3;
import org.figuramc.figura.math.vector.FiguraVec4;
import org.figuramc.figura.model.FiguraModelPart;
import org.figuramc.figura.model.FiguraModelPartReader;
import org.figuramc.figura.model.ParentType;
import org.figuramc.figura.model.PartCustomization;
import org.figuramc.figura.model.*;
import org.figuramc.figura.model.rendering.texture.FiguraTexture;
import org.figuramc.figura.model.rendering.texture.FiguraTextureSet;
import org.figuramc.figura.model.rendering.texture.RenderTypes;
Expand Down Expand Up @@ -197,8 +194,8 @@ protected PartCustomization setupRootCustomization(double vertOffset) {
customization.alpha = alpha;
customization.overlay = overlay;

customization.primaryTexture = Pair.of(FiguraTextureSet.OverrideType.PRIMARY, null);
customization.secondaryTexture = Pair.of(FiguraTextureSet.OverrideType.SECONDARY, null);
customization.primaryTexture = new TextureCustomization(FiguraTextureSet.OverrideType.PRIMARY, null);
customization.secondaryTexture = new TextureCustomization(FiguraTextureSet.OverrideType.SECONDARY, null);

return customization;
}
Expand Down Expand Up @@ -483,7 +480,7 @@ public void pushFaces(int faceCount, int[] remainingComplexity, FiguraTextureSet

private VertexData getTexture(PartCustomization customization, FiguraTextureSet textureSet, boolean primary) {
RenderTypes types = primary ? customization.getPrimaryRenderType() : customization.getSecondaryRenderType();
Pair<FiguraTextureSet.OverrideType, Object> texture = primary ? customization.primaryTexture : customization.secondaryTexture;
TextureCustomization texture = primary ? customization.primaryTexture : customization.secondaryTexture;
VertexData ret = new VertexData();

if (types == RenderTypes.NONE)
Expand Down
@@ -1,12 +1,12 @@
package org.figuramc.figura.model.rendering.texture;

import com.mojang.datafixers.util.Pair;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.multiplayer.PlayerInfo;
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
import net.minecraft.resources.ResourceLocation;
import org.figuramc.figura.mixin.render.layers.elytra.ElytraLayerAccessor;
import org.figuramc.figura.model.TextureCustomization;

import java.util.UUID;

Expand Down Expand Up @@ -53,10 +53,10 @@ public int getHeight() {
return -1;
}

public ResourceLocation getOverrideTexture(UUID owner, Pair<OverrideType, Object> pair) {
public ResourceLocation getOverrideTexture(UUID owner, TextureCustomization pair) {
OverrideType type;

if (pair == null || (type = pair.getFirst()) == null)
if (pair == null || (type = pair.getOverrideType()) == null)
return null;

return switch (type) {
Expand All @@ -77,7 +77,7 @@ yield switch (type) {
}
case RESOURCE -> {
try {
yield new ResourceLocation(String.valueOf(pair.getSecond()));
yield new ResourceLocation(String.valueOf(pair.getValue()));
} catch (Exception ignored) {
yield MissingTextureAtlasSprite.getLocation();
}
Expand All @@ -88,7 +88,7 @@ yield switch (type) {
case NORMAL -> textures[3] == null ? null : textures[3].getLocation();
case CUSTOM -> {
try {
yield ((FiguraTexture) pair.getSecond()).getLocation();
yield ((FiguraTexture) pair.getValue()).getLocation();
} catch (Exception ignored) {
yield MissingTextureAtlasSprite.getLocation();
}
Expand Down
2 changes: 2 additions & 0 deletions common/src/main/resources/assets/figura/lang/en_us.json
Expand Up @@ -1342,7 +1342,9 @@
"figura.docs.model_part.get_secondary_render_type": "Gets the current secondary render type of this model part\nNil by default, meaning the part copies the secondary render type of its parent",
"figura.docs.model_part.set_primary_render_type": "Sets the current primary render type of this model part\nNil by default, meaning the part copies the primary render type of its parent during rendering\nCheck the docs enum command for all render types",
"figura.docs.model_part.set_secondary_render_type": "Sets the current secondary render type of this model part\nNil by default, meaning the part copies the secondary render type of its parent during rendering\nCheck the docs enum command for all render types",
"figura.docs.model_part.get_primary_texture": "Gets the primary texture of this part\nReturns a table containing each texture",
"figura.docs.model_part.set_primary_texture": "Sets the primary texture override of this part\nCheck the TextureType types in the list docs\nIf using \"resource\", the second parameter should indicate the path to the Minecraft texture you want to use\nIf using \"custom\", the second parameter should indicate a texture object",
"figura.docs.model_part.get_secondary_texture": "Gets the secondary texture of this part\nReturns a table containing each texture",
"figura.docs.model_part.set_secondary_texture": "Sets the secondary texture override of this part\nCheck the TextureType types in the list docs\nIf using \"resource\", the second parameter should indicate the path to the Minecraft texture you want to use\nIf using \"custom\", the second parameter should indicate a texture object",
"figura.docs.model_part.get_textures": "Returns a table with all textures used by this part\nDo not include children textures, so groups usually will return an empty table",
"figura.docs.model_part.part_to_world_matrix": "Gets a matrix which transforms a point from this part's position to a world location\nRecommended to use this in POST_RENDER, as by then the matrix is updated\nIn RENDER it will be 1 frame behind the part's visual position for that frame\nAlso, if the model is not rendered in-world, the part's matrix will not be updated\nPaperdoll rendering and other UI rendering will not affect this matrix",
Expand Down

0 comments on commit 090054f

Please sign in to comment.