diff --git a/examplemods/lib.lcrender.js b/examplemods/lib.lcrender.js index 7d371f2..eb92027 100644 --- a/examplemods/lib.lcrender.js +++ b/examplemods/lib.lcrender.js @@ -2,7 +2,20 @@ ModAPI.meta.title("LibCustomRender"); ModAPI.meta.credits("By ZXMushroom63"); ModAPI.meta.icon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAQdJREFUOE9jZGBg+M9AAWAEGbBl2QmyjPCJsmAgaABbdybc8F+l01EswmsATONXLi4GYSkpBgZ+foY1O3cyuHWuhhuC1QBkjf///QMrFtHWZmD4+BHDEBQDUGzU1ITb8ubqVZyGoBjwsCONQYqXl0FYU5MBpAlsKxRgM+STUwoDhgG66upgZ4IAuiEooRcXx/DpCRuqAU97shg0jYzgfsVpSFwcg5mZGcOedRewGDBhAgPDokUohsBthmoE8U+dOoXdBfHHjoElUQxB03i9oABspnTJNFQXgARB3oAbwsAAdirMRmSNMFdhTQcwQ/BpxGsAzCUwRSCn4gJE5QV8uQxuAFlZEaoJAKrYrAHl38o6AAAAAElFTkSuQmCC"); ModAPI.meta.description("Library to make retexturing LCI items easier. Requires AsyncSink."); -(function LibRender() { +(async function LibRender() { + function waitUntilPropertyExists(obj, prop) { + return new Promise((res, rej)=>{ + var timer = setInterval(()=>{ + if (obj[prop]) { + clearInterval(timer); + res(); + } + }, 50); + }); + } + function rgbaToInt(red, green, blue, alpha) { + return (alpha << 24) | (red << 16) | (green << 8) | blue; + } function getLore(item) { if (item.$stackTagCompound && item.$stackTagCompound.$hasKey(ModAPI.util.str("display"), 10)) { var displayTag = item.$stackTagCompound.$getCompoundTag(ModAPI.util.str("display")); @@ -14,21 +27,57 @@ ModAPI.meta.description("Library to make retexturing LCI items easier. Requires } } } - function cloneBaseModel(baseModel, newTexture) { + function recursiveAssign(target, patch) { + var keys = Object.keys(patch); + keys.forEach(k => { + if (typeof patch[k] === "object" && patch[k]) { + recursiveAssign(target[k], patch[k]); + } else if (typeof patch[k] === "number") { + target[k] = patch[k]; + } + }); + } + function cloneBaseModel(baseModel, newTexture, texName) { var newBaseModelBuilder = ModAPI.reflect.getClassByName("SimpleBakedModel$Builder").constructors[0](0, 0, ModAPI.reflect.getClassByName("ItemCameraTransforms").constructors.find(x => x.length === 0)()); - var textureProp = ModAPI.util.getNearestProperty("$texture", newBaseModel); + newBaseModelBuilder.$builderTexture = eaglerTextureAtlasSprite(imageDataToLaxImgData(newTexture), ModAPI.util.str(texName)); + var newBaseModel = ModAPI.hooks.methods.nmcrm_SimpleBakedModel$Builder_makeBakedModel(newBaseModelBuilder); + newBaseModel.$generalQuads = baseModel.$generalQuads.$clone(); + newBaseModel.$faceQuads = baseModel.$faceQuads.$clone(); + var cameraTransformsId = ModAPI.util.getNearestProperty(newBaseModel, "$cameraTransforms"); + recursiveAssign(newBaseModel[cameraTransformsId], baseModel[cameraTransformsId]); + return newBaseModel; } ModAPI.events.newEvent("lib:libcustomrender:loaded"); + await waitUntilPropertyExists(ModAPI.minecraft, "renderItem"); var ItemRenderer = ModAPI.minecraft.renderItem; var ItemModelMesher = ItemRenderer.itemModelMesher; var laxImgDataClass = ModAPI.reflect.getClassByName("ImageData").class; - var makeLax1dudeImageData = ModAPI.reflect.getClassByName("ImageData").constructors.find(x => x.length === 3); + var makeLax1dudeImageData = ModAPI.reflect.getClassByName("ImageData").constructors.find(x => x.length === 4); var eaglerTextureAtlasSprite = (imageData, name) => { var atlas = ModAPI.reflect.getClassByName("EaglerTextureAtlasSprite").constructors[0](ModAPI.util.str(name)); var alias = ModAPI.util.wrap(atlas); - alias.loadSprite(ModAPI.util.makeArray(laxImgDataClass, [imageData])); + alias.loadSprite(ModAPI.util.makeArray(laxImgDataClass, [imageData]), null); + return atlas; }; + + /** + * @type {ImageData} + */ + function imageDataToLaxImgData(imageData) { + const { data, width, height } = imageData; + const intArray = []; + + for (let i = 0; i < data.length; i += 4) { + const red = data[i]; + const green = data[i + 1]; + const blue = data[i + 2]; + const alpha = data[i + 3]; + intArray.push(rgbaToInt(red, green, blue, alpha)); + } + + return makeLax1dudeImageData(width, height, ModAPI.array.int(intArray), 1); + } const LibCustomRender = {}; LibCustomRender.map = {}; LibCustomRender.addRetextureRule = (loreString, textureBuffer, baseItem) => { @@ -36,21 +85,18 @@ ModAPI.meta.description("Library to make retexturing LCI items easier. Requires var actualLoreStr = loreString; loreString = loreString.replaceAll(":", "_").toLowerCase().replace(/[^a-z_]/g, ''); if (!(textureBuffer instanceof ImageData)) { - return console.error("Texture for retexture rule is not an ArrayBuffer."); + return console.error("Texture for retexture rule is not an ImageData."); } if (!(typeof loreString === "string")) { return console.error("loreString for retexture rule is not a string."); } var baseModel = ItemModelMesher.simpleShapesCache.get(ModAPI.hooks.methods.jl_Integer_valueOf(ItemModelMesher.getIndex(ModAPI.items[baseItem].getRef(), 0))); LibCustomRender.map[loreString] = { - meta_int: ModAPI.util.hashCode(loreString) % 256, lore: actualLoreStr, identifier: loreString, - model: makeModelJSON(loreString) + model: cloneBaseModel(baseModel.getRef(), textureBuffer, loreString) } - var itemstack = ModAPI.reflect.getClassById("net.minecraft.item.ItemStack").constructors[5](ModAPI.items[baseItem].getRef(), 1, LibCustomRender.map[loreString].meta_int); - LibCustomRender.map[loreString].stack = itemstack; - ItemRenderer.registerItem0(ModAPI.items[baseItem].getRef(), LibCustomRender.map[loreString].meta_int, ModAPI.util.str(loreString)); + return LibCustomRender.map[loreString].model; } // override @@ -62,33 +108,23 @@ ModAPI.meta.description("Library to make retexturing LCI items easier. Requires var prefix = ModAPI.util.getMethodFromPackage("net.minecraft.client.renderer.ItemModelMesher", "getItemModel"); var methodName = methods.find(x => x.startsWith(prefix) && ModAPI.hooks.methods[x].length === 2); var original = ModAPI.hooks.methods[methodName]; - var testStack = ModAPI.reflect.getClassById("net.minecraft.item.ItemStack").constructors[5](ModAPI.items.paper.getRef(), 1, 0); ModAPI.hooks.methods[methodName] = function (...args) { var item = args[1]; var lore = item ? getLore(item) : ""; if (!item) { return original.apply(this, args); } - var shouldOverride = false; - var overrideItem = null; - var overrideModelT = null; var overrides = Object.values(LibCustomRender.map); for (let i = 0; i < overrides.length; i++) { const override = overrides[i]; if (lore === override.lore) { - shouldOverride = true; - overrideModelT = override.model; - overrideItem = override.stack; - break; + return override.model; } } - if (shouldOverride) { - return overrideModelT; - return original.apply(this, [args[0], overrideItem]); - } return original.apply(this, args); } ModAPI.events.callEvent("lib:libcustomrender:loaded", {}); globalThis.LibCustomRender = LibCustomRender; -})(); \ No newline at end of file +})(); +//LibCustomRender.addRetextureRule("mymod:test_item_1", new ImageData(1, 1)); \ No newline at end of file diff --git a/postinit.js b/postinit.js index 5801eb0..64bacaa 100644 --- a/postinit.js +++ b/postinit.js @@ -680,25 +680,25 @@ globalThis.modapi_postinit = "(" + (() => { return sendChatMessage.apply(this, [$this, $message]); } - ModAPI.events.newEvent("render", "client"); - const renderMethodName = ModAPI.util.getMethodFromPackage("net.minecraft.client.renderer.EntityRenderer", "renderWorldPass"); - const renderMethod = ModAPI.hooks.methods[renderMethodName]; - ModAPI.hooks.methods[renderMethodName] = function ($this, $int_pass, $float_partialTicks, $long_finishTimeNano) { - var shouldRenderHand = $this.$renderHand; - $this.$renderHand = 0; //Rendering the hand clears the depth bit, which we don't want to do. - var out = renderMethod.apply(this, [$this, $int_pass, $float_partialTicks, $long_finishTimeNano]); - var data = { - partialTicks: $float_partialTicks - } - ModAPI.events.callEvent("render", data); - if (shouldRenderHand) { - ModAPI.hooks.methods.nlevo_GlStateManager_clear(256); //GL_DEPTH_BUFFER_BIT, found in class RealOpenGLEnums - ModAPI.hooks.methods.nmcr_EntityRenderer_renderHand($this, $float_partialTicks, $int_pass); - ModAPI.hooks.methods.nmcr_EntityRenderer_renderWorldDirections($this, $float_partialTicks); - } - $this.$renderHand = shouldRenderHand; - return out; - } + // ModAPI.events.newEvent("render", "client"); + // const renderMethodName = ModAPI.util.getMethodFromPackage("net.minecraft.client.renderer.EntityRenderer", "renderWorldPass"); + // const renderMethod = ModAPI.hooks.methods[renderMethodName]; + // ModAPI.hooks.methods[renderMethodName] = function ($this, $int_pass, $float_partialTicks, $long_finishTimeNano) { + // var shouldRenderHand = $this.$renderHand; + // $this.$renderHand = 0; //Rendering the hand clears the depth bit, which we don't want to do. + // var out = renderMethod.apply(this, [$this, $int_pass, $float_partialTicks, $long_finishTimeNano]); + // var data = { + // partialTicks: $float_partialTicks + // } + // ModAPI.events.callEvent("render", data); + // if (shouldRenderHand) { + // ModAPI.hooks.methods.nlevo_GlStateManager_clear(256); //GL_DEPTH_BUFFER_BIT, found in class RealOpenGLEnums + // ModAPI.hooks.methods.nmcr_EntityRenderer_renderHand($this, $float_partialTicks, $int_pass); + // ModAPI.hooks.methods.nmcr_EntityRenderer_renderWorldDirections($this, $float_partialTicks); + // } + // $this.$renderHand = shouldRenderHand; + // return out; + // } const ScaledResolutionConstructor = ModAPI.reflect.getClassByName("ScaledResolution").constructors[0]; ModAPI.events.newEvent("frame", "client");