Initial Commit
This commit is contained in:
commit
2f3600c84f
2 changed files with 333 additions and 0 deletions
47
Package.swift
Normal file
47
Package.swift
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// swift-tools-version: 6.1
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "TestGame",
|
||||
platforms: [.macOS(.v14)],
|
||||
products: [
|
||||
.executable(name: "TestGame", targets: ["TestGame"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "../ArtifactUI"),
|
||||
.package(path: "../ArtifactPlatform", traits: ["SDL3"]),
|
||||
.package(path: "../ArtifactGraphics"),
|
||||
.package(path: "../ArtifactColor"),
|
||||
.package(path: "../ArtifactSDL3WebGPU"),
|
||||
.package(path: "../ArtifactWorld"),
|
||||
.package(path: "../ArtifactFilesystem"),
|
||||
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.2"),
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "TestGame",
|
||||
dependencies: [
|
||||
"ArtifactSDL3WebGPU",
|
||||
"ArtifactPlatform",
|
||||
"ArtifactGraphics",
|
||||
"ArtifactColor",
|
||||
"ArtifactUI",
|
||||
"ArtifactWorld",
|
||||
"ArtifactFilesystem",
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser")
|
||||
],
|
||||
linkerSettings: [
|
||||
.unsafeFlags(["-L\(Context.packageDirectory)/../GLFW/Artifacts/glfw/lib-universal", "-L\(Context.packageDirectory)/../WGPUNative/Artifacts/WGPUNative/lib/macOS"]),
|
||||
.linkedLibrary("glfw3"),
|
||||
.linkedLibrary("wgpu_native"),
|
||||
.linkedFramework("CoreGraphics"),
|
||||
.linkedFramework("IOKit"),
|
||||
.linkedFramework("Cocoa"),
|
||||
.linkedFramework("QuartzCore"),
|
||||
.linkedFramework("Metal")
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
286
Sources/TestGame/TestGame.swift
Normal file
286
Sources/TestGame/TestGame.swift
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
|
||||
import ArtifactPlatform
|
||||
import ArtifactGraphics
|
||||
import ArtifactUI
|
||||
import ArtifactState
|
||||
import ArtifactMath
|
||||
import ArtifactFilesystem
|
||||
import ArtifactColor
|
||||
import Foundation
|
||||
|
||||
import ArgumentParser
|
||||
|
||||
@main
|
||||
struct TestGame: AsyncParsableCommand {
|
||||
@Option(name: [.short, .long], defaultAsFlag: "")
|
||||
var config: String?
|
||||
|
||||
struct AppSettings: Codable {
|
||||
var name: String
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public mutating func run() async {
|
||||
let atlas = STBTextProvider()
|
||||
|
||||
@State var thing = 0
|
||||
|
||||
@Settings
|
||||
var settings: AppSettings = AppSettings(name: "test")
|
||||
|
||||
let scrollView2 = ScrollView {
|
||||
VStack {
|
||||
for i in 1...20 {
|
||||
Text("Sub-item \(i)")
|
||||
}
|
||||
}
|
||||
}
|
||||
.height(120)
|
||||
.backgroundColor(.gold)
|
||||
.style(BorderWidthStyle.self, 1)
|
||||
.style(BorderColorStyle.self, .blue)
|
||||
|
||||
let scrollView: ScrollView = ScrollView {
|
||||
VStack {
|
||||
for i in 1...20 {
|
||||
Text("Item \(i)")
|
||||
}
|
||||
|
||||
scrollView2
|
||||
|
||||
for i in 21...50 {
|
||||
Text("Item \(i)")
|
||||
}
|
||||
|
||||
Text("""
|
||||
If the ScrollView has an absolute height, preferredSize correctly returns that height.
|
||||
If the ScrollView has height: .auto (the default), preferredSize returns the sum of its children’s preferred sizes (or zero if it has no children yet / they haven’t been laid out).
|
||||
The fatal interaction appears when the ScrollView is given an absolute size after it has already been inserted, or when the Stack’s first measurement pass sees .auto / .grow and therefore writes a zero (or very small) height into child.frame.size.y. Subsequent layout of the ScrollView itself then sees a zero-height content area and never recovers.
|
||||
""")
|
||||
}
|
||||
}
|
||||
.height(.grow(1))
|
||||
|
||||
let styles = StyleSheet {
|
||||
StyleRule(.type(Stack.self) / (.type(Text.self) | .type(Stack.self))) {
|
||||
BackgroundColorStyle.property(Color(21, 22, 24))
|
||||
BorderColorStyle.property(Color(64, 64, 64))
|
||||
TextColorStyle.property(Color(100, 100, 100))
|
||||
}
|
||||
}
|
||||
|
||||
let ctx = UIContext(atlas, styles) {
|
||||
HStack {
|
||||
VStack {
|
||||
Text("Test 1")
|
||||
Text("Test 3")
|
||||
HStack {
|
||||
Spacer(width: 50%, height: .absolute(50))
|
||||
.style(BorderRadiusStyle.self, 4)
|
||||
.style(BackgroundColorStyle.self, .red)
|
||||
.onClick { ev in
|
||||
print("Click")
|
||||
scrollView.style[BackgroundColorStyle.self] = .rgb(.random(in: 0...255), .random(in: 0...255), .random(in: 0...255))
|
||||
}
|
||||
.onPointerDown { ev in
|
||||
print("Pointer down")
|
||||
}
|
||||
.onPointerUp { ev in
|
||||
print("Pointer up")
|
||||
}
|
||||
// .onPointerEnter { ev in
|
||||
// scrollView.style[BackgroundColorStyle.self] = .blue
|
||||
// }
|
||||
// .onPointerLeave { ev in
|
||||
// scrollView.style[BackgroundColorStyle.self] = .red
|
||||
// }
|
||||
}
|
||||
.alignment(.center)
|
||||
DerivedElement {
|
||||
Text("Thing: \(thing)")
|
||||
}
|
||||
}
|
||||
.groups("foo")
|
||||
.width(.grow(1))
|
||||
.padding(50, 25)
|
||||
.style(BorderRadiusStyle.self, 100)
|
||||
.style(BorderWidthStyle.self, 6)
|
||||
|
||||
VStack {
|
||||
scrollView
|
||||
Text("Test 2")
|
||||
}
|
||||
.alignment(.trailing)
|
||||
.width(.grow(1))
|
||||
.padding(15)
|
||||
}
|
||||
.width(100%)
|
||||
.height(100%)
|
||||
}
|
||||
|
||||
print(scrollView2.preferredSize())
|
||||
|
||||
let window = SDL3Window(width: 1024, height: 720)
|
||||
|
||||
let graphics = WebGPUGraphics(window)
|
||||
|
||||
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 = Mat4.ortho(left: 0, right: Float(window.width), bottom: Float(window.height), top: 0, near: -1, far: 1)
|
||||
|
||||
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: projection.bytes)
|
||||
|
||||
ctx.setViewportSize(to: Vec2(1024, 720))
|
||||
|
||||
let list = ctx.getDrawList()
|
||||
|
||||
verts.writeData(list.vertices)
|
||||
drawable.vertexCount = list.vertexCount
|
||||
inds.writeData(list.indices.withUnsafeBytes { Array($0) })
|
||||
drawable.indexCount = list.indices.count
|
||||
drawable.operations = list.operations.map {
|
||||
switch $0 {
|
||||
case .draw(let count):
|
||||
.draw(count: count)
|
||||
case .pushScissor(let rect):
|
||||
.pushScissor(rect: rect)
|
||||
case .popScissor:
|
||||
.popScissor
|
||||
case .pushTransform(let transformation):
|
||||
.pushTransform(transformation)
|
||||
case .popTransform:
|
||||
.popTransform
|
||||
}
|
||||
}
|
||||
|
||||
ctx.dump()
|
||||
|
||||
window.onDraw {
|
||||
ctx.update(deltaTime: $0)
|
||||
|
||||
if ctx.needsDisplay {
|
||||
let list = ctx.getDrawList()
|
||||
|
||||
verts.writeData(list.vertices)
|
||||
drawable.vertexCount = list.vertexCount
|
||||
inds.writeData(list.indices.withUnsafeBytes { Array($0) })
|
||||
drawable.indexCount = list.indices.count
|
||||
drawable.operations = list.operations.map {
|
||||
switch $0 {
|
||||
case .draw(let count):
|
||||
.draw(count: count)
|
||||
case .pushScissor(let rect):
|
||||
.pushScissor(rect: rect)
|
||||
case .popScissor:
|
||||
.popScissor
|
||||
case .pushTransform(let transformation):
|
||||
.pushTransform(transformation)
|
||||
case .popTransform:
|
||||
.popTransform
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.onResize { x, y in
|
||||
ctx.setViewportSize(to: Vec2d(Double(x), Double(y)))
|
||||
projection = Mat4.ortho(left: 0, right: Float(window.width), bottom: Float(window.height), top: 0, near: -1, far: 1)
|
||||
drawable.uniforms = projection.bytes
|
||||
}
|
||||
|
||||
window.actions.onKeyDown(.escape) {
|
||||
window.close()
|
||||
}
|
||||
|
||||
window.actions.onKeyDown(.enter) {
|
||||
scrollView.animate(duration: 1000, easing: .easeInOut) {
|
||||
HeightStyle.property(50%)
|
||||
// BackgroundColorStyle.property(.red)
|
||||
} to: {
|
||||
HeightStyle.property(100%)
|
||||
// BackgroundColorStyle.property(.blue)
|
||||
}
|
||||
}
|
||||
|
||||
window.actions.onPointerDown { pointer in
|
||||
ctx.handlePointerDown(pointer)
|
||||
}
|
||||
|
||||
window.actions.onPointerMove { pointer in
|
||||
ctx.handlePointerMove(pointer)
|
||||
}
|
||||
|
||||
window.actions.onPointerUp { pointer in
|
||||
ctx.handlePointerUp(pointer)
|
||||
}
|
||||
|
||||
window.actions.onWheel { pointer, delta in
|
||||
ctx.handleWheel(pointer, delta: delta)
|
||||
// thing += 1
|
||||
// print(thing)
|
||||
// //ctx.input.scroll(amount: Vec2d(delta) * 2)
|
||||
// scrollView.scrollPos += Vec2d(delta) * 4
|
||||
}
|
||||
|
||||
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