Skip to content

Commit

Permalink
Add AtlasSourceTypeRegistry (#3504)
Browse files Browse the repository at this point in the history
(cherry picked from commit 0d29db7)
  • Loading branch information
PepperCode1 authored and modmuss50 committed Jan 19, 2024
1 parent 9307220 commit e89ad72
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.api.client.rendering.v1;

import net.minecraft.client.texture.atlas.AtlasSourceType;
import net.minecraft.util.Identifier;

import net.fabricmc.fabric.impl.client.rendering.AtlasSourceTypeRegistryImpl;

/**
* A registry for custom {@link AtlasSourceType}s. Registered types will be automatically available for use in atlas definition JSON files.
*/
public final class AtlasSourceTypeRegistry {
private AtlasSourceTypeRegistry() {
}

/**
* Registers a new {@link AtlasSourceType}.
*
* @param id the identifier of the atlas source type
* @param type the atlas source type to register
*/
public static void register(Identifier id, AtlasSourceType type) {
AtlasSourceTypeRegistryImpl.register(id, type);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.impl.client.rendering;

import java.util.Objects;

import net.minecraft.client.texture.atlas.AtlasSourceType;
import net.minecraft.util.Identifier;

import net.fabricmc.fabric.mixin.client.rendering.AtlasSourceManagerAccessor;

public final class AtlasSourceTypeRegistryImpl {
private AtlasSourceTypeRegistryImpl() {
}

public static void register(Identifier id, AtlasSourceType type) {
Objects.requireNonNull(id, "id must not be null!");
Objects.requireNonNull(type, "type must not be null!");

AtlasSourceType oldType = AtlasSourceManagerAccessor.getSourceTypeById().putIfAbsent(id, type);

if (oldType != null) {
throw new IllegalStateException("Duplicate registration " + id);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.mixin.client.rendering;

import com.google.common.collect.BiMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

import net.minecraft.client.texture.atlas.AtlasSourceManager;
import net.minecraft.client.texture.atlas.AtlasSourceType;
import net.minecraft.util.Identifier;

@Mixin(AtlasSourceManager.class)
public interface AtlasSourceManagerAccessor {
@Accessor("SOURCE_TYPE_BY_ID")
static BiMap<Identifier, AtlasSourceType> getSourceTypeById() {
throw new AssertionError();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"compatibilityLevel": "JAVA_17",
"client": [
"ArmorFeatureRendererMixin",
"AtlasSourceManagerAccessor",
"BlockColorsMixin",
"BlockEntityRendererFactoriesMixin",
"BuiltinModelItemRendererMixin",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.test.rendering;

import net.minecraft.item.Item;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;

import net.fabricmc.api.ModInitializer;

public class CustomAtlasSourcesTestInit implements ModInitializer {
public static final Item DOUBLE_IRON_INGOT = new Item(new Item.Settings());

@Override
public void onInitialize() {
Registry.register(Registries.ITEM, new Identifier("fabric-rendering-v1-testmod", "double_iron_ingot"), DOUBLE_IRON_INGOT);
}
}
8 changes: 5 additions & 3 deletions fabric-rendering-v1/src/testmod/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
"license": "Apache-2.0",
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.rendering.CustomAtlasSourcesTestInit",
"net.fabricmc.fabric.test.rendering.TooltipComponentTestInit"
],
"client": [
"net.fabricmc.fabric.test.rendering.client.WorldRenderEventsTests",
"net.fabricmc.fabric.test.rendering.client.ArmorRenderingTests",
"net.fabricmc.fabric.test.rendering.client.CustomAtlasSourcesTest",
"net.fabricmc.fabric.test.rendering.client.DimensionalRenderingTest",
"net.fabricmc.fabric.test.rendering.client.FeatureRendererTest",
"net.fabricmc.fabric.test.rendering.client.HudAndShaderTest",
"net.fabricmc.fabric.test.rendering.client.TooltipComponentTests",
"net.fabricmc.fabric.test.rendering.client.DimensionalRenderingTest",
"net.fabricmc.fabric.test.rendering.client.HudAndShaderTest"
"net.fabricmc.fabric.test.rendering.client.WorldRenderEventsTests"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.test.rendering.client;

import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;

import com.mojang.logging.LogUtils;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import org.slf4j.Logger;

import net.minecraft.client.resource.metadata.AnimationResourceMetadata;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.SpriteContents;
import net.minecraft.client.texture.SpriteDimensions;
import net.minecraft.client.texture.SpriteOpener;
import net.minecraft.client.texture.atlas.AtlasSource;
import net.minecraft.client.texture.atlas.AtlasSourceType;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.metadata.ResourceMetadata;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;

import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.rendering.v1.AtlasSourceTypeRegistry;

public class CustomAtlasSourcesTest implements ClientModInitializer {
@Override
public void onInitializeClient() {
AtlasSourceTypeRegistry.register(new Identifier("fabric-rendering-v1-testmod", "double"), DoubleAtlasSource.TYPE);
}

private static class DoubleAtlasSource implements AtlasSource {
private static final Logger LOGGER = LogUtils.getLogger();
public static final Codec<DoubleAtlasSource> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Identifier.CODEC.fieldOf("resource").forGetter(source -> source.resource),
Identifier.CODEC.fieldOf("sprite").forGetter(source -> source.sprite)
).apply(instance, DoubleAtlasSource::new));
public static final AtlasSourceType TYPE = new AtlasSourceType(CODEC);

private final Identifier resource;
private final Identifier sprite;

DoubleAtlasSource(Identifier resource, Identifier sprite) {
this.resource = resource;
this.sprite = sprite;
}

@Override
public void load(ResourceManager resourceManager, SpriteRegions regions) {
Identifier resourceId = RESOURCE_FINDER.toResourcePath(resource);
Optional<Resource> optionalResource = resourceManager.getResource(resourceId);

if (optionalResource.isPresent()) {
regions.add(sprite, new DoubleSpriteRegion(resourceId, optionalResource.get(), sprite));
} else {
LOGGER.warn("Missing sprite: {}", resourceId);
}
}

@Override
public AtlasSourceType getType() {
return TYPE;
}

private static class DoubleSpriteRegion implements AtlasSource.SpriteRegion {
private final Identifier resourceId;
private final Resource resource;
private final Identifier spriteId;

DoubleSpriteRegion(Identifier resourceId, Resource resource, Identifier spriteId) {
this.resourceId = resourceId;
this.resource = resource;
this.spriteId = spriteId;
}

@Override
public SpriteContents apply(SpriteOpener spriteOpener) {
ResourceMetadata metadata;

try {
metadata = resource.getMetadata();
} catch (Exception e) {
LOGGER.error("Unable to parse metadata from {}", resourceId, e);
return null;
}

NativeImage image;

try (InputStream inputStream = resource.getInputStream()) {
image = NativeImage.read(inputStream);
} catch (IOException e) {
LOGGER.error("Using missing texture, unable to load {}", resourceId, e);
return null;
}

int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
AnimationResourceMetadata animationMetadata = metadata.decode(AnimationResourceMetadata.READER).orElse(AnimationResourceMetadata.EMPTY);
SpriteDimensions dimensions = animationMetadata.getSize(imageWidth, imageHeight);
int frameWidth = dimensions.width();
int frameHeight = dimensions.height();

if (!MathHelper.isMultipleOf(imageWidth, frameWidth) || !MathHelper.isMultipleOf(imageHeight, dimensions.height())) {
LOGGER.error("Image {} size {},{} is not multiple of frame size {},{}", resourceId, imageWidth, imageHeight, frameWidth, frameHeight);
image.close();
return null;
}

int frameCountX = imageWidth / frameWidth;
int frameCountY = imageHeight / frameHeight;
int offsetX = frameWidth / 16;
int offsetY = frameHeight / 16;

NativeImage doubleImage = new NativeImage(image.getFormat(), image.getWidth(), image.getHeight(), false);

for (int frameY = 0; frameY < frameCountY; frameY++) {
for (int frameX = 0; frameX < frameCountX; frameX++) {
blendRect(image, doubleImage, frameX * frameWidth + offsetX, frameY * frameHeight + offsetY, frameX * frameWidth, frameY * frameHeight, frameWidth - offsetX, frameHeight - offsetY);
blendRect(image, doubleImage, frameX * frameWidth, frameY * frameHeight, frameX * frameWidth + offsetX, frameY * frameHeight + offsetY, frameWidth - offsetX, frameHeight - offsetY);
}
}

return new SpriteContents(spriteId, dimensions, doubleImage, metadata);
}

private static void blendRect(NativeImage src, NativeImage dst, int srcX, int srcY, int destX, int destY, int width, int height) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
int c = src.getColor(srcX + x, srcY + y);
dst.blend(destX + x, destY + y, c);
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "fabric-rendering-v1-testmod:item/double_iron_ingot"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"sources": [
{
"type": "fabric-rendering-v1-testmod:double",
"resource": "minecraft:item/iron_ingot",
"sprite": "fabric-rendering-v1-testmod:item/double_iron_ingot"
}
]
}

0 comments on commit e89ad72

Please sign in to comment.