public void ShouldWriteCustomTranslation() { var texture = new CompositeTexture( "Name", 10, 10, Patches: ImmutableArray.Create( new Patch( "Name2", 0, 0, Translation: new Translation.Custom("SomeString")))); var actual = GetText(texture); var expected = Assemble( "Texture Name, 10, 10", "{", "\tPatch Name2, 0, 0", "\t{", "\t\tTranslation \"SomeString\"", "\t}", "}"); actual.Should().Be(expected); }
public void ShouldWriteDesaturateTranslation() { var texture = new CompositeTexture( "Name", 10, 10, Patches: ImmutableArray.Create( new Patch( "Name2", 0, 0, Translation: new Translation.Desaturate(20)))); var actual = GetText(texture); var expected = Assemble( "Texture Name, 10, 10", "{", "\tPatch Name2, 0, 0", "\t{", "\t\tTranslation Desaturate, 20", "\t}", "}"); actual.Should().Be(expected); }
public void ShouldWriteExampleTexture2() { var texture = new CompositeTexture( "SWEXPLUP", 512, 512, XScale: 4, YScale: 4, Patches: ImmutableArray.Create( new Patch( "AG_512_2", 0, 0), new Patch( "MSW1_UP", 64, 288, Style: RenderStyle.CopyAlpha))); var actual = GetText(texture); var expected = Assemble( "Texture SWEXPLUP, 512, 512", "{", "\tXScale 4.0", "\tYScale 4.0", "\tPatch AG_512_2, 0, 0", "\tPatch MSW1_UP, 64, 288", "\t{", "\t\tStyle CopyAlpha", "\t}", "}"); actual.Should().Be(expected); }
public FlaskTextureSource(ICoreClientAPI capi, ItemStack forContents, CompositeTexture contentTexture, Block flask) { this.capi = capi; this.forContents = forContents; this.contentTexture = contentTexture; this.corkTextPos = capi.BlockTextureAtlas.GetPosition(flask, "topper"); this.blockTextPos = capi.BlockTextureAtlas.GetPosition(flask, "glass"); this.bracingTextPos = capi.BlockTextureAtlas.GetPosition(flask, "bracing"); }
private static string GetText(CompositeTexture compositeTexture) { using var ms = new MemoryStream(); using (var writer = new StreamWriter(ms, Encoding.ASCII, leaveOpen: true)) { TexturesWriter.Write(compositeTexture, writer); } ms.Position = 0; using var reader = new StreamReader(ms); return(reader.ReadToEnd()); }
public override void OnLoaded(ICoreAPI api) { base.OnLoaded(api); if (api.Side == EnumAppSide.Client) { if (Textures == null || !Textures.TryGetValue("specialSecondTexture", out grassTex)) { grassTex = Textures?.First().Value; } } }
public virtual TextureAtlasPosition this[string textureCode] { get { CompositeTexture cpt = null; if (extraTexturesByTextureName?.TryGetValue(textureCode, out cpt) == true) { return(capi.EntityTextureAtlas.Positions[cpt.Baked.TextureSubId]); } return(defaultTexSource[textureCode]); } }
public override TextureAtlasPosition this[string textureCode] { get { CompositeTexture cpt = null; if (extraTexturesByTextureName != null && extraTexturesByTextureName.TryGetValue(textureCode, out cpt) == true) { return(capi.EntityTextureAtlas.Positions[cpt.Baked.TextureSubId]); } return(skinTexPos); } }
private void loadTexture(Shape entityShape, string code, AssetLocation location, int textureWidth, int textureHeight, string shapePathForLogging) { ICoreClientAPI api = entity.World.Api as ICoreClientAPI; CompositeTexture ctex = new CompositeTexture() { Base = location }; entityShape.TextureSizes[code] = new int[] { textureWidth, textureHeight }; AssetLocation shapeTexloc = location; // Weird backreference to the shaperenderer. Should be refactored. var texturesByLoc = (entity as EntityAgent).extraTextureByLocation; var texturesByName = (entity as EntityAgent).extraTexturesByTextureName; BakedCompositeTexture bakedCtex; if (!texturesByLoc.TryGetValue(shapeTexloc, out bakedCtex)) { int textureSubId = 0; TextureAtlasPosition texpos; IAsset texAsset = api.Assets.TryGet(location.Clone().WithPathPrefixOnce("textures/").WithPathAppendixOnce(".png")); if (texAsset != null) { BitmapRef bmp = texAsset.ToBitmap(api); api.EntityTextureAtlas.InsertTexture(bmp, out textureSubId, out texpos, -1); } else { api.World.Logger.Warning("Skin part shape {0} defined texture {1}, no such texture found.", shapePathForLogging, location); } ctex.Baked = new BakedCompositeTexture() { BakedName = location, TextureSubId = textureSubId }; texturesByName[code] = ctex; texturesByLoc[shapeTexloc] = ctex.Baked; } else { ctex.Baked = bakedCtex; texturesByName[code] = ctex; } }
public override int GetRandomColor(ICoreClientAPI capi, BlockPos pos, BlockFacing facing) { BELantern be = capi.World.BlockAccessor.GetBlockEntity(pos) as BELantern; if (be != null) { CompositeTexture tex = null; if (Textures.TryGetValue(be.material, out tex)) { return(capi.BlockTextureAtlas.GetRandomColor(tex.Baked.TextureSubId)); } } return(base.GetRandomColor(capi, pos, facing)); }
public override void OnJsonTesselation(ref MeshData sourceMesh, ref int[] lightRgbsByCorner, BlockPos pos, Block[] chunkExtBlocks, int extIndex3d) { Block upBlock = chunkExtBlocks[extIndex3d + TileSideEnum.MoveIndex[TileSideEnum.Up]]; if (upBlock is BlockFence) { int var = (GameMath.MurmurHash3Mod(pos.X, pos.Y, pos.Z, 8) + 1); MeshData mesh; if (!continousFenceMeches.TryGetValue(cntCode + var, out mesh)) { AssetLocation loc = Shape.Base.Clone(); loc.Path = loc.Path.Replace("-top", ""); loc.WithPathAppendixOnce(".json"); loc.WithPathPrefixOnce("shapes/"); Shape shape = capi.Assets.TryGet(loc).ToObject <Shape>(); CompositeTexture ct = Textures["wall"]; int prevSubid = ct.Baked.TextureSubId; ct.Baked.TextureSubId = ct.Baked.BakedVariants[GameMath.MurmurHash3Mod(pos.X, pos.Y, pos.Z, ct.Alternates.Length)].TextureSubId; capi.Tesselator.TesselateShape(this, shape, out mesh, new Vec3f(Shape.rotateX, Shape.rotateY, Shape.rotateZ), Shape.QuantityElements, Shape.SelectiveElements); ct.Baked.TextureSubId = prevSubid; continousFenceMeches[cntCode] = mesh; } sourceMesh = mesh; } // Todo: make this work /* int nBlockId = chunkExtIds[extIndex3d + TileSideEnum.MoveIndex[TileSideEnum.Up]]; * Block upblock = api.World.Blocks[nBlockId]; * * if (upblock.snowLevel >= 1 && snowLayerBlock != null) * { * sourceMesh = sourceMesh.Clone(); * sourceMesh.AddMeshData(capi.TesselatorManager.GetDefaultBlockMesh(snowLayerBlock)); * return; * }*/ return; // no windwave for solid fences! //base.OnJsonTesselation(ref sourceMesh, ref lightRgbsByCorner, pos, chunkExtIds, chunkLightExt, extIndex3d); }
public override int GetRandomColor(ICoreClientAPI capi, BlockPos pos, BlockFacing facing) { BlockEntityGenericTypedContainer be = capi.World.BlockAccessor.GetBlockEntity(pos) as BlockEntityGenericTypedContainer; if (be != null) { CompositeTexture tex = null; if (!Textures.TryGetValue(be.type + "-lid", out tex)) { Textures.TryGetValue(be.type + "-top", out tex); } return(capi.BlockTextureAtlas.GetRandomPixel(tex?.Baked == null ? 0 : tex.Baked.TextureSubId)); } return(base.GetRandomColor(capi, pos, facing)); }
public static void Write(CompositeTexture texture, StreamWriter writer) { var output = new WriterUtil(); var optional = texture.Optional ? "optional " : ""; output = output .Line($"{texture.Namespace} {optional}{texture.Name}, {texture.Width}, {texture.Height}") .OpenBrace() .OptionalLine("XScale", texture.XScale, 1) .OptionalLine("YScale", texture.YScale, 1) .OptionalLine(nameof(texture.Offset), texture.Offset, new TextureOffset(), offset => $"{offset.Horizontal}, {offset.Vertical}") .Flag(nameof(texture.WorldPanning), texture.WorldPanning) .Flag(nameof(texture.NoDecals), texture.NoDecals); foreach (var patch in texture.Patches) {
private void fullnessMeterDraw(Context ctx, ImageSurface surface, ElementBounds currentBounds) { ItemSlot liquidSlot = Inventory[1]; // Water ID Slot if (liquidSlot.Empty) { return; } BlockEntitySingleSink besink = capi.World.BlockAccessor.GetBlockEntity(BlockEntityPosition) as BlockEntitySingleSink; float itemsPerLitre = 1f; int capacity = besink.CapacityLitres; WaterTightContainableProps props = BlockLiquidContainerBase.GetInContainerProps(liquidSlot.Itemstack); if (props != null) { itemsPerLitre = props.ItemsPerLitre; capacity = Math.Max(capacity, props.MaxStackSize); } float fullnessRelative = liquidSlot.StackSize / itemsPerLitre / capacity; double offY = (1 - fullnessRelative) * currentBounds.InnerHeight; ctx.Rectangle(0, offY, currentBounds.InnerWidth, currentBounds.InnerHeight - offY); //ctx.SetSourceRGBA(ravg/255.0, gavg / 255.0, bavg / 255.0, aavg / 255.0); //ctx.Fill(); CompositeTexture tex = liquidSlot.Itemstack.Collectible.Attributes?["waterTightContainerProps"]?["texture"]?.AsObject <CompositeTexture>(null, liquidSlot.Itemstack.Collectible.Code.Domain); if (tex != null) { ctx.Save(); Matrix m = ctx.Matrix; m.Scale(GuiElement.scaled(3), GuiElement.scaled(3)); ctx.Matrix = m; AssetLocation loc = tex.Base.Clone().WithPathAppendixOnce(".png"); GuiElement.fillWithPattern(capi, ctx, loc.Path, true, false); ctx.Restore(); } }
public override void OnLoaded(ICoreAPI api) { base.OnLoaded(api); chunksize = api.World.BlockAccessor.ChunkSize; if (api is ICoreClientAPI) { Block fullCoverBlock = api.World.GetBlock(this.CodeWithParts("7")); mapColorTextureSubId = fullCoverBlock.Textures["specialSecondTexture"].Baked.TextureSubId; var soilBlock = api.World.GetBlock(new AssetLocation("soil-low-normal")); if (soilBlock.Textures == null || !soilBlock.Textures.TryGetValue("specialSecondTexture", out grassTex)) { grassTex = soilBlock.Textures?.First().Value; } } }
public void Rebase(DynFoliageProperties props) { if (TexturesBasePath == null) { TexturesBasePath = props.TexturesBasePath; } if (Textures == null) { Textures = new Dictionary <string, CompositeTexture>(); foreach (var val in props.Textures) { Textures[val.Key] = val.Value.Clone(); } } LeafParticlesTexture = props.LeafParticlesTexture?.Clone(); BlossomParticlesTexture = props.BlossomParticlesTexture?.Clone(); }
// We need the tool item textures also in the block atlas public override void OnCollectTextures(ICoreAPI api, ITextureLocationDictionary textureDict) { base.OnCollectTextures(api, textureDict); for (int i = 0; i < api.World.Items.Count; i++) { Item item = api.World.Items[i]; if (item.Tool == null && item.Attributes?["rackable"].AsBool() != true) { continue; } ToolTextures tt = new ToolTextures(); if (item.Shape != null) { IAsset asset = api.Assets.TryGet(item.Shape.Base.Clone().WithPathPrefixOnce("shapes/").WithPathAppendixOnce(".json")); if (asset != null) { Shape shape = asset.ToObject <Shape>(); foreach (var val in shape.Textures) { CompositeTexture ctex = new CompositeTexture(val.Value.Clone()); ctex.Bake(api.Assets); textureDict.AddTextureLocation(new AssetLocationAndSource(ctex.Baked.BakedName, "Shape code " + item.Shape.Base)); tt.TextureSubIdsByCode[val.Key] = textureDict[new AssetLocationAndSource(ctex.Baked.BakedName)]; } } } foreach (var val in item.Textures) { val.Value.Bake(api.Assets); textureDict.AddTextureLocation(new AssetLocationAndSource(val.Value.Baked.BakedName, "Item code " + item.Code)); tt.TextureSubIdsByCode[val.Key] = textureDict[new AssetLocationAndSource(val.Value.Baked.BakedName)]; } ToolTextureSubIds(api)[item] = tt; } }
public void ShouldWriteTextureOffset() { var texture = new CompositeTexture( "Name", 10, 10, Offset: new TextureOffset(5, 6), Patches: ImmutableArray <Patch> .Empty); var actual = GetText(texture); var expected = Assemble( "Texture Name, 10, 10", "{", "\tOffset 5, 6", "}"); actual.Should().Be(expected); }
private void fullnessMeterDraw(Context ctx, ImageSurface surface, ElementBounds currentBounds) { ItemSlot liquidSlot = Inventory[6] as ItemSlot; if (liquidSlot.Empty) { return; } BlockEntityCauld becauld = capi.World.BlockAccessor.GetBlockEntity(BlockEntityPosition) as BlockEntityCauld; float itemsPerLitre = 1f; int capacity = becauld.capacityLitres; WaterTightContainableProps props = BlockLiquidContainerBase.GetContainableProps(liquidSlot.Itemstack); if (props != null) { itemsPerLitre = props.ItemsPerLitre; capacity = Math.Max(capacity, props.MaxStackSize); } float fullnessRelative = liquidSlot.StackSize / itemsPerLitre / capacity; double offY = (1 - fullnessRelative) * currentBounds.InnerHeight; ctx.Rectangle(0, offY, currentBounds.InnerWidth, currentBounds.InnerHeight - offY); CompositeTexture tex = props?.Texture ?? liquidSlot.Itemstack.Collectible.Attributes?["inContainerTexture"].AsObject <CompositeTexture>(null, liquidSlot.Itemstack.Collectible.Code.Domain); if (tex != null) { ctx.Save(); Matrix m = ctx.Matrix; m.Scale(GuiElement.scaled(3), GuiElement.scaled(3)); ctx.Matrix = m; AssetLocation loc = tex.Base.Clone().WithPathAppendixOnce(".png"); GuiElement.fillWithPattern(capi, ctx, loc, true, false, tex.Alpha); ctx.Restore(); } }
public void ShouldNotWritePatchParentsIfNoContent() { var texture = new CompositeTexture( "Name", 10, 10, Patches: ImmutableArray.Create( new Patch( "Name2", 0, 0))); var actual = GetText(texture); var expected = Assemble( "Texture Name, 10, 10", "{", "\tPatch Name2, 0, 0", "}"); actual.Should().Be(expected); }
public void ShouldWriteLadderTexture() { var texture = new CompositeTexture( "ALADDER2", 72, 256, Patches: ImmutableArray.Create( new Patch("RW45_1", 0, 0, Rotate: PatchRotation.Rotate90), new Patch("RW45_1", 0, 64, Rotate: PatchRotation.Rotate90), new Patch("RW45_1", 0, 128, Rotate: PatchRotation.Rotate90), new Patch("RW45_1", 0, 172, Rotate: PatchRotation.Rotate90))); var actual = GetText(texture); var expected = Assemble( "Texture ALADDER2, 72, 256", "{", "\tPatch RW45_1, 0, 0", "\t{", "\t\tRotate 90", "\t}", "\tPatch RW45_1, 0, 64", "\t{", "\t\tRotate 90", "\t}", "\tPatch RW45_1, 0, 128", "\t{", "\t\tRotate 90", "\t}", "\tPatch RW45_1, 0, 172", "\t{", "\t\tRotate 90", "\t}", "}"); actual.Should().Be(expected); }
public void TesselateShape() { CompositeShape compositeShape = entity.Properties.Client.Shape; Shape entityShape = entity.Properties.Client.LoadedShape; if (entityShape == null) { return; } ShapeElement headElement = null; // Only for player entity for now if (entityShape.Elements != null && HeadControl) { headElement = FindHead(entityShape.Elements); } entityShape.ResolveAndLoadJoints(headElement); ITexPositionSource texSource = GetTextureSource(); MeshData meshdata; if (entity.Properties.Client.Shape.VoxelizeTexture) { int altTexNumber = entity.WatchedAttributes.GetInt("textureIndex", 0); TextureAtlasPosition pos = texSource["all"]; CompositeTexture tex = altTexNumber == 0 ? entity.Properties.Client.FirstTexture : entity.Properties.Client.FirstTexture.Alternates[altTexNumber - 1]; meshdata = capi.Tesselator.VoxelizeTexture(tex, capi.EntityTextureAtlas.Size, pos); for (int i = 0; i < meshdata.xyz.Length; i += 3) { meshdata.xyz[i] -= 0.125f; meshdata.xyz[i + 1] -= 0.5f; meshdata.xyz[i + 2] += 0.125f / 2; } curAnimator = new BlendEntityAnimator(entity, new Animation[0], new ShapeElement[0], new Dictionary <int, AnimationJoint>()); } else { string animDictkey = (entity.Code + entity.Properties.Client.Shape.Base.ToString()); try { capi.Tesselator.TesselateShapeWithJointIds("entity", entityShape, out meshdata, texSource, new Vec3f(), compositeShape.QuantityElements, compositeShape.SelectiveElements); } catch (Exception e) { capi.World.Logger.Fatal("Failed tesselating entity {0} with id {1}. Entity will probably be invisible!. The teselator threw {2}", entity.Code, entity.EntityId, e); curAnimator = new BlendEntityAnimator(entity, entityShape.Animations, entityShape.Elements, entityShape.JointsById, headElement); return; } // We cache animations because they are cpu intensive to calculate if (AnimationsByShape.ContainsKey(animDictkey)) { entityShape.Animations = AnimationsByShape[animDictkey]; } else { for (int i = 0; entityShape.Animations != null && i < entityShape.Animations.Length; i++) { entityShape.Animations[i].GenerateAllFrames(entityShape.Elements, entityShape.JointsById); } AnimationsByShape[animDictkey] = entityShape.Animations; } curAnimator = new BlendEntityAnimator(entity, entityShape.Animations, entityShape.Elements, entityShape.JointsById, headElement); } meshdata.Rgba2 = null; if (meshRefOpaque != null) { capi.Render.DeleteMesh(meshRefOpaque); meshRefOpaque = null; } if (meshRefOit != null) { capi.Render.DeleteMesh(meshRefOit); meshRefOit = null; } MeshData opaqueMesh = meshdata.Clone().Clear(); MeshData oitMesh = meshdata.Clone().Clear(); opaqueMesh.AddMeshData(meshdata, EnumChunkRenderPass.Opaque); oitMesh.AddMeshData(meshdata, EnumChunkRenderPass.Transparent); if (opaqueMesh.VerticesCount > 0) { meshRefOpaque = capi.Render.UploadMesh(opaqueMesh); } if (oitMesh.VerticesCount > 0) { meshRefOit = capi.Render.UploadMesh(oitMesh); } }
public ContainerTextureSource(ICoreClientAPI capi, ItemStack forContents, CompositeTexture contentTexture) { this.capi = capi; this.forContents = forContents; this.contentTexture = contentTexture; }
public virtual void TesselateShape() { if (!loaded) { return; } shapeFresh = true; CompositeShape compositeShape = OverrideCompositeShape != null ? OverrideCompositeShape : entity.Properties.Client.Shape; Shape entityShape = OverrideEntityShape != null ? OverrideEntityShape : entity.Properties.Client.LoadedShapeForEntity; if (entityShape == null) { return; } OnTesselation?.Invoke(ref entityShape, compositeShape.Base.ToString()); entity.OnTesselation(ref entityShape, compositeShape.Base.ToString()); defaultTexSource = GetTextureSource(); TyronThreadPool.QueueTask(() => { MeshData meshdata; if (entity.Properties.Client.Shape.VoxelizeTexture) { int altTexNumber = entity.WatchedAttributes.GetInt("textureIndex", 0); TextureAtlasPosition pos = defaultTexSource["all"]; CompositeTexture[] Alternates = entity.Properties.Client.FirstTexture.Alternates; CompositeTexture tex = altTexNumber == 0 ? entity.Properties.Client.FirstTexture : Alternates[altTexNumber % Alternates.Length]; meshdata = capi.Tesselator.VoxelizeTexture(tex, capi.EntityTextureAtlas.Size, pos); for (int i = 0; i < meshdata.xyz.Length; i += 3) { meshdata.xyz[i] -= 0.125f; meshdata.xyz[i + 1] -= 0.5f; meshdata.xyz[i + 2] += 0.125f / 2; } } else { try { TesselationMetaData meta = new TesselationMetaData() { quantityElements = compositeShape.QuantityElements, selectiveElements = compositeShape.SelectiveElements, texSource = this, withJointIds = true, withDamageEffect = true, typeForLogging = "entity" }; capi.Tesselator.TesselateShape(meta, entityShape, out meshdata, new Vec3f(compositeShape.rotateX, compositeShape.rotateY, compositeShape.rotateZ)); meshdata.Translate(compositeShape.offsetX, compositeShape.offsetY, compositeShape.offsetZ); } catch (Exception e) { capi.World.Logger.Fatal("Failed tesselating entity {0} with id {1}. Entity will probably be invisible!. The teselator threw {2}", entity.Code, entity.EntityId, e); return; } } MeshData opaqueMesh = meshdata.Clone().Clear(); MeshData oitMesh = meshdata.Clone().Clear(); opaqueMesh.AddMeshData(meshdata, EnumChunkRenderPass.Opaque); oitMesh.AddMeshData(meshdata, EnumChunkRenderPass.Transparent); capi.Event.EnqueueMainThreadTask(() => { if (meshRefOpaque != null) { meshRefOpaque.Dispose(); meshRefOpaque = null; } if (meshRefOit != null) { meshRefOit.Dispose(); meshRefOit = null; } if (capi.IsShuttingDown) { return; } if (opaqueMesh.VerticesCount > 0) { meshRefOpaque = capi.Render.UploadMesh(opaqueMesh); } if (oitMesh.VerticesCount > 0) { meshRefOit = capi.Render.UploadMesh(oitMesh); } }, "uploadentitymesh"); capi.TesselatorManager.ThreadDispose(); }); }
/// <summary> /// /// </summary> /// <param name="code">Any unique Identifier</param> /// <param name="cshape"></param> /// <param name="entityShape"></param> /// <param name="shapePathForLogging"></param> /// <param name="disableElements"></param> /// <returns></returns> protected Shape addGearToShape(string code, CompositeShape cshape, Shape entityShape, string shapePathForLogging, string[] disableElements = null, Dictionary <string, AssetLocation> textureOverrides = null) { AssetLocation shapePath = shapePath = cshape.Base.CopyWithPath("shapes/" + cshape.Base.Path + ".json"); if (disableElements != null) { foreach (var val in disableElements) { entityShape.RemoveElementByName(val); } } IAsset asset = Api.Assets.TryGet(shapePath); if (asset == null) { Api.World.Logger.Warning("Compositshape {0} (code: {2}) defined but not found, was supposed to be at {1}. Part will be invisible.", cshape.Base, shapePath, code); return(null); } Shape armorShape; try { armorShape = asset.ToObject <Shape>(); } catch (Exception e) { Api.World.Logger.Warning("Exception thrown when trying to load gear shape {0} (code: {2}) . Will be invisible. Exception: {1}", cshape.Base, e, code); return(null); } bool added = applyStepParents(null, armorShape.Elements, entityShape, code, cshape, shapePathForLogging); if (added && armorShape.Textures != null) { Dictionary <string, AssetLocation> newdict = new Dictionary <string, AssetLocation>(); foreach (var val in armorShape.Textures) { newdict[code + "-" + val.Key] = val.Value; } // Texture overrides if (textureOverrides != null) { foreach (var val in textureOverrides) { newdict[code + "-" + val.Key] = val.Value; } } armorShape.Textures = newdict; foreach (var val in armorShape.Textures) { CompositeTexture ctex = new CompositeTexture() { Base = val.Value }; entityShape.TextureSizes[val.Key] = new int[] { armorShape.TextureWidth, armorShape.TextureHeight }; AssetLocation armorTexLoc = val.Value; // Weird backreference to the shaperenderer. Should be refactored. var texturesByLoc = extraTextureByLocation; var texturesByName = extraTexturesByTextureName; BakedCompositeTexture bakedCtex; ICoreClientAPI capi = Api as ICoreClientAPI; if (!texturesByLoc.TryGetValue(armorTexLoc, out bakedCtex)) { int textureSubId = 0; TextureAtlasPosition texpos; IAsset texAsset = Api.Assets.TryGet(val.Value.Clone().WithPathPrefixOnce("textures/").WithPathAppendixOnce(".png")); if (texAsset != null) { BitmapRef bmp = texAsset.ToBitmap(capi); capi.EntityTextureAtlas.InsertTexture(bmp, out textureSubId, out texpos); } else { capi.World.Logger.Warning("Entity armor shape {0} defined texture {1}, not no such texture found.", shapePath, val.Value); } ctex.Baked = new BakedCompositeTexture() { BakedName = val.Value, TextureSubId = textureSubId }; texturesByName[val.Key] = ctex; texturesByLoc[armorTexLoc] = ctex.Baked; } else { ctex.Baked = bakedCtex; texturesByName[val.Key] = ctex; } } foreach (var val in armorShape.TextureSizes) { entityShape.TextureSizes[val.Key] = val.Value; } } return(entityShape); }
protected Shape addGearToShape(ItemSlot slot, Shape entityShape, string shapePathForLogging) { if (slot.Empty) { return(entityShape); } ItemStack stack = slot.Itemstack; JsonObject attrObj = stack.Collectible.Attributes; string[] disableElements = attrObj?["disableElements"]?.AsArray <string>(null); if (disableElements != null) { foreach (var val in disableElements) { entityShape.RemoveElementByName(val); } } if (attrObj?["wearableAttachment"].Exists != true) { return(entityShape); } CompositeShape compArmorShape = !attrObj["attachShape"].Exists ? (stack.Class == EnumItemClass.Item ? stack.Item.Shape : stack.Block.Shape) : attrObj["attachShape"].AsObject <CompositeShape>(null, stack.Collectible.Code.Domain); AssetLocation shapePath = shapePath = compArmorShape.Base.CopyWithPath("shapes/" + compArmorShape.Base.Path + ".json"); IAsset asset = Api.Assets.TryGet(shapePath); if (asset == null) { Api.World.Logger.Warning("Entity armor shape {0} defined in {1} {2} not found, was supposed to be at {3}. Armor piece will be invisible.", compArmorShape.Base, stack.Class, stack.Collectible.Code, shapePath); return(null); } Shape armorShape; try { armorShape = asset.ToObject <Shape>(); } catch (Exception e) { Api.World.Logger.Warning("Exception thrown when trying to load entity armor shape {0} defined in {1} {2}. Armor piece will be invisible. Exception: {3}", compArmorShape.Base, slot.Itemstack.Class, slot.Itemstack.Collectible.Code, e); return(null); } bool added = false; foreach (var val in armorShape.Elements) { ShapeElement elem; if (val.StepParentName != null) { elem = entityShape.GetElementByName(val.StepParentName, StringComparison.InvariantCultureIgnoreCase); if (elem == null) { Api.World.Logger.Warning("Entity armor shape {0} defined in {1} {2} requires step parent element with name {3}, but no such element was found in shape {4}. Will not be visible.", compArmorShape.Base, slot.Itemstack.Class, slot.Itemstack.Collectible.Code, val.StepParentName, shapePathForLogging); continue; } } else { Api.World.Logger.Warning("Entity armor shape element {0} in shape {1} defined in {2} {3} did not define a step parent element. Will not be visible.", val.Name, compArmorShape.Base, slot.Itemstack.Class, slot.Itemstack.Collectible.Code); continue; } if (elem.Children == null) { elem.Children = new ShapeElement[] { val }; } else { elem.Children = elem.Children.Append(val); } val.SetJointIdRecursive(elem.JointId); val.WalkRecursive((el) => { foreach (var face in el.Faces) { face.Value.Texture = "#" + stack.Collectible.Code + "-" + face.Value.Texture.TrimStart('#'); } }); added = true; } if (added && armorShape.Textures != null) { Dictionary <string, AssetLocation> newdict = new Dictionary <string, AssetLocation>(); foreach (var val in armorShape.Textures) { newdict[stack.Collectible.Code + "-" + val.Key] = val.Value; } // Item overrides var collDict = stack.Class == EnumItemClass.Block ? stack.Block.Textures : stack.Item.Textures; foreach (var val in collDict) { newdict[stack.Collectible.Code + "-" + val.Key] = val.Value.Base; } armorShape.Textures = newdict; foreach (var val in armorShape.Textures) { CompositeTexture ctex = new CompositeTexture() { Base = val.Value }; entityShape.TextureSizes[val.Key] = new int[] { armorShape.TextureWidth, armorShape.TextureHeight }; AssetLocation armorTexLoc = val.Value; // Weird backreference to the shaperenderer. Should be refactored. var texturesByLoc = extraTextureByLocation; var texturesByName = extraTexturesByTextureName; BakedCompositeTexture bakedCtex; ICoreClientAPI capi = Api as ICoreClientAPI; if (!texturesByLoc.TryGetValue(armorTexLoc, out bakedCtex)) { int textureSubId = 0; TextureAtlasPosition texpos; IAsset texAsset = Api.Assets.TryGet(val.Value.Clone().WithPathPrefixOnce("textures/").WithPathAppendixOnce(".png")); if (texAsset != null) { BitmapRef bmp = texAsset.ToBitmap(capi); capi.EntityTextureAtlas.InsertTextureCached(val.Value, bmp, out textureSubId, out texpos); } else { capi.World.Logger.Warning("Entity armor shape {0} defined texture {1}, no such texture found.", shapePath, val.Value); } ctex.Baked = new BakedCompositeTexture() { BakedName = val.Value, TextureSubId = textureSubId }; texturesByName[val.Key] = ctex; texturesByLoc[armorTexLoc] = ctex.Baked; } else { ctex.Baked = bakedCtex; texturesByName[val.Key] = ctex; } } foreach (var val in armorShape.TextureSizes) { entityShape.TextureSizes[val.Key] = val.Value; } } return(entityShape); }
public virtual void TesselateShape() { CompositeShape compositeShape = OverrideCompositeShape != null ? OverrideCompositeShape : entity.Properties.Client.Shape; Shape entityShape = OverrideEntityShape != null ? OverrideEntityShape : entity.Properties.Client.LoadedShape; if (entityShape == null) { return; } if (eagent != null) { entityShape = addGearToShape(entityShape, compositeShape.Base.ToString()); } defaultTexSource = GetTextureSource(); MeshData meshdata; if (entity.Properties.Client.Shape.VoxelizeTexture) { int altTexNumber = entity.WatchedAttributes.GetInt("textureIndex", 0); TextureAtlasPosition pos = defaultTexSource["all"]; CompositeTexture[] Alternates = entity.Properties.Client.FirstTexture.Alternates; CompositeTexture tex = altTexNumber == 0 ? entity.Properties.Client.FirstTexture : Alternates[altTexNumber % Alternates.Length]; meshdata = capi.Tesselator.VoxelizeTexture(tex, capi.EntityTextureAtlas.Size, pos); for (int i = 0; i < meshdata.xyz.Length; i += 3) { meshdata.xyz[i] -= 0.125f; meshdata.xyz[i + 1] -= 0.5f; meshdata.xyz[i + 2] += 0.125f / 2; } } else { try { capi.Tesselator.TesselateShapeWithJointIds("entity", entityShape, out meshdata, this, new Vec3f(), compositeShape.QuantityElements, compositeShape.SelectiveElements); meshdata.Translate(compositeShape.offsetX, compositeShape.offsetY, compositeShape.offsetZ); } catch (Exception e) { capi.World.Logger.Fatal("Failed tesselating entity {0} with id {1}. Entity will probably be invisible!. The teselator threw {2}", entity.Code, entity.EntityId, e); return; } } meshdata.Rgba2 = null; if (meshRefOpaque != null) { capi.Render.DeleteMesh(meshRefOpaque); meshRefOpaque = null; } if (meshRefOit != null) { capi.Render.DeleteMesh(meshRefOit); meshRefOit = null; } MeshData opaqueMesh = meshdata.Clone().Clear(); MeshData oitMesh = meshdata.Clone().Clear(); opaqueMesh.AddMeshData(meshdata, EnumChunkRenderPass.Opaque); oitMesh.AddMeshData(meshdata, EnumChunkRenderPass.Transparent); if (opaqueMesh.VerticesCount > 0) { meshRefOpaque = capi.Render.UploadMesh(opaqueMesh); } if (oitMesh.VerticesCount > 0) { meshRefOit = capi.Render.UploadMesh(oitMesh); } }