From 323d213291f2846f7b1c4e82d319982a4fcd7301 Mon Sep 17 00:00:00 2001 From: mewhenthe Date: Fri, 1 May 2026 23:25:22 +0200 Subject: [PATCH] render robot model with blockentrenderer --- .../client/NeoComputersFabricClient.java | 2 + .../fabric/client/model/CableModel.java | 138 ------------------ .../client/model/FabricModelWrapper.java | 2 +- .../fabric/client/model/ModelLoader.java | 3 +- .../org/neoflock/neocomputers/block/Blocks.kt | 2 +- .../neoflock/neocomputers/block/RobotBlock.kt | 26 ++++ .../neocomputers/block/model/AbstractModel.kt | 4 +- .../neocomputers/block/model/RobotModel.kt | 7 +- .../neocomputers/entity/BlockEntities.kt | 6 + .../neocomputers/entity/RobotEntity.kt | 15 ++ .../entity/render/RobotEntityRenderer.kt | 96 ++++++++++++ .../neocomputers/blockstates/robot.json | 2 +- 12 files changed, 153 insertions(+), 150 deletions(-) delete mode 100644 src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/CableModel.java create mode 100644 src/main/kotlin/org/neoflock/neocomputers/block/RobotBlock.kt create mode 100644 src/main/kotlin/org/neoflock/neocomputers/entity/RobotEntity.kt create mode 100644 src/main/kotlin/org/neoflock/neocomputers/entity/render/RobotEntityRenderer.kt diff --git a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/NeoComputersFabricClient.java b/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/NeoComputersFabricClient.java index cd714ec..4c50720 100644 --- a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/NeoComputersFabricClient.java +++ b/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/NeoComputersFabricClient.java @@ -15,6 +15,7 @@ import org.neoflock.neocomputers.block.CableBlock; import org.neoflock.neocomputers.entity.BlockEntities; import org.neoflock.neocomputers.entity.render.CaseEntityRenderer; import org.neoflock.neocomputers.entity.render.RelayEntityRenderer; +import org.neoflock.neocomputers.entity.render.RobotEntityRenderer; import org.neoflock.neocomputers.entity.render.ScreenEntityRenderer; import org.neoflock.neocomputers.item.Items; import org.neoflock.neocomputers.platforms.fabric.client.model.ModelLoader; @@ -26,6 +27,7 @@ public class NeoComputersFabricClient implements ClientModInitializer { BlockEntityRenderers.register(BlockEntities.INSTANCE.getSCREEN_ENTITY().get(), ScreenEntityRenderer::new); BlockEntityRenderers.register(BlockEntities.INSTANCE.getCASE_ENTITY().get(), CaseEntityRenderer::new); BlockEntityRenderers.register(BlockEntities.INSTANCE.getRELAY_ENTITY().get(), RelayEntityRenderer::new); + BlockEntityRenderers.register(BlockEntities.INSTANCE.getROBOT_ENTITY().get(), RobotEntityRenderer::new); ColorProviderRegistry.BLOCK.register((state, world, pos, index) -> { if (index == 0) { diff --git a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/CableModel.java b/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/CableModel.java deleted file mode 100644 index cb8196a..0000000 --- a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/CableModel.java +++ /dev/null @@ -1,138 +0,0 @@ -//package org.neoflock.neocomputers.platforms.fabric.client.model; -// -//import net.fabricmc.fabric.api.renderer.v1.Renderer; -//import net.fabricmc.fabric.api.renderer.v1.RendererAccess; -//import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; -//import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; -//import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; -//import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; -//import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -//import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -//import net.minecraft.client.Minecraft; -//import net.minecraft.client.renderer.block.model.BakedQuad; -//import net.minecraft.client.renderer.block.model.ItemOverrides; -//import net.minecraft.client.renderer.block.model.ItemTransforms; -//import net.minecraft.client.renderer.texture.TextureAtlas; -//import net.minecraft.client.renderer.texture.TextureAtlasSprite; -//import net.minecraft.client.resources.model.*; -//import net.minecraft.core.BlockPos; -//import net.minecraft.core.Direction; -//import net.minecraft.resources.ResourceLocation; -//import net.minecraft.util.RandomSource; -//import net.minecraft.world.level.BlockAndTintGetter; -//import net.minecraft.world.level.block.state.BlockState; -//import org.jetbrains.annotations.Nullable; -//import org.neoflock.neocomputers.NeoComputers; -// -//import java.util.ArrayList; -//import java.util.Collection; -//import java.util.List; -//import java.util.function.Function; -//import java.util.function.Supplier; -// -//// this totally could have been done with datagen, why do i do this -//public class CableModel implements BakedModel, UnbakedModel, FabricBakedModel { -// private TextureAtlasSprite sprite = Minecraft -//// TextureAtlasSprite sprite = atlas.apply(new Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.withDefaultNamespace("block/furnace_top"))); -// -// private float MIN = 6/16F; -// private float MAX = 10/16F; -// -// private BlockState state; -// -// private Mesh mesh; -// @Override -// public List getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) { -//// NeoComputers.INSTANCE.getLOGGER().info("Obtained blockstate!"); -// this.state = state; -// return List.of(); -// } -// -// @Override -// public boolean useAmbientOcclusion() { -// return false; -// } -// -// @Override -// public boolean isGui3d() { -// return true; -// } -// -// @Override -// public boolean usesBlockLight() { -// return true; -// } -// -// @Override -// public boolean isCustomRenderer() { -// return false; -// } -// -// @Override -// public TextureAtlasSprite getParticleIcon() { -// Function atlas = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS); -// return atlas.apply(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/teto")); -// } -// -// @Override -// public ItemTransforms getTransforms() { -// return ItemTransforms.NO_TRANSFORMS; -// } -// -// @Override -// public ItemOverrides getOverrides() { -// return ItemOverrides.EMPTY; -// } -// -// @Override -// public Collection getDependencies() { -// return List.of(); -// } -// -// @Override -// public void resolveParents(Function resolver) { -// } -// -// @Override -// public @Nullable BakedModel bake(ModelBaker baker, Function spriteGetter, ModelState state) { -// TextureAtlasSprite sprite = spriteGetter.apply(new Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/teto"))); -// -// Renderer renderer = RendererAccess.INSTANCE.getRenderer(); -// MeshBuilder builder = renderer.meshBuilder(); -// QuadEmitter emitter = builder.getEmitter(); -// -// bakeCenter(emitter, sprite); -// -// -// mesh = builder.build(); -// return this; -// } -// -// @Override -// public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { -// mesh.outputTo(context.getEmitter()); -// } -// -// public void bakeCenter(QuadEmitter emitter, TextureAtlasSprite sprite) { -// for (Direction dir : Direction.values()) { -// emitter.square(dir, MIN, MIN, MAX, MAX, MIN); -// emitter.spriteBake(sprite, MutableQuadView.BAKE_LOCK_UV); -// emitter.color(-1, -1, -1, -1); -// emitter.emit(); -// } -// } -// public void bakeConnection(Direction dir, QuadEmitter emitter, TextureAtlasSprite sprite) { -// int mag = dir.getStepX()+dir.getStepZ(); // i dont want to hear it -// float bottom = dir.getStepY()==0 ? 6/16F : (dir.getStepY()==1 ? 10/16F : 0F); -// -// for (Direction d : dir.getAxis().getPlane().faces) { -// emitter.square(d, (6/16F)+0.5F*mag, bottom, 6/16F) -// } -// -// } -// -//// @Override -//// public boolean isVanillaAdapter() { -//// return false; // TODO: let this be true so maybe forge and fabric can be unified -//// } -//} diff --git a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/FabricModelWrapper.java b/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/FabricModelWrapper.java index 1a2b576..03ac22e 100644 --- a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/FabricModelWrapper.java +++ b/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/FabricModelWrapper.java @@ -49,7 +49,7 @@ public class FabricModelWrapper implements FabricBakedModel, UnbakedModel { @Override public @Nullable BakedModel bake(ModelBaker baker, Function spriteGetter, ModelState state) { - model.bake(baker, spriteGetter, state); + model.bake(spriteGetter, state); return model; } } diff --git a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/ModelLoader.java b/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/ModelLoader.java index fa06313..44d4ebe 100644 --- a/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/ModelLoader.java +++ b/src/main/java/org/neoflock/neocomputers/platforms/fabric/client/model/ModelLoader.java @@ -8,14 +8,13 @@ import org.neoflock.neocomputers.NeoComputers; import org.neoflock.neocomputers.block.model.RobotModel; public class ModelLoader implements ModelLoadingPlugin { - public static final ResourceLocation ROBOT = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "robot"); + public static final ResourceLocation ROBOT = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/robot"); @Override public void onInitializeModelLoader(Context pluginContext) { pluginContext.modifyModelOnLoad().register((original, context) -> { final ModelResourceLocation id = context.topLevelId(); if (id != null && id.id().equals(ROBOT)) { -//// NeoComputers.INSTANCE.getLOGGER().error("DOING CABLEEEEEEE"); return new FabricModelWrapper(new RobotModel()); } return original; diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/Blocks.kt b/src/main/kotlin/org/neoflock/neocomputers/block/Blocks.kt index df08650..2c8ccf0 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/Blocks.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/Blocks.kt @@ -35,7 +35,7 @@ object Blocks { val REDSTONEIO_BLOCK: RegistrySupplier = BaseBlock.register("redio") { RedstoneIOBlock() } val CABLE_BLOCK: RegistrySupplier = BaseBlock.register("cable") { CableBlock() } val RELAY_BLOCK: RegistrySupplier = BaseBlock.register("relay") { RelayBlock() } - val ROBOT_BLOCK: RegistrySupplier = BaseBlock.register("robot") { BaseBlock(BlockBehaviour.Properties.of().noOcclusion()) } + val ROBOT_BLOCK: RegistrySupplier = BaseBlock.register("robot") { RobotBlock() } fun registerBlockItems() { BLOCKS.forEach(Consumer { sup: RegistrySupplier -> diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/RobotBlock.kt b/src/main/kotlin/org/neoflock/neocomputers/block/RobotBlock.kt new file mode 100644 index 0000000..e88e9dc --- /dev/null +++ b/src/main/kotlin/org/neoflock/neocomputers/block/RobotBlock.kt @@ -0,0 +1,26 @@ +package org.neoflock.neocomputers.block + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.RenderShape +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockBehaviour +import net.minecraft.world.level.block.state.BlockState +import org.neoflock.neocomputers.NeoComputers +import org.neoflock.neocomputers.entity.RobotEntity + +class RobotBlock : BaseBlock(Properties.of().noOcclusion()), EntityBlock { // todo: node stuff + override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity { + NeoComputers.LOGGER.info("block entity created..") + Blocks.CHEST + return RobotEntity(pos, state) + } + + override fun getRenderShape(state: BlockState): RenderShape { + return RenderShape.INVISIBLE // this is so not good + } +// public RenderShape getRenderShape(BlockState state) { +// return RenderShape.ENTITYBLOCK_ANIMATED; +// } +} \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/model/AbstractModel.kt b/src/main/kotlin/org/neoflock/neocomputers/block/model/AbstractModel.kt index cb96b61..cfdb81a 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/model/AbstractModel.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/model/AbstractModel.kt @@ -19,7 +19,7 @@ import net.minecraft.util.RandomSource import net.minecraft.world.level.block.state.BlockState import java.util.function.Function -abstract class AbstractModel : BakedModel, UnbakedModel { +abstract class AbstractModel : BakedModel { val atlas = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS) var baker: ModelBaker? = null; var mesh: Map> = mapOf( @@ -47,7 +47,7 @@ abstract class AbstractModel : BakedModel, UnbakedModel { return atlas.apply(particle()) } -// abstract fun bake(atlas: (ResourceLocation) -> TextureAtlasSprite) + abstract fun bake(atlas: Function, state: ModelState): BakedModel abstract fun particle(): ResourceLocation diff --git a/src/main/kotlin/org/neoflock/neocomputers/block/model/RobotModel.kt b/src/main/kotlin/org/neoflock/neocomputers/block/model/RobotModel.kt index 614f28f..fcfd532 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/block/model/RobotModel.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/block/model/RobotModel.kt @@ -23,14 +23,11 @@ class RobotModel() : AbstractModel() { val size = 0.4f val l = 0.5f-size; val h = 0.5f+size; - override fun getDependencies(): Collection = listOf() - - override fun resolveParents(resolver: Function) { } // override fun bake(atlas: (ResourceLocation) -> TextureAtlasSprite) { - override fun bake(baker: ModelBaker, atlas: Function, state: ModelState): BakedModel? { + override fun bake(atlas: Function, state: ModelState): BakedModel { NeoComputers.LOGGER.info("baking") - val sprite = atlas.apply(Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/robot")))!! + val sprite = atlas.apply(Material(TextureAtlas.LOCATION_BLOCKS, ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "robot")))!! var verts: SchizoConsumer = SchizoConsumer() // top pyramid, enjoy reading this schizo mess diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt index 7de8b59..72ecd71 100644 --- a/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/BlockEntities.kt @@ -94,6 +94,12 @@ object BlockEntities { ) } + val ROBOT_ENTITY: RegistrySupplier> = BLOCKENTITIES.register("robot") { + BlockEntityType( + ::RobotEntity, setOf(Blocks.ROBOT_BLOCK.get()), BullshitFix() + ) + } + fun registerPowerBlocks() { PowerManager.registerPowerDevice(CAPACITOR_ENTITY.get()) PowerManager.registerPowerDevice(CAPACITOR2_ENTITY.get()) diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/RobotEntity.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/RobotEntity.kt new file mode 100644 index 0000000..2b802db --- /dev/null +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/RobotEntity.kt @@ -0,0 +1,15 @@ +package org.neoflock.neocomputers.entity + +import net.minecraft.client.model.geom.ModelPart +import net.minecraft.core.BlockPos +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockState +import org.neoflock.neocomputers.NeoComputers + +class RobotEntity(pos: BlockPos, state: BlockState) : BlockEntity(BlockEntities.ROBOT_ENTITY.get(), pos, state,) { + val body: ModelPart? = null + + init { + NeoComputers.LOGGER.info("yooo") + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/neoflock/neocomputers/entity/render/RobotEntityRenderer.kt b/src/main/kotlin/org/neoflock/neocomputers/entity/render/RobotEntityRenderer.kt new file mode 100644 index 0000000..514460a --- /dev/null +++ b/src/main/kotlin/org/neoflock/neocomputers/entity/render/RobotEntityRenderer.kt @@ -0,0 +1,96 @@ +package org.neoflock.neocomputers.entity.render + +import com.mojang.blaze3d.vertex.DefaultVertexFormat +import com.mojang.blaze3d.vertex.PoseStack +import com.mojang.blaze3d.vertex.VertexConsumer +import com.mojang.blaze3d.vertex.VertexFormat +import net.minecraft.client.Minecraft +import net.minecraft.client.renderer.ItemBlockRenderTypes +import net.minecraft.client.renderer.MultiBufferSource +import net.minecraft.client.renderer.RenderStateShard +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.block.ModelBlockRenderer +import net.minecraft.client.renderer.block.model.BakedQuad +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider +import net.minecraft.client.renderer.texture.OverlayTexture +import net.minecraft.client.renderer.texture.TextureAtlas +import net.minecraft.client.renderer.texture.TextureAtlasSprite +import net.minecraft.client.resources.model.BakedModel +import net.minecraft.client.resources.model.Material +import net.minecraft.client.resources.model.ModelState +import net.minecraft.resources.ResourceLocation +import net.minecraft.util.RandomSource +import org.neoflock.neocomputers.NeoComputers +import org.neoflock.neocomputers.block.model.RobotModel +import org.neoflock.neocomputers.entity.RobotEntity +import kotlin.math.sin + +class RobotEntityRenderer(val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer { +// val body: ModelPart = context.modelSet.bakeLayer(ModelLayerLocation(ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/robot"), "inventory")) +// val body: BlockModel = BlockModel + val atlas: (Material?) -> TextureAtlasSprite = { m -> + Minecraft.getInstance().getTextureAtlas(m!!.atlasLocation()).apply(m!!.texture()) + } + + val loc: ResourceLocation = ResourceLocation.fromNamespaceAndPath(NeoComputers.MODID, "block/robot") + var model: BakedModel? = RobotModel().bake(atlas, object : ModelState {}) + val renderer: ModelBlockRenderer = ModelBlockRenderer(Minecraft.getInstance().blockColors) // so ass + +// val RENDER_TYPE = RenderType.create("nc_case", DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, +// RenderType.TRANSIENT_BUFFER_SIZE, RenderType.CompositeState.builder() +// .setShaderState(RenderStateShard.RENDERTYPE_) +// .createCompositeState(false)) + +// val RENDER_TYPE = { tex: ResourceLocation -> RenderType.create("nc_robot", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, VertexFormat.Mode.QUADS, +// RenderType.SMALL_BUFFER_SIZE, RenderType.CompositeState.builder() +// .setShaderState(RenderStateShard.POSITION_COLOR_TEX_LIGHTMAP_SHADER) +// .setLightmapState(RenderStateShard.LIGHTMAP) +// .setTextureState(RenderStateShard.TextureStateShard(tex, false, false)) +// .createCompositeState(false)) } + + override fun render(ent: RobotEntity, partialTick: Float, poseStack: PoseStack, bufferSource: MultiBufferSource, packedLight: Int, packedOverlay: Int) { +// if (model == null) this.model = Minecraft.getInstance().modelManager.getModel(loc) // worst cant describe how much ts sux holy shit!!!!! +// if (model == null) this.model = +// NeoComputers.LOGGER.info(model.toString()) + poseStack.pushPose() +// NeoComputers.LOGGER.info(sin(ent.level!!.dayTime.toFloat()/20F).toString()) + poseStack.translate(0f, sin(ent.level!!.dayTime.toFloat()/20F)*0.2F, 0f) +// val buffer = bufferSource.getBuffer(RENDER_TYPE(ResourceLocation.withDefaultNamespace("textures/atlas/block.png-atlas"))) +// Minecraft.getInstance().blockRenderer +// val buffer = bufferSource.getBuffer(RenderType.entityCutout(ResourceLocation.withDefaultNamespace("textures/atlas/block.png"))) + NeoComputers.LOGGER.info(packedLight.toHexString()) + renderModel(poseStack.last(), bufferSource, model!!.getQuads(ent.blockState, null, RandomSource.create()), packedLight) + +// renderer.renderModel(poseStack.last(), bufferSource, ent.blockState, model!!, 1f, 1f, 1f, 0xF000F0, packedOverlay) + + poseStack.popPose() + } + + fun renderModel(pose: PoseStack.Pose, bufferSource: MultiBufferSource, quads: List, light: Int) { + for (quad in quads) { + val data = quad.vertices + val buffer = bufferSource.getBuffer(RenderType.entitySolid(ResourceLocation.fromNamespaceAndPath( + NeoComputers.MODID, "textures/block/robot.png"))) // TODO: use atlas tex instead of ts, also slanted normals + for (i in 0..3) { + val offset = i*8 + val fu = quad.sprite.u1 - quad.sprite.u0 + val fv = quad.sprite.v1 - quad.sprite.v0 + + + val x = Float.fromBits(data[offset+0]) + val y = Float.fromBits(data[offset+1]) + val z = Float.fromBits(data[offset+2]) + val col = data[offset+3] + val u = Float.fromBits(data[offset+4])/fu + val v = Float.fromBits(data[offset+5])/fv + + buffer.addVertex(pose, x, y, z).setColor(col).setUv(u, v).setLight(light).setNormal(quad.direction.stepX.toFloat(),quad.direction.stepY.toFloat(),quad.direction.stepZ.toFloat()).setOverlay( + OverlayTexture.NO_OVERLAY) +// buffer.addVertex(pose, x, y, z).setUv(u, v) + } +// Minecraft.getInstance().blockRenderer. + } + } + +} \ No newline at end of file diff --git a/src/main/resources/assets/neocomputers/blockstates/robot.json b/src/main/resources/assets/neocomputers/blockstates/robot.json index c387762..06d54ed 100644 --- a/src/main/resources/assets/neocomputers/blockstates/robot.json +++ b/src/main/resources/assets/neocomputers/blockstates/robot.json @@ -1,7 +1,7 @@ { "variants": { "": { - "model": "neocomputers:block/robot" + "model": "" } } } \ No newline at end of file