public SpriteFont(string name, byte[] data, int size, float scale, SheetBuilder builder) { if (builder.Type != SheetT.BGRA) { throw new ArgumentException("The sheet builder must create BGRA sheets.", "builder"); } deviceScale = scale; this.size = size; this.builder = builder; face = new Face(Library, data, 0); face.SetPixelSizes((uint)(size * deviceScale), (uint)(size * deviceScale)); glyphs = new Cache <Pair <char, Color>, GlyphInfo>(CreateGlyph); //PERF: Cache these delegates for Measure calls Func <char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance; lineWidth = line => line.Sum(characterWidth) / deviceScale; if (size <= 24) { PrecacheColor(Color.White, name); } }
public void EndFrame() { if (!isInFrame) { throw new InvalidOperationException("BeginFrame has not been called,There is no frame to end."); } isInFrame = false; sheetBuilder = null; if (doRender.Count == 0) { return; } Sheet currentSheet = null; IFrameBuffer fbo = null; foreach (var v in doRender) { //Change sheet if (v.First != currentSheet) { if (fbo != null) { DisableFrameBuffer(fbo); } currentSheet = v.First; fbo = EnableFrameBuffer(currentSheet); } v.Second(); } if (fbo != null) { DisableFrameBuffer(fbo); } doRender.Clear(); }
/// <summary> /// /// </summary> /// <param name="wr"></param> /// <param name="models"></param> /// <param name="camera"></param> /// <param name="scale">缩放</param> /// <param name="groundNormal"></param> /// <param name="lightSource"></param> /// <param name="lightAmbientColor"></param> /// <param name="lightDiffuseColor"></param> /// <param name="color"></param> /// <param name="normals"></param> /// <param name="shadowPalette"></param> /// <returns></returns> public ModelRenderProxy RenderAsync(WorldRenderer wr, IEnumerable <ModelAnimation> models, WRot camera, float scale, float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor, PaletteReference color, PaletteReference normals, PaletteReference shadowPalette) { if (!isInFrame) { throw new InvalidOperationException("BeginFrame has not been called.You cannot render until a frame has been started."); } //Correct for inverted y-axis var scaleTransform = Util.ScaleMatrix(scale, scale, scale); //Correct for bogus light source definition var lightYaw = Util.MakeFloatMatrix(new WRot(WAngle.Zero, WAngle.Zero, -lightSource.Yaw).AsMatrix()); var lightPitch = Util.MakeFloatMatrix(new WRot(WAngle.Zero, -lightSource.Pitch, WAngle.Zero).AsMatrix()); var shadowTransform = Util.MatrixMultiply(lightPitch, lightYaw); var invShadowTransform = Util.MatrixInverse(shadowTransform); var cameraTransform = Util.MakeFloatMatrix(camera.AsMatrix()); var invCameraTransform = Util.MatrixInverse(cameraTransform); if (invCameraTransform == null) { throw new InvalidOperationException("Failed to invert the cameraTransform matrix during RenderAsync"); } //Sprite rectangle var tl = new Vector2(float.MaxValue, float.MaxValue); var br = new Vector2(float.MinValue, float.MinValue); //Shadow sprite rectangle var stl = new Vector2(float.MaxValue, float.MaxValue); var sbr = new Vector2(float.MinValue, float.MinValue); foreach (var m in models) { // Convert screen offset back to world coords var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc())); var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]); var worldTransform = m.RotationFunc().Aggregate(Util.IdentityMatrix(), (x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x)); worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform); worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform); var bounds = m.Model.Bounds(m.FrameFunc()); var worldBounds = Util.MatrixAABBMultiply(worldTransform, bounds); var screenBounds = Util.MatrixAABBMultiply(cameraTransform, worldBounds); var shadowBounds = Util.MatrixAABBMultiply(shadowTransform, worldBounds); //Aggregate bounds rects tl = Vector2.Min(tl, new Vector2(screenBounds[0], screenBounds[1])); br = Vector2.Max(br, new Vector2(screenBounds[3], screenBounds[4])); stl = Vector2.Min(stl, new Vector2(shadowBounds[0], shadowBounds[1])); sbr = Vector2.Max(sbr, new Vector2(shadowBounds[3], shadowBounds[4])); } //Inflate rects to ensure redering is within bounds tl -= SpritePadding; br += SpritePadding; stl -= SpritePadding; sbr += SpritePadding; //Corners of the shadow quad,in shadow-space var corners = new float[][] { new[] { stl.X, stl.Y, 0, 1 }, new[] { sbr.X, sbr.Y, 0, 1 }, new[] { sbr.X, stl.Y, 0, 1 }, new[] { stl.X, sbr.Y, 0, 1 } }; var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform); var shadowGroundNormal = Util.MatrixVectorMultiply(shadowTransform, groundNormal); var screenCorners = new Vector3[4]; for (var j = 0; j < 4; j++) { //Project to ground plane. corners[j][2] = -(corners[j][1] * shadowGroundNormal[1] / shadowGroundNormal[2] + corners[j][0] * shadowGroundNormal[0] / shadowGroundNormal[2]); //Rotate to camera-space corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]); screenCorners[j] = new Vector3(corners[j][0], corners[j][1], 0); } //Shadows are rendered at twice the resolution to reduce artifacts. //阴影以两倍的分辨率渲染以减少伪像 Size spriteSize, shadowSpriteSize; Int2 spriteOffset, shadowSpriteOffset; CalculateSpriteGeometry(tl, br, 1, out spriteSize, out spriteOffset); CalculateSpriteGeometry(stl, sbr, 2, out shadowSpriteSize, out shadowSpriteOffset); if (sheetBuilder == null) { sheetBuilder = new SheetBuilder(SheetT.BGRA, AllocateSheet); } var sprite = sheetBuilder.Allocate(spriteSize, 0, spriteOffset); var shadowSprite = sheetBuilder.Allocate(shadowSpriteSize, 0, shadowSpriteOffset); var sb = sprite.Bounds; var ssb = shadowSprite.Bounds; var spriteCenter = new Vector2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2); var shadowCenter = new Vector2(ssb.Left + ssb.Width / 2, ssb.Top + ssb.Height / 2); var translateMtx = Util.TranslationMatrix(spriteCenter.X - spriteOffset.X, renderer.SheetSize - (spriteCenter.Y - spriteOffset.Y), 0); var shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, renderer.SheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0); var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx); var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx); doRender.Add(Pair.New <Sheet, Action>(sprite.Sheet, () => { foreach (var m in models) { //Convert screen offset to world offset var offsetVec = Util.MatrixVectorMultiply(invCameraTransform, wr.ScreenVector(m.OffsetFunc())); var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]); var rotations = m.RotationFunc().Aggregate(Util.IdentityMatrix(), (x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x)); var worldTransform = Util.MatrixMultiply(scaleTransform, rotations); worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform); var transform = Util.MatrixMultiply(cameraTransform, worldTransform); transform = Util.MatrixMultiply(correctionTransform, transform); var shadow = Util.MatrixMultiply(shadowTransform, worldTransform); shadow = Util.MatrixMultiply(shadowCorrectionTransform, shadow); var lightTransform = Util.MatrixMultiply(Util.MatrixInverse(rotations), invShadowTransform); var frame = m.FrameFunc(); for (uint i = 0; i < m.Model.Sections; i++) { var rd = m.Model.RenderData(i); var t = m.Model.TransformationMatrix(i, frame); var it = Util.MatrixInverse(t); if (it == null) { throw new InvalidOperationException("Failed to invert the transformed matrix of frame {0} during RenderAsync.".F(i)); } // Transform light vector from shadow -> world -> limb coords var lightDirection = ExtractRotationVector(Util.MatrixMultiply(it, lightTransform)); Render(rd, wr.World.ModelCache, Util.MatrixMultiply(transform, t), lightDirection, lightAmbientColor, lightDiffuseColor, color.TextureMidIndex, normals.TextureMidIndex); //Disable shadow normals by forcing zero diffuse and identity ambient light if (m.ShowShadow) { Render(rd, wr.World.ModelCache, Util.MatrixMultiply(shadow, t), lightDirection, ShadowAmbient, ShadowDiffuse, shadowPalette.TextureMidIndex, normals.TextureMidIndex); } } } })); var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, ZVector); screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector); return(new ModelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2] / screenLightVector[1])); }
public Theater(TileSet tileset) { this.tileset = tileset; var allocated = false; Func <Sheet> allocate = () => { if (allocated) { throw new SheetOverflowException("Terrain sheet overflow.Try increasing the tileset SheetSize parameter."); } allocated = true; return(new Sheet(SheetT.Indexed, new Size(tileset.SheetSize, tileset.SheetSize))); }; sheetBuilder = new SheetBuilder(SheetT.Indexed, allocate); random = new MersenneTwister(); var frameCache = new FrameCache(WarGame.ModData.DefaultFileSystem, WarGame.ModData.SpriteLoaders); foreach (var t in tileset.Templates) { var variants = new List <Sprite[]>(); foreach (var i in t.Value.Images) { var allFrames = frameCache[i]; var frameCount = tileset.EnableDepth ? allFrames.Length / 2 : allFrames.Length; var indices = t.Value.Frames != null ? t.Value.Frames : Enumerable.Range(0, frameCount); variants.Add(indices.Select(j => { var f = allFrames[j]; var tile = t.Value.Contains(j)?t.Value[j]:null; //The internal z axis is inverted from expectation (negative is closer) var zOffset = tile != null ? -tile.ZOffset : 0; var zRamp = tile != null ? tile.ZRamp : 1f; var offset = new Vector3(f.Offset, zOffset); var s = sheetBuilder.Allocate(f.Size, zRamp, offset); Util.FastCopyIntoChannel(s, f.Data); if (tileset.EnableDepth) { var ss = sheetBuilder.Allocate(f.Size, zRamp, offset); Util.FastCopyIntoChannel(ss, allFrames[j + frameCount].Data); //s and ss are guaranteed to use the same sheet because of the custom terrain sheet allocation. s = new SpriteWithSecondaryData(s, ss.Bounds, ss.Channel); } return(s); }).ToArray()); } var allSprites = variants.SelectMany(s => s); if (tileset.IgnoreTileSpriteOffsets) { allSprites = allSprites.Select(s => new Sprite(s.Sheet, s.Bounds, s.ZRamp, new Vector3(Vector2.Zero, s.Offset.Z), s.Channel, s.BlendMode)); } templates.Add(t.Value.Id, new TheaterTemplate(allSprites.ToArray(), variants.First().Count(), t.Value.Images.Length)); } missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1)); Sheet.ReleaseBuffer(); }
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, SheetBuilder sheetBuilder) { this.SheetBuilder = sheetBuilder; this.loaders = loaders; this.fileSystem = fileSystem; }
/// <summary> /// /// </summary> /// <param name="fileSystem"></param> /// <param name="filename"></param> /// <param name="loaders"></param> /// <param name="sheetBuilder"></param> /// <returns></returns> public static Sprite[] GetSprites(IReadOnlyFileSystem fileSystem, string filename, ISpriteLoader[] loaders, SheetBuilder sheetBuilder) { return(GetFrames(fileSystem, filename, loaders).Select(a => sheetBuilder.Add(a)).ToArray()); }