Provisional example.
This commit is contained in:
commit
08d1bec224
1 changed files with 202 additions and 0 deletions
202
main.swift
Normal file
202
main.swift
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
// Example
|
||||||
|
|
||||||
|
import stb_truetype
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class STBTextProvider: UITextProvider {
|
||||||
|
// TODO: Add a fallback font.
|
||||||
|
let font = try! Data(contentsOf: URL(filePath: "/path/to/Arial.ttf"))
|
||||||
|
var pixels = [UInt8](repeating: 0, count: 1024 * 1024)
|
||||||
|
var chars = [stbtt_bakedchar](repeating: .init(), count: 256)
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
let atlas = font.withUnsafeBytes { ptr in
|
||||||
|
pixels.withUnsafeMutableBufferPointer { pixels in
|
||||||
|
chars.withUnsafeMutableBufferPointer { chars in
|
||||||
|
stbtt_BakeFontBitmap(ptr.baseAddress, 0, 32, pixels.baseAddress, 1024, 1024, Int32("\0".utf8CString[0]), 256, chars.baseAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixels[0] = .max
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getQuadForChar(_ char: Character) -> UIRect {
|
||||||
|
var x: Float = 0
|
||||||
|
var y: Float = 0
|
||||||
|
var quad = stbtt_aligned_quad()
|
||||||
|
chars.withUnsafeMutableBufferPointer { chars in
|
||||||
|
stbtt_GetBakedQuad(chars.baseAddress, 1024, 1024, Int32(char.asciiValue ?? "?".first!.asciiValue!), &x, &y, &quad, 1)
|
||||||
|
}
|
||||||
|
return UIRect(origin: UIVec2(Double(x), Double(y)), size: UIVec2(Double(quad.x1 - quad.x0), Double(quad.y1 - quad.y0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getCharInfo(_ char: Character) -> stbtt_bakedchar {
|
||||||
|
return chars[Int(char.asciiValue ?? "?".first!.asciiValue!)]
|
||||||
|
}
|
||||||
|
|
||||||
|
public func measure(text: String, maxWidth: Double?) -> UIVec2 {
|
||||||
|
var out = UIVec2(0.0, 0.0)
|
||||||
|
var advance = UIVec2(0.0, 32.0)
|
||||||
|
for char in text {
|
||||||
|
let info = getCharInfo(char)
|
||||||
|
advance.x += Double(info.xadvance)
|
||||||
|
if let maxWidth = maxWidth, advance.x >= maxWidth {
|
||||||
|
out.x = maxWidth
|
||||||
|
advance.x = 0
|
||||||
|
advance.y += 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.x = max(out.x, advance.x)
|
||||||
|
out.y = advance.y
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
public func drawText(into list: inout UIDrawList, rect: UIRect, text: String, color: UIColor, fontName: String?, fontSize: Float, alignment: Alignment) {
|
||||||
|
var advance = 0.0
|
||||||
|
for char in text {
|
||||||
|
let info = getCharInfo(char)
|
||||||
|
let cw = Double(info.x1 - info.x0)
|
||||||
|
let ch = Double(info.y1 - info.y0)
|
||||||
|
|
||||||
|
let uvLeft = Double(info.x0) / 1024.0
|
||||||
|
let uvRight = Double(info.x1) / 1024.0
|
||||||
|
let uvTop = Double(info.y0) / 1024.0
|
||||||
|
let uvBottom = Double(info.y1) / 1024.0
|
||||||
|
|
||||||
|
list.addQuad(
|
||||||
|
bounds: UIRect(
|
||||||
|
origin: rect.origin + (advance + Double(info.xoff), Double(fontSize + info.yoff)),
|
||||||
|
size: UIVec2(cw, ch)
|
||||||
|
),
|
||||||
|
uv: UIRect(
|
||||||
|
origin: UIVec2(uvLeft, uvTop),
|
||||||
|
size: UIVec2(uvRight - uvLeft, uvBottom - uvTop)
|
||||||
|
),
|
||||||
|
color: color
|
||||||
|
)
|
||||||
|
advance += Double(info.xadvance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let atlas = STBTextProvider()
|
||||||
|
|
||||||
|
let ctx = UIContext(STBTextProvider()) {
|
||||||
|
HStack {
|
||||||
|
VStack {
|
||||||
|
Text("Test 1")
|
||||||
|
Text("Test 3")
|
||||||
|
Text("Test 4")
|
||||||
|
}
|
||||||
|
.width(.grow(1))
|
||||||
|
.backgroundColor(.white)
|
||||||
|
.withStyle(BorderRadiusStyle.self, 100)
|
||||||
|
.withStyle(BorderWidthStyle.self, 6)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
ScrollView {
|
||||||
|
VStack {
|
||||||
|
for i in 1...50 {
|
||||||
|
Text("Item \(i)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.height(.grow(1))
|
||||||
|
Text("Test 2")
|
||||||
|
}
|
||||||
|
.alignment(.trailing)
|
||||||
|
.width(.grow(1))
|
||||||
|
.backgroundColor(.init(60, 60, 60))
|
||||||
|
}
|
||||||
|
.width(.relative(1))
|
||||||
|
.height(.relative(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = GLFWWindow(width: 1024, height: 720)
|
||||||
|
let graphics = window.createGraphics(WebGPUGraphics.self)
|
||||||
|
|
||||||
|
let uniforms = graphics.createBuffer(label: "Uniform buffer", usage: [.uniform, .copyDest], size: 2048)
|
||||||
|
let texture = graphics.createTexture(label: "Texture", usage: [.texture, .copyDest], size: [1024, 1024])
|
||||||
|
let sampler = graphics.createSampler(label: "Sampler")
|
||||||
|
|
||||||
|
var rgba = [UInt8](repeating: 0, count: 1024*1024*4)
|
||||||
|
for i in 0..<1024*1024 {
|
||||||
|
let gray = atlas.pixels[i]
|
||||||
|
let idx = i * 4
|
||||||
|
rgba[idx+0] = 255 // R
|
||||||
|
rgba[idx+1] = 255 // G
|
||||||
|
rgba[idx+2] = 255 // B
|
||||||
|
rgba[idx+3] = gray // A
|
||||||
|
}
|
||||||
|
texture.writeData(rgba)
|
||||||
|
|
||||||
|
var projection = glm_mat4()
|
||||||
|
glm_ortho(0, Float(window.width), Float(window.height), 0, -1, 1, &projection.0)
|
||||||
|
|
||||||
|
let verts = graphics.createBuffer(label: "UI vertex buffer", usage: [.vertex, .copyDest], size: 1024 * 1024)
|
||||||
|
let inds = graphics.createBuffer(label: "UI index buffer", usage: [.index, .copyDest], size: 1024 * 1024)
|
||||||
|
var drawable = Drawable(passes: ["ui"], vertices: verts, vertexCount: 0, indices: inds, indexCount: 0, uniforms: mat4AsBytes(&projection))
|
||||||
|
|
||||||
|
|
||||||
|
let list = ctx.getDrawList()
|
||||||
|
|
||||||
|
print("Vertices: \(list.vertices)")
|
||||||
|
|
||||||
|
verts.writeData(list.vertices)
|
||||||
|
drawable.vertexCount = list.vertexCount
|
||||||
|
inds.writeData(list.indices.withUnsafeBytes { Array($0) })
|
||||||
|
drawable.indexCount = list.indices.count
|
||||||
|
|
||||||
|
ctx.dump()
|
||||||
|
|
||||||
|
window.registerOnResize { x, y in
|
||||||
|
ctx.viewportSize = UIVec2(Double(x), Double(y))
|
||||||
|
glm_ortho(0, Float(window.width), Float(window.height), 0, -1, 1, &projection.0)
|
||||||
|
drawable.uniforms = mat4AsBytes(&projection)
|
||||||
|
}
|
||||||
|
|
||||||
|
graphics.registerPass(
|
||||||
|
RenderPass(
|
||||||
|
name: "ui",
|
||||||
|
label: "UI Pass",
|
||||||
|
bindings: [
|
||||||
|
.buffer(stage: .vertex, type: .uniform, buffer: uniforms),
|
||||||
|
.texture(stage: .fragment, sampleType: .float, texture: texture),
|
||||||
|
.sampler(stage: .fragment, type: .filtering, sampler: sampler)
|
||||||
|
],
|
||||||
|
vertexLayout: [.init(attributes: [.vec2f, .vec2f, .vec4n])],
|
||||||
|
vertexShader: Shader(entryPoint: "vs_main", source: """
|
||||||
|
struct Uniforms {
|
||||||
|
ortho: mat4x4<f32>,
|
||||||
|
}
|
||||||
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) pos: vec4<f32>,
|
||||||
|
@location(0) uv: vec2<f32>,
|
||||||
|
@location(1) color: vec4<f32>,
|
||||||
|
}
|
||||||
|
@vertex
|
||||||
|
fn vs_main(@location(0) pos: vec2<f32>, @location(1) uv: vec2<f32>, @location(2) color: vec4<f32>) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
out.pos = uniforms.ortho * vec4<f32>(pos, 0.0, 1.0);
|
||||||
|
out.uv = uv;
|
||||||
|
out.color = color;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
colorTargets: [ColorTarget(channels: .all, colorBlendOperation: .add, colorBlendSrcFactor: .srcAlpha, colorBlendDestFactor: .oneMinusSrcAlpha, alphaBlendOperation: .add, alphaBlendSrcFactor: .one, alphaBlendDestFactor: .oneMinusSrcAlpha)],
|
||||||
|
fragmentShader: Shader(entryPoint: "fs_main", source: """
|
||||||
|
@group(0) @binding(1) var texture: texture_2d<f32>;
|
||||||
|
@group(0) @binding(2) var sampler_: sampler;
|
||||||
|
@fragment
|
||||||
|
fn fs_main(@location(0) uv: vec2<f32>, @location(1) color: vec4<f32>) -> @location(0) vec4<f32> {
|
||||||
|
return textureSample(texture, sampler_, uv) * color;
|
||||||
|
}
|
||||||
|
"""),
|
||||||
|
clearColor: .init(0, 0, 128, .max)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
graphics.scene.append(drawable)
|
||||||
|
|
||||||
|
window.run()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue