Skip to content

Commit

Permalink
Add model data module
Browse files Browse the repository at this point in the history
  • Loading branch information
AlphaMode committed Nov 17, 2024
1 parent aa8dec5 commit 56347bb
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Just choose a version and use its version number.
| `level_events` | Provides common level events for mods. |
| `loot` | A small library to modify mob loot |
| `mixin_extensions` | More features for Mixins |
| `model_data` | Addon to model api to make building model data easier. |
| `model_loader` | Base loader for custom model types |
| `models` | Model implementations, ModelData, RenderTypes |
| `obj_loader` | Loading .obj models |
Expand Down
1 change: 1 addition & 0 deletions modules/model_data/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package io.github.fabricators_of_create.porting_lib.models.data;

import com.google.common.base.Preconditions;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceArrayMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;

/**
* A container for data to be passed to {@link BakedModel} instances.
* <p>
* All objects stored in here <b>MUST BE IMMUTABLE OR THREAD-SAFE</b>.
* Properties will be accessed from another thread.
*
* @see ModelProperty
* @see BlockEntity#getRenderData()
* @see BakedModel#getQuads(BlockState, Direction, RandomSource)
*/
public final class ModelData {
public static final ModelData EMPTY = ModelData.builder().build();

private final Map<ModelProperty<?>, Object> properties;

@Nullable
private Set<ModelProperty<?>> propertySetView;

private ModelData(Map<ModelProperty<?>, Object> properties) {
this.properties = properties;
}

public Set<ModelProperty<?>> getProperties() {
var view = propertySetView;
if (view == null) {
propertySetView = view = Collections.unmodifiableSet(properties.keySet());
}
return view;
}

public boolean has(ModelProperty<?> property) {
return properties.containsKey(property);
}

@Nullable
public <T> T get(ModelProperty<T> property) {
return (T) properties.get(property);
}

public Builder derive() {
return new Builder(this);
}

public static Builder builder() {
return new Builder(null);
}

/**
* Helper to create a {@link ModelData} instance for a single property-value pair, without the verbosity
* and runtime overhead of creating a builder object.
*/
public static <T> ModelData of(ModelProperty<T> property, T value) {
Preconditions.checkState(property.test(value), "The provided value is invalid for this property.");
// Must use one of the two map types from the builder to avoid megamorphic calls to Map.get() later
Reference2ReferenceArrayMap<ModelProperty<?>, Object> map = new Reference2ReferenceArrayMap<>(1);
map.put(property, value);
return new ModelData(map);
}

public static final class Builder {
/**
* Hash maps are slower than array maps for *extremely* small maps (empty maps or singletons are the most
* extreme examples). Many block entities/models only use a single model data property, which means the
* overhead of hashing is quite wasteful. However, we do want to support any number of properties with
* reasonable performance. Therefore, we use an array map until the number of properties reaches this
* threshold, at which point we convert it to a hash map.
*/
private static final int HASH_THRESHOLD = 4;

private Map<ModelProperty<?>, Object> properties;

private Builder(@Nullable ModelData parent) {
if (parent != null) {
// When cloning the map, use the expected type based on size
properties = parent.properties.size() >= HASH_THRESHOLD ? new Reference2ReferenceOpenHashMap<>(parent.properties) : new Reference2ReferenceArrayMap<>(parent.properties);
} else {
// Allocate the maximum number of entries we'd ever put into the map.
// We convert to a hash map *after* insertion of the HASH_THRESHOLD
// entry, so we need at least that many spots.
properties = new Reference2ReferenceArrayMap<>(HASH_THRESHOLD);
}
}

@Contract("_, _ -> this")
public <T> Builder with(ModelProperty<T> property, T value) {
Preconditions.checkState(property.test(value), "The provided value is invalid for this property.");
properties.put(property, value);
// Convert to a hash map if needed
if (properties.size() == HASH_THRESHOLD && properties instanceof Reference2ReferenceArrayMap<ModelProperty<?>, Object>) {
properties = new Reference2ReferenceOpenHashMap<>(properties);
}
return this;
}

@Contract("-> new")
public ModelData build() {
return new ModelData(properties);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.fabricators_of_create.porting_lib.models.data;

import com.google.common.base.Predicates;
import java.util.function.Predicate;

/**
* A property to be used in {@link ModelData}.
* <p>
* May optionally validate incoming values.
*
* @see ModelData
*/
public class ModelProperty<T> implements Predicate<T> {
private final Predicate<T> predicate;

public ModelProperty() {
this(Predicates.alwaysTrue());
}

public ModelProperty(Predicate<T> predicate) {
this.predicate = predicate;
}

@Override
public boolean test(T value) {
return predicate.test(value);
}
}
8 changes: 8 additions & 0 deletions modules/model_data/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

{
"schemaVersion": 1,
"id": "porting_lib_model_data",
"version": "${version}",
"name": "Porting Lib Model Data",
"description": "Addon to model api to make building model data easier."
}

0 comments on commit 56347bb

Please sign in to comment.