public MeshTable(int area, Vector3[] list) { if (area < 2) area = 2; AREA_LIMIT = area; _Points = new List<Vector3>(list); Vector3 first = _Points[0]; float minx = first.x, maxx = first.x, minz = first.z, maxz = first.z; foreach(Vector3 p in _Points) { if (minx > p.x) minx = p.x; if (maxx < p.x) maxx = p.x; if (minz > p.z) minz = p.z; if (maxz < p.z) maxz = p.z; } Debug.Log("[MeshTable] {" + minx + ">" + maxx + "," + minz + ">" + maxz + "}"); float xstep = (maxx - minx) / AREA_LIMIT; float zstep = (maxz - minz) / AREA_LIMIT; _Blocks = new MeshBlock[AREA_LIMIT * AREA_LIMIT]; for(int xi = 0; xi < AREA_LIMIT; xi++) { float xstart = minx + xstep * xi; float xend = xstart + xstep; for(int zi = 0; zi < AREA_LIMIT; zi++) { float zstart = minz + zstep * zi; float zend = zstart + zstep; int index = xi * AREA_LIMIT + zi; _Blocks[index] = new MeshBlock(this, xstart, xend, zstart, zend); } } for(int i = 0; i < _Points.Count; i++) { Vector3 point = _Points[i]; int xi = (int)((point.x - minx) / xstep); int zi = (int)((point.z - minz) / zstep); if (xi < 0) xi = 0; if (xi >= AREA_LIMIT) xi = AREA_LIMIT - 1; if (zi < 0) zi = 0; if (zi >= AREA_LIMIT) zi = AREA_LIMIT - 1; int index = xi * AREA_LIMIT + zi; _Blocks[index].AddIndex(i); } #if UNITY_EDITOR string msg = ""; for (int b = 0; b < _Blocks.Length; b++) { msg += " " + _Blocks[b].Count; if ((b + 1) % AREA_LIMIT == 0) msg += "\n"; else msg += ","; } Debug.Log("[MeshTable]\n" + msg); #endif if (xstep > zstep) _Around = xstep / 10.0f; else _Around = zstep / 10.0f; }
/// <summary>Draws the given Emoji character.</summary> public void DrawEmoji(Glyph character, ref float left, Renderman renderer) { if (!character.Image.Loaded) { return; } BoxRegion screenRegion = renderer.CurrentRegion; float top = renderer.TopOffset; // It's an image (e.g. Emoji). AtlasLocation locatedAt = RequireImage(character.Image); if (locatedAt == null) { // It needs to be isolated. Big emoji image! return; } if (CharacterProviders.FixHeight) { // Set the region: screenRegion.Set(left, top, locatedAt.Width, locatedAt.Height); } else { screenRegion.Set(left, top, FontSize, FontSize); } if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // Ensure correct batch: renderer.SetupBatch(this, locatedAt.Atlas, null); // If the two overlap, this means it's actually visible. MeshBlock block = Add(renderer); // Set it's colour: block.SetColour(renderer.ColorOverlay); // And clip our meshblock to fit within boundary: block.TextUV = null; block.ImageUV = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.ImageUV); block.Done(renderer.Transform); } left += (character.AdvanceWidth) + LetterSpacing; if (character.Charcode == (int)' ') { left += WordSpacing; } }
public override void Paint() { MeshBlock block = FirstBlock; while (block != null) { block.SetColour(FontColour); block.Paint(); block = block.LocalBlockAfter; } }
/// <summary>Sets up this renderman.</summary> private void Init() { Block = new MeshBlock(this); ClippingBoundary = new BoxRegion(0, 0, UnityEngine.Screen.width, UnityEngine.Screen.height); if (RootDocument == null) { HtmlDocument hdoc = new HtmlDocument(this); hdoc.SetRawLocation(new Location("resources://", null)); RootDocument = hdoc; } }
public override void Paint(LayoutBox box, Renderman renderer) { MeshBlock block = GetFirstBlock(renderer); if (block == null) { // This can happen if an animation is requesting that a now offscreen element gets painted only. return; } block.PaintColour(BaseColour * renderer.ColorOverlay); }
public override void Paint() { // Any meshes in this elements queue should now change colour: Color colour = Element.Style.Computed.ColorOverlay; MeshBlock block = FirstBlock; while (block != null) { block.SetColour(colour); block.Paint(); block = block.LocalBlockAfter; } }
/// <summary>Applies any transforms (rotate,scale etc) now. Note that tranforms are post-processes /// so they are very fast and mostly done by paint events.</summary> public void ApplyTransform() { MeshBlock block = FirstBlock; while (block != null) { if (block.Transform != null) { block.Layout(); } block = block.LocalBlockAfter; } }
/// <summary>Transforms all the verts by the given delta matrix. Used during a Paint only.</summary> public virtual void ApplyTransform(Matrix4x4 delta, Renderman renderer) { // Get the first block: MeshBlock block = GetFirstBlock(renderer); for (int i = 0; i < BlockCount; i++) { // Transform the verts: block.TransformVertices(delta); // Seek to the next one: block.Next(); } }
public MeshBlock Add(Renderman renderer) { // Allocate the block: MeshBlock block = renderer.CurrentBatch.Mesh.Allocate(renderer); if (Batch == null) { Batch = renderer.CurrentBatch; FirstBlockIndex = block.Buffer.BlocksBefore + block.BlockIndex; } BlockCount++; return(block); }
MeshBlock GetOrCreateBlock(Vector3 blockCoords) { if (Blocks.Keys.Contains(blockCoords)) { var block = Blocks[blockCoords]; return(block); } else { var newBlock = new MeshBlock(); newBlock.Coordinates = blockCoords; Blocks.Add(blockCoords, newBlock); return(newBlock); } }
public override void Paint(LayoutBox box, Renderman renderer) { Color colour = Colour * renderer.ColorOverlay; MeshBlock block = GetFirstBlock(renderer); // For each block.. for (int i = 0; i < BlockCount; i++) { // Paint the colour: block.PaintColour(colour); // Go to next block: block.Next(); } }
public override void Paint() { // Get the computed style: ComputedStyle computed = Element.Style.Computed; // Any meshes in my queue should now change colour: MeshBlock block = FirstBlock; if (Colour == null || Colour.Length == 1) { // Most common case. This is a single colour border. // Get the default colour - that's the same as the text colour: Color colour = Color.black; // Does this border have a colour? if (Colour == null) { // Grab the text colour if there is one: if (computed.Text != null) { // It's the same as the font colour: colour = computed.Text.FontColour; } else { // Nope - We need to set alpha: colour.a = computed.ColorOverlay.a; } } else { colour = Colour[0]; } // For each block, set the colour: while (block != null) { block.SetColour(colour); block.Paint(); block = block.LocalBlockAfter; } return; } }
/// <summary>Gets the first rendered block of this property. Used during Paint passes.</summary> public MeshBlock GetFirstBlock(Renderman renderer) { if (Batch == null) { // Layout required. return(null); } // Use the shared block: MeshBlock block = renderer.Block; // Load into it now: block.SetBatchIndex(Batch, FirstBlockIndex); // Return it: return(block); }
public override void Paint(LayoutBox box, Renderman renderer) { // Any meshes in this elements queue should now change colour: Color colour = renderer.ColorOverlay; MeshBlock block = GetFirstBlock(renderer); // For each block.. for (int i = 0; i < BlockCount; i++) { // Paint the colour: block.PaintColour(colour); // Go to next block: block.Next(); } }
public override void Paint(LayoutBox box, Renderman renderer) { // Any meshes in my queue should now change colour: MeshBlock block = GetFirstBlock(renderer); if (BaseColour == null || BaseColour.Count == 1) { // Most common case. This is a single colour border. // Get the default colour - that's the same as the text colour: Color colour = Color.black; // Does this border have a colour? if (BaseColour == null) { // Grab the text colour if there is one: if (RenderData.Text != null) { // It's the same as the font colour: colour = RenderData.Text.BaseColour * renderer.ColorOverlay; } else { // Nope - We need to set alpha: colour.a = renderer.ColorOverlay.a; } } else { colour = BaseColour[0].GetColour(RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } // For each block.. for (int i = 0; i < BlockCount; i++) { // Paint the colour: block.PaintColour(colour); // Go to next block: block.Next(); } return; } }
internal override void Layout(LayoutBox box, Renderman renderer) { // Get the top left inner corner (inside margin and border): float width = box.PaddedWidth; float height = box.PaddedHeight; float top = box.Y + box.Border.Top; float left = box.X + box.Border.Left; // Is it clipped? if (renderer.IsInvisible(left, top, width, height)) { // Totally not visible. return; } // Ensure we have a batch (doesn't change graphics or font thus both nulls): renderer.SetupBatch(this, null, null); // Get the transform: Transformation transform = renderer.Transform; for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { // Add it: MeshBlock block = Add(renderer); // Set the UV to that of the solid block colour pixel: block.SetSolidColourUV(); // Set the colour - here we set it unevenly to trigger gradients. // block.SetColour(BackingColour * renderer.ColorOverlay); // BoxRegion clipZone=new BoxRegion(left,top,width,height); // Set the verts too: // block.SetClipped(renderer.ClippingBoundary,clipZone,renderer,box.ZIndex-0.006f); block.Done(transform); } } }
public override void Paint(LayoutBox box, Renderman renderer) { // Does the given renderer belong to the worldUI? if (Renderer != null && renderer == Renderer.Renderer) { // Yes! We're actually drawing the element. Do nothing. return; } MeshBlock block = GetFirstBlock(renderer); if (block == null) { // This can happen if an animation is requesting that a now offscreen element gets painted only. return; } block.PaintColour(renderer.ColorOverlay); }
/// <summary>Draws an underline (or a strikethrough).</summary> public virtual void DrawUnderline(Renderman renderer) { // Ensure we have a batch: renderer.SetupBatch(this, null, null); // And get our block ready: MeshBlock block = Add(renderer); // Set the UV to that of the solid block colour pixel: block.SetSolidColourUV(); // Set the colour: block.SetColour(renderer.FontColour); // Set the verts: block.SetClipped(renderer.ClippingBoundary, renderer.CurrentRegion, renderer, renderer.TextDepth); // Ok! block.Done(renderer.Transform); }
public override void Paint(LayoutBox box, Renderman renderer) { // Resolve colour: BaseColour = RenderData.computedStyle.Resolve(Css.Properties.ColorProperty.GlobalProperty) .GetColour(RenderData, Css.Properties.ColorProperty.GlobalProperty); Color colour = BaseColour * renderer.ColorOverlay; MeshBlock block = GetFirstBlock(renderer); // For each block.. for (int i = 0; i < BlockCount; i++) { // Paint the colour: block.PaintColour(colour); // Go to next block: block.Next(); } }
/// <summary>Draws a character with x-inverted UV's. Used for rendering e.g. "1 < 2" in right-to-left.</summary> protected virtual void DrawInvertCharacter(ref float left, Renderman renderer) { BoxRegion screenRegion = renderer.CurrentRegion; float top = renderer.TopOffset; int index = renderer.CharacterIndex; Glyph character = Text.Characters[index]; if (character == null) { return; } if (Text.Kerning != null) { left += Text.Kerning[index] * Text.FontSize; } // Get atlas location (if it has one): AtlasLocation locatedAt = character.Location; if (locatedAt != null) { // We're on the atlas! float y = top + renderer.TextAscender - ((character.Height + character.MinY) * Text.FontSize); float scaleFactor = renderer.TextScaleFactor; screenRegion.Set(left + (character.LeftSideBearing * Text.FontSize), y, locatedAt.Width * scaleFactor, locatedAt.Height * scaleFactor); if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // True if this character is visible. // Ensure correct batch: renderer.SetupBatch(this, null, locatedAt.Atlas); MeshBlock block = Add(renderer); block.SetColour(renderer.FontColour); block.ApplyOutline(); // And clip our meshblock to fit within boundary: block.ImageUV = null; UVBlock uvs = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.TextUV); if (uvs.Shared) { uvs = new UVBlock(uvs); } // Invert along X: float temp = uvs.MinX; uvs.MinX = uvs.MaxX; uvs.MaxX = temp; // Assign to the block: block.TextUV = uvs; block.Done(renderer.Transform); } } left += (character.AdvanceWidth * Text.FontSize) + Text.LetterSpacing; if (character.Charcode == (int)' ') { left += Text.WordSpacing; } }
/// <summary>Draws a character with x-inverted UV's. Used for rendering e.g. "1 < 2" in right-to-left.</summary> protected virtual void DrawInvertCharacter(ref float left, Renderman renderer) { BoxRegion screenRegion = renderer.CurrentRegion; float top = renderer.TopOffset; int index = renderer.CharacterIndex; Glyph character = Characters[index]; if (character == null) { return; } if (Kerning != null) { left += Kerning[index] * FontSize; } // Get atlas location (if it has one): AtlasLocation locatedAt = character.Location; if (locatedAt != null) { // We're on the atlas! float y = top + renderer.TextAscender - ((character.Height + character.MinY) * FontSize); float scaleFactor = renderer.TextScaleFactor; screenRegion.Set(left + (character.LeftSideBearing * FontSize), y, locatedAt.Width * scaleFactor, locatedAt.Height * scaleFactor); if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // True if this character is visible. // Ensure correct batch: renderer.SetupBatch(this, null, locatedAt.Atlas); MeshBlock block = Add(renderer); block.SetColour(renderer.FontColour); block.ApplyOutline(); // And clip our meshblock to fit within boundary: // Clip our meshblock to fit within boundary: if (Background != null && Isolated) { // Setup the batch material for this char: Material imageMaterial = Background.Image.Contents.GetImageMaterial(renderer.CurrentShaderSet.Normal); SetBatchMaterial(renderer, imageMaterial); // Reapply text atlas: renderer.CurrentBatch.SetFontAtlas(locatedAt.Atlas); // Apply the image UV's (we're always isolated so these can tile by going out of range): block.ImageUV = block.SetClipped( renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, Background.ImageLocation, block.ImageUV ); } else { block.ImageUV = null; } UVBlock uvs = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.TextUV); if (uvs.Shared) { uvs = new UVBlock(uvs); } // Invert along X: float temp = uvs.MinX; uvs.MinX = uvs.MaxX; uvs.MaxX = temp; // Assign to the block: block.TextUV = uvs; block.Done(renderer.Transform); } } left += (character.AdvanceWidth * FontSize) + LetterSpacing; if (character.Charcode == (int)' ') { left += WordSpacing; } }
static void ExportFile(string fileName, string destFolder) { String targetMeshFile = Path.GetFileNameWithoutExtension(fileName) + ".mesh"; String targetAnimFile = Path.GetFileNameWithoutExtension(fileName) + ".anim"; Console.WriteLine("Exporting " + targetMeshFile + "..."); AssimpContext importer = new AssimpContext(); importer.SetConfig(new NormalSmoothingAngleConfig(66.0f)); importer.SetConfig(new VertexBoneWeightLimitConfig(4)); Scene scene = importer.ImportFile(fileName, PostProcessPreset.TargetRealTimeMaximumQuality | PostProcessSteps.FlipUVs); System.Globalization.CultureInfo customCulture = (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone(); customCulture.NumberFormat.NumberDecimalSeparator = "."; System.Threading.Thread.CurrentThread.CurrentCulture = customCulture; FileStream fs = new FileStream(Path.Combine(destFolder, targetMeshFile), FileMode.Create); BinaryWriter dest = new BinaryWriter(fs); MeshBlock meshBlock = new MeshBlock(dest, "MX3D"); List<MeshNode> nodes = new List<MeshNode>(); addNodes(nodes, scene.RootNode, scene.Meshes[0]); for (int j = 0; j < 1; j++) { MeshBlock groupBlock = new MeshBlock(dest, "XGRP"); exportGroup(dest, scene.Meshes[j], scene, nodes); groupBlock.EndBlock(dest); } MeshBlock skeletonDataBlock; skeletonDataBlock = new MeshBlock(dest, "XSKL"); UInt32 boneCount = (UInt32)nodes.Count; dest.Write(boneCount); for (int j = 0; j < nodes.Count; j++) { exportBone(dest, nodes[j], scene, j); } skeletonDataBlock.EndBlock(dest); meshBlock.EndBlock(dest); dest.Close(); fs.Close(); for (int i = 0; i < scene.AnimationCount; i++) { Animation animation = scene.Animations[i]; String name = animation.Name; if (name.Length <= 0) { name = Path.GetFileNameWithoutExtension(targetAnimFile); int p = name.LastIndexOf("@"); if (p >= 0) { name = name.Substring(p + 1); } } Console.WriteLine("Exporting " + name + " animation..."); fs = new FileStream(Path.Combine(destFolder, targetAnimFile), FileMode.Create); dest = new BinaryWriter(fs); byte[] tagData = Encoding.ASCII.GetBytes("ANIM"); dest.Write(tagData); float FPS = (float)animation.TicksPerSecond; dest.Write(FPS); byte loop = 1; dest.Write(loop); float loopPoint = 0.0f; dest.Write(loopPoint); float animSpeed = 1.0f; dest.Write(animSpeed); String nextAnim = ""; exportString(dest, nextAnim); UInt32 channelCount = (UInt32)animation.NodeAnimationChannelCount; dest.Write(channelCount); for (int j = 0; j < channelCount; j++) { NodeAnimationChannel channel = animation.NodeAnimationChannels[j]; exportString(dest, channel.NodeName); UInt32 posCount = (UInt32)channel.PositionKeyCount; dest.Write(posCount); for (int k = 0; k < posCount; k++) { float t = (float)channel.PositionKeys[k].Time; float x = channel.PositionKeys[k].Value.X; float y = channel.PositionKeys[k].Value.Y; float z = channel.PositionKeys[k].Value.Z; float w = 1.0f; dest.Write(t); dest.Write(x); dest.Write(y); dest.Write(z); dest.Write(w); } UInt32 rotCount = (UInt32)channel.RotationKeyCount; dest.Write(rotCount); for (int k = 0; k < rotCount; k++) { float t = (float)channel.RotationKeys[k].Time; float x = channel.RotationKeys[k].Value.X; float y = channel.RotationKeys[k].Value.Y; float z = channel.RotationKeys[k].Value.Z; float w = channel.RotationKeys[k].Value.W; dest.Write(t); dest.Write(x); dest.Write(y); dest.Write(z); dest.Write(w); } UInt32 scaleCount = (UInt32)channel.ScalingKeyCount; dest.Write(scaleCount); for (int k = 0; k < scaleCount; k++) { float t = (float)channel.ScalingKeys[k].Time; float x = channel.ScalingKeys[k].Value.X; float y = channel.ScalingKeys[k].Value.Y; float z = channel.ScalingKeys[k].Value.Z; float w = 1.0f; dest.Write(t); dest.Write(x); dest.Write(y); dest.Write(z); dest.Write(w); } } dest.Close(); fs.Close(); } }
internal override void Layout(LayoutBox box, Renderman renderer) { if (Image == null || !Image.Loaded) { return; } if (Clipping == BackgroundClipping.Text) { return; } if (Image.Contents.Isolate || renderer.RenderMode == RenderMode.NoAtlas || Filtering != FilterMode.Point || ForcedIsolate) { // SPA is an animation format, so we need a custom texture atlas to deal with it. // This is because the frames of any animation would quickly exhaust our global texture atlas. // So to get a custom atlas, we must isolate this property. Isolate(); } else { // Reverse isolation, if we are isolated already: Include(); } // Get the full shape of the element: float width; float height; float minX; float minY; if (renderer.ViewportBackground) { // Applying to whole background: BoxRegion viewport = renderer.Viewport; minY = (int)viewport.Y; minX = (int)viewport.X; width = (int)viewport.Width; height = (int)viewport.Height; renderer.ViewportBackground = false; } else { width = (int)(box.PaddedWidth); height = (int)(box.PaddedHeight); minY = (int)(box.Y + box.Border.Top); minX = (int)(box.X + box.Border.Left); } if (width == 0 || height == 0) { if (Visible) { SetVisibility(false); } return; } // Tell the image that the box has likely changed - this allows it to redraw (e.g. SVGs): float trueImageWidth; float trueImageHeight; Image.Contents.OnLayout(RenderData, box, out trueImageWidth, out trueImageHeight); BoxRegion boundary = new BoxRegion(minX, minY, width, height); if (!boundary.Overlaps(renderer.ClippingBoundary)) { if (Visible) { SetVisibility(false); } return; } else if (!Visible) { // ImageLocation will allocate here if it's needed. SetVisibility(true); } boundary.ClipBy(renderer.ClippingBoundary); // Texture time - get it's location on that atlas: AtlasLocation locatedAt = ImageLocation; if (locatedAt == null) { // We're not using the atlas here. if (!Isolated) { Isolate(); } locatedAt = new AtlasLocation(trueImageWidth, trueImageHeight); } // Isolation is all done - safe to setup the batch now: renderer.SetupBatch(this, locatedAt.Atlas, null); // Great - Use locatedAt.Width/locatedAt.Height - this removes any risk of overflowing into some other image. int imageCountX = 1; int imageCountY = 1; float imageWidth = trueImageWidth * RenderData.ValueScale; float imageHeight = trueImageHeight * RenderData.ValueScale; bool autoX = false; bool autoY = false; if (SizeX != null) { if (SizeX.IsAuto) { autoX = true; } else { imageWidth = SizeX.GetDecimal(RenderData, Css.Properties.BackgroundSize.GlobalPropertyX); } } if (SizeY != null) { if (SizeY.IsAuto) { autoY = true; } else { imageHeight = SizeY.GetDecimal(RenderData, Css.Properties.BackgroundSize.GlobalPropertyY); } } if (autoX) { imageWidth = imageHeight * trueImageWidth / trueImageHeight; } else if (autoY) { imageHeight = imageWidth * trueImageHeight / trueImageWidth; } // offsetX and offsetY are the images position offset from where it should be (e.g. x of -200 means it's 200px left) // Apply the offset origin: float offsetX = OffsetOriginX * (width - imageWidth); float offsetY = OffsetOriginY * (height - imageHeight); float offset; // Resolve the offset values, if there is any: if (OffsetX != null) { offset = OffsetX.GetDecimal(RenderData, ValueAxis.X); if (OffsetOriginX == 1f) { offsetX -= offset; } else { offsetX += offset; } } if (OffsetY != null) { offset = OffsetY.GetDecimal(RenderData, ValueAxis.Y); if (OffsetOriginY == 1f) { offsetY -= offset; } else { offsetY += offset; } } if (RepeatX) { // Get the rounded up number of images: imageCountX = (int)Math.Ceiling(width / imageWidth); if (offsetX != 0) { // If we have an offset, another image is introduced. imageCountX++; } } if (RepeatY) { // Get the rounded up number of images: imageCountY = (int)Math.Ceiling(height / imageHeight); if (offsetY != 0) { // If we have an offset, another image is introduced. imageCountY++; } } float blockX = minX + offsetX; float blockY = minY + offsetY; if (RepeatX && offsetX > 0) { // We're repeating and the image is offset by a +ve number. // This means a small gap, OffsetX px wide, is open on this left side. // So to fill it, we need to offset this first image by a much bigger number - the value imageWidth-OffsetX. blockX -= (imageWidth - offsetX); // This results in the first image having OffsetX pixels exposed in the box - this is what we want. } if (RepeatY && offsetY > 0) { // Similar thing to above: blockY -= (imageHeight - offsetY); } BoxRegion screenRegion = new BoxRegion(); bool first = true; float startX = blockX; Color colour = renderer.ColorOverlay; float zIndex = (RenderData.computedStyle.ZIndex - 0.003f); Transformation transform = renderer.Transform; for (int y = 0; y < imageCountY; y++) { for (int x = 0; x < imageCountX; x++) { // Draw at blockX/blockY. screenRegion.Set(blockX, blockY, imageWidth, imageHeight); if (screenRegion.Overlaps(boundary)) { // If the two overlap, this means it's actually visible. MeshBlock block = Add(renderer); if (first) { first = false; if (Isolated) { // Set current material: SetBatchMaterial(renderer, Image.Contents.GetImageMaterial(renderer.CurrentShaderSet.Isolated)); } } // Set its colour: block.SetColour(colour); // And clip our meshblock to fit within boundary: block.TextUV = null; block.ImageUV = block.SetClipped(boundary, screenRegion, renderer, zIndex, locatedAt, block.ImageUV); block.Done(transform); } blockX += imageWidth; } blockX = startX; blockY += imageHeight; } }
static void exportGroup(BinaryWriter dest, Assimp.Mesh mesh, Assimp.Scene scene, List<MeshNode> nodes) { UInt32 vertexFormat = VF(vertexPosition); if (mesh.HasBones) vertexFormat |= VF(vertexBone); if (mesh.HasNormals) vertexFormat |= VF(vertexNormal); if (mesh.HasTangentBasis) vertexFormat |= VF(vertexTangent) | VF(vertexBiTangent); for (int i = 0; i < mesh.TextureCoordinateChannelCount; i++ ) { vertexFormat |= VF(vertexUV0 + i); } dest.Write(vertexFormat); exportString(dest, mesh.Name); UInt16 vertexCount = (UInt16 ) mesh.VertexCount; UInt32 meshFlags = 0; dest.Write(meshFlags); dest.Write(vertexCount); MeshBlock vertexDataBlock; vertexDataBlock = new MeshBlock(dest, "VATX"); dest.Write(vertexPosition); dest.Write(typeVector3D); Matrix4x4 mat = scene.RootNode.Transform; for (int i = 0; i < mesh.VertexCount; i++) { Vector3D V = new Vector3D(); V.X = mesh.Vertices[i].X; V.Y = mesh.Vertices[i].Y; V.Z = mesh.Vertices[i].Z; dest.Write(V.X); dest.Write(V.Y); dest.Write(V.Z); } vertexDataBlock.EndBlock(dest); /*if (mesh.HasVertexColors(0)) { vertexDataBlock = new MeshBlock(w, "VATX"); dest.Write(vertexColor); dest.Write(typeColor); for (int i = 0; i < mesh.VertexCount; i++) { byte R = (byte)(mesh.VertexColorChannels[0][i].R * 255.0f); byte G = (byte)(mesh.VertexColorChannels[0][i].G * 255.0f); byte B = (byte)(mesh.VertexColorChannels[0][i].B * 255.0f); byte A = (byte)(mesh.VertexColorChannels[0][i].A * 255.0f); UInt32 color = (UInt32) (R + (G << 8) + (B << 16) + (A << 24)); dest.Write(color); } vertexDataBlock.EndBlock(w); }*/ if (mesh.HasBones) { vertexDataBlock = new MeshBlock(dest, "VATX"); dest.Write(vertexBone); dest.Write(typeFloat); for (int i = 0; i < mesh.VertexCount; i++) { int boneIndex = -1; for (int j = 0; j < nodes.Count; j++) { var bone = nodes[j].bone; if (bone == null) continue; if (bone.HasVertexWeights) { for (int k=0; k<bone.VertexWeightCount; k++) if (bone.VertexWeights[k].VertexID == i) { boneIndex = j; break; } } if (boneIndex >=0) { break; } } boneIndex++; float boneID = boneIndex; dest.Write(boneID); } vertexDataBlock.EndBlock(dest); } if (mesh.HasNormals) { vertexDataBlock = new MeshBlock(dest, "VATX"); dest.Write(vertexNormal); dest.Write(typeVector3D); for (int i = 0; i < mesh.VertexCount; i++) { dest.Write(mesh.Normals[i].X); dest.Write(mesh.Normals[i].Y); dest.Write(mesh.Normals[i].Z); } vertexDataBlock.EndBlock(dest); } if (mesh.HasTangentBasis) { vertexDataBlock = new MeshBlock(dest, "VATX"); dest.Write(vertexTangent); dest.Write(typeVector3D); for (int i = 0; i < mesh.VertexCount; i++) { dest.Write(mesh.Tangents[i].X); dest.Write(mesh.Tangents[i].Y); dest.Write(mesh.Tangents[i].Z); } vertexDataBlock.EndBlock(dest); vertexDataBlock = new MeshBlock(dest, "VATX"); dest.Write(vertexBiTangent); dest.Write(typeVector3D); for (int i = 0; i < mesh.VertexCount; i++) { dest.Write(mesh.BiTangents[i].X); dest.Write(mesh.BiTangents[i].Y); dest.Write(mesh.BiTangents[i].Z); } vertexDataBlock.EndBlock(dest); } for (int j = 0; j < mesh.TextureCoordinateChannelCount; j++) { vertexDataBlock = new MeshBlock(dest, "VATX"); byte uvSlot = (byte)(vertexUV0 + j); dest.Write(uvSlot); dest.Write(typeVector2D); for (int i = 0; i < mesh.VertexCount; i++) { dest.Write(mesh.TextureCoordinateChannels[j][i].X); dest.Write(mesh.TextureCoordinateChannels[j][i].Y); //dest.Write(mesh.TextureCoordinateChannels[j][i].Z); } vertexDataBlock.EndBlock(dest); } MeshBlock lodDataBlock; lodDataBlock = new MeshBlock(dest, "GLOD"); byte lodLevel = 0; UInt32 triangleCount = (UInt32) mesh.FaceCount; dest.Write(lodLevel); dest.Write(triangleCount); for (int i = 0; i < mesh.FaceCount; i++) { UInt16 A = (UInt16) mesh.Faces[i].Indices[0]; UInt16 B = (UInt16) mesh.Faces[i].Indices[1]; UInt16 C = (UInt16) mesh.Faces[i].Indices[2]; dest.Write(A); dest.Write(B); dest.Write(C); } lodDataBlock.EndBlock(dest); MeshBlock textureDataBlock; textureDataBlock = new MeshBlock(dest, "MDIF"); UInt32 diffuseColor = 0xFFFFFFFF; dest.Write(diffuseColor); exportString(dest, scene.Materials[mesh.MaterialIndex].TextureDiffuse.FilePath); textureDataBlock.EndBlock(dest); }
public void Render(float alpha, float cornerX, float cornerY) { // Grab the renderer: Renderman renderer = RoundCorners.Renderer; // Get the z-Index: float zIndex = renderer.Depth + 0.006f; // Figure out where half way is (divide by 2): int halfway = (BlocksRequired >> 1); Color colour; if (Border.Colour == null) { if (RoundCorners.Computed.Text != null) { // Same as the font colour: colour = RoundCorners.Computed.Text.FontColour; } else { // Get the default colour: colour = Color.black; // Alpha is required: colour.a = alpha; } } else if (Border.Colour.Length == 1) { // Get the only colour: colour = Border.Colour[0]; } else { // Get the first colour: colour = Border.Colour[FromIndex]; } // Grab the clipping boundary: BoxRegion clip = renderer.ClippingBoundary; // Make it relative to the corners location: float minClipX = clip.X - cornerX; float minClipY = clip.Y - cornerY; float maxClipX = clip.MaxX - cornerX; float maxClipY = clip.MaxY - cornerY; // For each block.. for (int i = 0; i < BlocksRequired; i++) { // Get a block: MeshBlock block = Border.Add(); // Read the outer arc: Vector2 outerPointA = OuterArc[i]; // Figure out the bounding box (constant for a particular block). float minX = outerPointA.x; float maxX = minX; float minY = outerPointA.y; float maxY = minY; Vector2 outerPointB = OuterArc[i + 1]; // Update the bounding box: if (outerPointB.x < minX) { minX = outerPointB.x; } else if (outerPointB.x > maxX) { maxX = outerPointB.x; } if (outerPointB.y < minY) { minY = outerPointB.y; } else if (outerPointB.y > maxY) { maxY = outerPointB.y; } // Line segment A->B on the "outer" arc. // Read the inner arc: Vector2 innerPointA = InnerArc[i]; // Update the bounding box: if (innerPointA.x < minX) { minX = innerPointA.x; } else if (innerPointA.x > maxX) { maxX = innerPointA.x; } if (innerPointA.y < minY) { minY = innerPointA.y; } else if (innerPointA.y > maxY) { maxY = innerPointA.y; } Vector2 innerPointB = InnerArc[i + 1]; // Update the bounding box: if (innerPointB.x < minX) { minX = innerPointB.x; } else if (innerPointB.x > maxX) { maxX = innerPointB.x; } if (innerPointB.y < minY) { minY = innerPointB.y; } else if (innerPointB.y > maxY) { maxY = innerPointB.y; } // How does our bounding box compare to the clipping region? if (maxX < minClipX) { continue; } else if (minX > maxClipX) { continue; } if (maxY < minClipY) { continue; } else if (minY > maxClipY) { continue; } // Line segment A->B on the "inner" arc. // Set the UV to that of the solid block colour pixel: block.SetSolidColourUV(); // Get the border colour: if (i == halfway) { // Get the next colour: if (Border.Colour != null && Border.Colour.Length != 1) { colour = Border.Colour[ToIndex]; } } // Set the border colour: block.SetColour(colour); // Apply the block region: block.VertexTopLeft = renderer.PixelToWorldUnit(cornerX + outerPointA.x, cornerY + outerPointA.y, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(cornerX + outerPointB.x, cornerY + outerPointB.y, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerX + innerPointA.x, cornerY + innerPointA.y, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(cornerX + innerPointB.x, cornerY + innerPointB.y, zIndex); } }
protected override void Layout() { if (Characters == null || FontToDraw == null || Characters.Length == 0) { return; } // The blocks we allocate here come from FontToDraw. // They use the same renderer and same layout service, but just a different mesh. // This is to enable potentially very large font atlases with multiple fonts. ComputedStyle computed = Element.Style.Computed; Renderman renderer = Element.Document.Renderer; float top = computed.OffsetTop + computed.StyleOffsetTop; float left = computed.OffsetLeft + computed.StyleOffsetLeft; // Should we auto-alias the text? // Note that this property "drags" to following elements which is correct. // We don't really want to break batching chains for aliasing. if (Alias == float.MaxValue) { // Yep! Note all values here are const. float aliasing = Fonts.AutoAliasOffset - ((FontSize - Fonts.AutoAliasRelative) * Fonts.AutoAliasRamp); if (aliasing > 0.1f) { renderer.FontAliasing = aliasing; } } else { // Write aliasing: renderer.FontAliasing = Alias; } if (Extrude != 0f) { // Compute the extrude now: if (Text3D == null) { Text3D = Get3D(FontSize, FontColour, ref left, ref top); } else { // Update it. } return; } else { Text3D = null; } if (!AllWhitespace) { // Firstly, make sure the batch is using the right font texture. // This may generate a new batch if the font doesn't match the previous or existing font. // Get the full shape of the element: int width = computed.PaddedWidth; int height = computed.PaddedHeight; int minY = computed.OffsetTop + computed.BorderTop; int minX = computed.OffsetLeft + computed.BorderLeft; BoxRegion boundary = new BoxRegion(minX, minY, width, height); if (!boundary.Overlaps(renderer.ClippingBoundary)) { if (Visible) { SetVisibility(false); } return; } else if (!Visible) { // ImageLocation will allocate here if it's needed. SetVisibility(true); } } float zIndex = computed.ZIndex; BoxRegion screenRegion = new BoxRegion(); // First up, underline. if (TextLine != null) { // We have one. Locate it next. float lineWeight = (FontToDraw.StrikeSize * FontSize); float yOffset = 0f; switch (TextLine.Type) { case TextLineType.Underline: yOffset = Ascender + lineWeight; break; case TextLineType.StrikeThrough: yOffset = (FontToDraw.StrikeOffset * FontSize); yOffset = Ascender - yOffset; break; case TextLineType.Overline: yOffset = (lineWeight * 2f); break; } Color lineColour = FontColour; if (TextLine.ColourOverride) { lineColour = TextLine.Colour; } screenRegion.Set(left, top + yOffset, computed.PixelWidth, lineWeight); if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // Ensure we have a batch: SetupBatch(null, null); // This region is visible. Clip it: screenRegion.ClipBy(renderer.ClippingBoundary); // And get our block ready: MeshBlock block = Add(); // Set the UV to that of the solid block colour pixel: block.SetSolidColourUV(); // Set the colour: block.SetColour(lineColour); block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex); } } // Next, render the characters. // If we're rendering from right to left, flip the punctuation over. // Is the word itself rightwards? bool rightwardWord = false; if (StartPunctuationCount < Characters.Length) { // Is the first actual character a rightwards one? Glyph firstChar = Characters[StartPunctuationCount]; if (firstChar != null) { rightwardWord = firstChar.Rightwards; } } // Right to left (e.g. arabic): if (computed.DrawDirection == DirectionType.RTL) { int end = Characters.Length - EndPunctuationCount; // Draw the punctuation from the end of the string first, backwards: if (EndPunctuationCount > 0) { for (int i = Characters.Length - 1; i >= end; i--) { DrawInvertCharacter(i, ref left, top, renderer, zIndex, screenRegion); } } if (rightwardWord) { // Render the word itself backwards. for (int i = end - 1; i >= StartPunctuationCount; i--) { DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion); } } else { // Draw the middle characters: for (int i = StartPunctuationCount; i < end; i++) { DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion); } } // Draw the punctuation from the start of the string last, backwards: if (StartPunctuationCount > 0) { for (int i = StartPunctuationCount - 1; i >= 0; i--) { DrawInvertCharacter(i, ref left, top, renderer, zIndex, screenRegion); } } } else if (rightwardWord) { // Render the word itself backwards. for (int i = Characters.Length - 1; i >= 0; i--) { DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion); } } else { // Draw it as is. for (int i = 0; i < Characters.Length; i++) { DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion); } } }
internal override void Layout(LayoutBox box, Renderman renderer) { // Dimensions: float width = box.Width; float height = box.Height; if (Renderer == null) { // Create the FWUI now: Renderer = new FlatWorldUI("#Internal-PowerUI-Raster-" + RasterID, (int)width, (int)height); RasterID++; if (Filter != null) { // Set source: Filter.Set("source0", Renderer.Texture); } // Grab the output texture: Output = Renderer.Texture; } // Does the given renderer belong to the worldUI? if (renderer == Renderer.Renderer) { // Yes! We're actually drawing the element. return; } // Next we'll draw the rastered image. // It's essentially just the output from the renderer. // Get the top left inner corner (inside margin and border): float top = box.Y; float left = box.X; // Update the FlatWorldUI next: UpdateRenderer(box, width, height); // Always isolated: Isolate(); // Make sure the renderer stalls and doesn't draw anything else of this element or its kids. renderer.StallStatus = 2; // Setup boundary: BoxRegion boundary = new BoxRegion(left, top, width, height); if (!boundary.Overlaps(renderer.ClippingBoundary)) { if (Visible) { SetVisibility(false); } return; } else if (!Visible) { // ImageLocation will allocate here if it's needed. SetVisibility(true); } // Texture time - get its location on that atlas: if (LocatedAt == null) { LocatedAt = new AtlasLocation(width, height); } else { // Dimensions changed? int w = (int)width; int h = (int)height; if (LocatedAt.Width != w || LocatedAt.Height != h) { // Update it: LocatedAt.UpdateFixed(width, height); } } boundary.ClipBy(renderer.ClippingBoundary); // Ensure we have a batch: renderer.SetupBatch(this, null, null); if (Material == null) { // Create the material now using the isolated shader: Material = new Material(renderer.CurrentShaderSet.Isolated); // Hook up the output: Material.SetTexture("_MainTex", Output); } // Allocate the block: MeshBlock block = Add(renderer); // Set current material: SetBatchMaterial(renderer, Material); // Set the (overlay) colour: block.SetColour(renderer.ColorOverlay); block.TextUV = null; // Z-index (same as a background-image): float zIndex = (RenderData.computedStyle.ZIndex - 0.003f); BoxRegion screenRegion = new BoxRegion(); screenRegion.Set(left, top, width, height); // Setup the block: block.ImageUV = block.SetClipped(boundary, screenRegion, renderer, zIndex, LocatedAt, block.ImageUV); // Flush it: block.Done(renderer.Transform); }
//--------------------------------------
protected override void Layout() { if (Image == null || !Image.Loaded()) { return; } if (Clipping == BackgroundClipping.Text) { return; } Renderman renderer = Element.Document.Renderer; if (Image.Animated || Image.IsDynamic || renderer.RenderMode == RenderMode.NoAtlas || Filtering != FilterMode.Point || ForcedIsolate) { // SPA is an animation format, so we need a custom texture atlas to deal with it. // This is because the frames of any animation would quickly exhaust our global texture atlas. // So to get a custom atlas, we must isolate this property. Isolate(); } else if (Image.IsVideo) { // Similarly with a video, we need to isolate it aswell. Isolate(); #if !MOBILE if (!Image.Video.isPlaying && Element["autoplay"] != null) { // Play now: Image.Video.Play(); // Fire an onplay event: Element.Run("onplay"); // Clear: Element["autoplay"] = null; } #endif } else { // Reverse isolation, if we are isolated already: Include(); } ComputedStyle computed = Element.Style.Computed; // Get the full shape of the element: int width = computed.PaddedWidth; int height = computed.PaddedHeight; int minY = computed.OffsetTop + computed.BorderTop; int minX = computed.OffsetLeft + computed.BorderLeft; if (width == 0 || height == 0) { if (Visible) { SetVisibility(false); } return; } BoxRegion boundary = new BoxRegion(minX, minY, width, height); if (!boundary.Overlaps(renderer.ClippingBoundary)) { if (Visible) { SetVisibility(false); } return; } else if (!Visible) { // ImageLocation will allocate here if it's needed. SetVisibility(true); } boundary.ClipBy(renderer.ClippingBoundary); // Texture time - get it's location on that atlas: AtlasLocation locatedAt = ImageLocation; if (locatedAt == null) { // We're not using the atlas here. if (!Isolated) { Isolate(); } int imgWidth = Image.Width(); int imgHeight = Image.Height(); locatedAt = new AtlasLocation(0, 0, imgWidth, imgHeight, imgWidth, imgHeight); } // Isolation is all done - safe to setup the batch now: SetupBatch(locatedAt.Atlas, null); // Great - Use locatedAt.Width/locatedAt.Height - this removes any risk of overflowing into some other image. int imageCountX = 1; int imageCountY = 1; int trueImageWidth = locatedAt.Width; int trueImageHeight = locatedAt.Height; int imageWidth = trueImageWidth; int imageHeight = trueImageHeight; bool autoX = false; bool autoY = false; if (Image.PixelPerfect) { imageWidth = (int)(imageWidth * ScreenInfo.ResolutionScale); imageHeight = (int)(imageWidth * ScreenInfo.ResolutionScale); } if (SizeX != null) { if (SizeX.Single != 0f) { imageWidth = (int)(width * SizeX.Single); } else if (SizeX.PX != 0) { imageWidth = SizeX.PX; } else if (SizeX.IsAuto()) { autoX = true; } } if (SizeY != null) { if (SizeY.Single != 0f) { imageHeight = (int)(height * SizeY.Single); } else if (SizeY.PX != 0) { imageHeight = SizeY.PX; } else if (SizeY.IsAuto()) { autoY = true; } } if (autoX) { imageWidth = imageHeight * trueImageWidth / trueImageHeight; } else if (autoY) { imageHeight = imageWidth * trueImageHeight / trueImageWidth; } // offsetX and offsetY are the images position offset from where it should be (e.g. x of -200 means it's 200px left) // Resolve the true offset values: int offsetX = 0; int offsetY = 0; if (OffsetX != null) { // Resolve a potential mixed % and px: offsetX = OffsetX.GetMixed(width - imageWidth); } if (OffsetY != null) { // Resolve a potential mixed % and px: offsetY = OffsetY.GetMixed(height - imageHeight); } if (RepeatX) { // Get the rounded up number of images: imageCountX = (width - 1) / imageWidth + 1; if (offsetX != 0) { // If we have an offset, another image is introduced. imageCountX++; } } if (RepeatY) { // Get the rounded up number of images: imageCountY = (height - 1) / imageHeight + 1; if (offsetY != 0) { // If we have an offset, another image is introduced. imageCountY++; } } int blockX = minX + offsetX; int blockY = minY + offsetY; if (RepeatX && offsetX > 0) { // We're repeating and the image is offset by a +ve number. // This means a small gap, OffsetX px wide, is open on this left side. // So to fill it, we need to offset this first image by a much bigger number - the value imageWidth-OffsetX. blockX -= (imageWidth - offsetX); // This results in the first image having OffsetX pixels exposed in the box - this is what we want. } if (RepeatY && offsetY > 0) { // Similar thing to above: blockY -= (imageHeight - offsetY); } BoxRegion screenRegion = new BoxRegion(); bool first = true; int startX = blockX; Color colour = computed.ColorOverlay; float zIndex = (computed.ZIndex - 0.003f); for (int y = 0; y < imageCountY; y++) { for (int x = 0; x < imageCountX; x++) { // Draw at blockX/blockY. screenRegion.Set(blockX, blockY, imageWidth, imageHeight); if (screenRegion.Overlaps(boundary)) { // If the two overlap, this means it's actually visible. MeshBlock block = Add(); if (Image.Animated && first) { first = false; // Make sure we have an instance: Image.GoingOnDisplay(); block.ParentMesh.SetMaterial(Image.Animation.AnimatedMaterial); } else if (Image.IsVideo && first) { first = false; block.ParentMesh.SetMaterial(Image.VideoMaterial); } else if (Isolated && first) { first = false; block.ParentMesh.SetMaterial(Image.ImageMaterial); } // Set it's colour: block.SetColour(colour); // And clip our meshblock to fit within boundary: block.TextUV = null; block.ImageUV = block.SetClipped(boundary, screenRegion, renderer, zIndex, locatedAt, block.ImageUV); } blockX += imageWidth; } blockX = startX; blockY += imageHeight; } }
public void Render(LayoutBox box, Renderman renderer, float cornerX, float cornerY) { if (OuterArc == null) { RecomputeArcs(box.Border); } else if (InnerArc == null) { RecomputeInnerArc(box.Border); } // Get the z-Index: float zIndex = RoundCorners.Computed.MaxZIndex + 0.006f; // Figure out where half way is (divide by 2): int halfway = (BlocksRequired >> 1); Color colour = Colour; // Grab the clipping boundary: BoxRegion clip = renderer.ClippingBoundary; // Make it relative to the corners location: float minClipX = clip.X - cornerX; float minClipY = clip.Y - cornerY; float maxClipX = clip.MaxX - cornerX; float maxClipY = clip.MaxY - cornerY; // For each block.. for (int i = 0; i < BlocksRequired; i++) { // Read the outer arc: Vector2 outerPointA = OuterArc[i]; // Figure out the bounding box (constant for a particular block). float minX = outerPointA.x; float maxX = minX; float minY = outerPointA.y; float maxY = minY; Vector2 outerPointB = OuterArc[i + 1]; // Update the bounding box: if (outerPointB.x < minX) { minX = outerPointB.x; } else if (outerPointB.x > maxX) { maxX = outerPointB.x; } if (outerPointB.y < minY) { minY = outerPointB.y; } else if (outerPointB.y > maxY) { maxY = outerPointB.y; } // Line segment A->B on the "outer" arc. // Read the inner arc: Vector2 innerPointA = InnerArc[i]; // Update the bounding box: if (innerPointA.x < minX) { minX = innerPointA.x; } else if (innerPointA.x > maxX) { maxX = innerPointA.x; } if (innerPointA.y < minY) { minY = innerPointA.y; } else if (innerPointA.y > maxY) { maxY = innerPointA.y; } Vector2 innerPointB = InnerArc[i + 1]; // Update the bounding box: if (innerPointB.x < minX) { minX = innerPointB.x; } else if (innerPointB.x > maxX) { maxX = innerPointB.x; } if (innerPointB.y < minY) { minY = innerPointB.y; } else if (innerPointB.y > maxY) { maxY = innerPointB.y; } // How does our bounding box compare to the clipping region? if (maxX < minClipX) { continue; } else if (minX > maxClipX) { continue; } if (maxY < minClipY) { continue; } else if (minY > maxClipY) { continue; } // Line segment A->B on the "inner" arc. // Get a block: MeshBlock block = Border.Add(renderer); // Set the UV to that of the solid block colour pixel: block.SetSolidColourUV(); // Get the border colour: if (i == halfway) { // Get the next colour: if (Border.BaseColour != null && Border.BaseColour.Count != 1) { colour = Border.BaseColour[ToIndex].GetColour(Border.RenderData, Css.Properties.BorderColor.GlobalProperty) * renderer.ColorOverlay; } } // Set the border colour: block.SetColour(colour); // Apply the block region: block.VertexTopLeft = renderer.PixelToWorldUnit(cornerX + outerPointA.x, cornerY + outerPointA.y, zIndex); block.VertexTopRight = renderer.PixelToWorldUnit(cornerX + outerPointB.x, cornerY + outerPointB.y, zIndex); block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerX + innerPointA.x, cornerY + innerPointA.y, zIndex); block.VertexBottomRight = renderer.PixelToWorldUnit(cornerX + innerPointB.x, cornerY + innerPointB.y, zIndex); block.Done(renderer.Transform); } }
/// <summary>Draws a character with x-inverted UV's. Used for rendering e.g. "1 < 2" in right-to-left.</summary> private void DrawInvertCharacter(int index, ref float left, float top, Renderman renderer, float zIndex, BoxRegion screenRegion) { Glyph character = Characters[index]; if (character == null) { return; } if (Kerning != null) { left += Kerning[index] * FontSize; } if (character.Space) { left += SpaceSize + LetterSpacing; return; } float y = top + Ascender - ((character.Height + character.MinY) * FontSize); AtlasLocation locatedAt = character.Location; if (locatedAt == null) { // Not in font. return; } screenRegion.Set(left + (character.LeftSideBearing * FontSize), y, locatedAt.Width * ScaleFactor, locatedAt.Height * ScaleFactor); if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // True if this character is visible. // Ensure correct batch: SetupBatch(null, locatedAt.Atlas); MeshBlock block = Add(); block.SetColour(FontColour); // And clip our meshblock to fit within boundary: block.ImageUV = null; UVBlock uvs = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex, locatedAt, block.TextUV); if (uvs.Shared) { uvs = new UVBlock(uvs); } // Invert along X: float temp = uvs.MinX; uvs.MinX = uvs.MaxX; uvs.MaxX = temp; // Assign to the block: block.TextUV = uvs; } left += (character.AdvanceWidth * FontSize) + LetterSpacing; }
/// <summary>Draws a character and advances the pen onwards.</summary> private void DrawCharacter(int index, ref float left, float top, Renderman renderer, float zIndex, BoxRegion screenRegion) { Glyph character = Characters[index]; if (character == null) { return; } if (Kerning != null) { left += Kerning[index] * FontSize; } AtlasLocation locatedAt; if (character.Image != null) { if (!character.Image.Loaded()) { return; } // It's an image (e.g. Emoji). locatedAt = RequireImage(character.Image); if (locatedAt == null) { // It needs to be isolated. Big emoji image! return; } if (CharacterProviders.FixHeight) { // Set the region: screenRegion.Set(left, top, locatedAt.Width, locatedAt.Height); } else { screenRegion.Set(left, top, FontSize, FontSize); } if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // Ensure correct batch: SetupBatch(locatedAt.Atlas, null); // If the two overlap, this means it's actually visible. MeshBlock block = Add(); // Set it's colour: block.SetColour(Element.Style.Computed.ColorOverlay); // And clip our meshblock to fit within boundary: block.TextUV = null; block.ImageUV = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex, locatedAt, block.ImageUV); } left += (character.AdvanceWidth) + LetterSpacing; return; } else if (character.Space) { left += SpaceSize + LetterSpacing; return; } locatedAt = character.Location; if (locatedAt == null) { // Not in font. return; } float y = top + Ascender - ((character.Height + character.MinY) * FontSize); screenRegion.Set(left + (character.LeftSideBearing * FontSize), y, locatedAt.Width * ScaleFactor, locatedAt.Height * ScaleFactor); if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // True if this character is visible. // Ensure correct batch: SetupBatch(null, locatedAt.Atlas); MeshBlock block = Add(); block.SetColour(FontColour); // And clip our meshblock to fit within boundary: block.ImageUV = null; block.TextUV = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex, locatedAt, block.TextUV); } left += (character.AdvanceWidth * FontSize) + LetterSpacing; }
/// <summary>Renders the inverse of this corner for the border.</summary> public void RenderInverse(LayoutBox box, Renderman renderer, float cornerX, float cornerY) { float scale = RoundCorners.Computed.RenderData.ValueScale; if (scale != ValueScale) { // Value scale has changed - reset the radius: Radius = Radius * scale / ValueScale; ValueScale = scale; } if (OuterArc == null) { RecomputeArcs(box.Border); } else if (InnerArc == null) { RecomputeInnerArc(box.Border); } // Get the z-Index: float zIndex = RoundCorners.Computed.MaxZIndex + 0.004f; // Grab the size of the outer arc array: int arcSize = OuterArc.Length; int currentIndex = 0; // Resolve the corner: Vector3 corner = renderer.PixelToWorldUnit(cornerX, cornerY, zIndex); // Ensure a batch is available: renderer.SetupBatch(InverseBorder, null, null); // For each inverse block: for (int i = 0; i < InverseBlocksRequired; i++) { // Get a block: MeshBlock block = InverseBorder.Add(renderer); // Set the clear colour: block.SetColour(Color.clear); // Always going to be space to sample two. Sample the first: Vector2 outerPoint = InnerArc[currentIndex]; // Apply the triangle: block.VertexTopRight = corner; // Apply the first: block.VertexTopLeft = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex); // Sample the second: outerPoint = InnerArc[currentIndex + 1]; // Apply the second: block.VertexBottomLeft = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex); if ((currentIndex + 2) >= arcSize) { // Match the previous vertex: block.VertexBottomRight = block.VertexBottomLeft; } else { // Grab the next point along: outerPoint = InnerArc[currentIndex + 2]; // Resolve and apply the third: block.VertexBottomRight = renderer.PixelToWorldUnit(cornerX + outerPoint.x, cornerY + outerPoint.y, zIndex); } block.Done(renderer.Transform); // Move index along: currentIndex += 2; } }
/// <summary>Draws a character and advances the pen onwards.</summary> protected virtual void DrawCharacter(ref float left, Renderman renderer) { BoxRegion screenRegion = renderer.CurrentRegion; Color fontColour = renderer.FontColour; float top = renderer.TopOffset; int index = renderer.CharacterIndex; Glyph character = Text.Characters[index]; if (character == null) { return; } if (Text.Kerning != null) { left += Text.Kerning[index] * Text.FontSize; } AtlasLocation locatedAt; if (character.Image != null) { DrawEmoji(character, ref left, renderer); return; } // Get atlas location: locatedAt = character.Location; // Does this character have a visual glyph? E.g. a space does not. if (locatedAt != null) { float y = top + renderer.TextAscender - ((character.Height + character.MinY) * Text.FontSize); float scaleFactor = renderer.TextScaleFactor; screenRegion.Set(left + (character.LeftSideBearing * Text.FontSize), y, locatedAt.Width * scaleFactor, locatedAt.Height * scaleFactor); if (screenRegion.Overlaps(renderer.ClippingBoundary)) { // True if this character is visible. // Ensure correct batch: renderer.SetupBatch(this, null, locatedAt.Atlas); MeshBlock block = Add(renderer); block.SetColour(fontColour); block.ApplyOutline(); // And clip our meshblock to fit within boundary: block.ImageUV = null; block.TextUV = block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, RenderData.computedStyle.ZIndex, locatedAt, block.TextUV); block.Done(renderer.Transform); } } left += (character.AdvanceWidth * Text.FontSize) + Text.LetterSpacing; if (character.Charcode == (int)' ') { left += Text.WordSpacing; } }