diff --git a/draw/draw.odin b/draw/draw.odin index a2684ca..ba0e20b 100644 --- a/draw/draw.odin +++ b/draw/draw.odin @@ -1,5 +1,7 @@ package edit2draw +import e2_glyph "shared:Edit2D/glyph" +import "core:strings" import "base:runtime" import "core:fmt" import "core:math/linalg" @@ -7,15 +9,37 @@ import gl "vendor:OpenGL" import b2 "vendor:box2d" +CameraType :: enum +{ + BOX_2D, + ORTHO, +} + Camera :: struct { - center : b2.Vec2, - width, height : i32, - zoom : f32, - rotation : f32, + center : [2]f32, + width, height : i32, + zoom, rotation : f32, + type : CameraType, } RGBA8 :: [4]u8 +/* +just coping the box2d here because the shaders depends on these structure but we don't want to depend on it +*/ + +Rot :: struct +{ + c, s : f32, //cosine and sine +} + +Transform :: struct +{ + p: Vec2, + q: Rot, +} + + make_rgba :: proc(color : b2.HexColor, alpha : f32) -> RGBA8 { c := i32(color) return { @@ -26,8 +50,18 @@ make_rgba :: proc(color : b2.HexColor, alpha : f32) -> RGBA8 { } } +make_rgba_i32 :: proc(color : i32, alpha : f32) -> RGBA8 { + c := i32(color) + return { + u8((c >> 16) & 0xFF), + u8((c >> 8) & 0xFF), + u8(c & 0xFF), + u8(0xFF * alpha), + } +} + make_hex_color :: proc(rgba: RGBA8) -> b2.HexColor { - return b2.HexColor((u32(rgba.r) << 16) | (u32(rgba.g) << 8) | u32(rgba.b)) + return b2.HexColor((u32(rgba.r) << 16) | (u32(rgba.g) << 8) | u32(rgba.b)) } @@ -51,18 +85,17 @@ camera_init :: proc() -> Camera { camera_convert_screen_to_world_64 :: proc( cam : ^Camera, ps : [2]f64, -) -> b2.Vec2 { + ) -> [2]f32 { ps_32 :[2]f32= {f32(ps.x), f32(ps.y)} return camera_convert_screen_to_world_32(cam, ps_32) - } camera_convert_screen_to_world_32 :: proc( cam : ^Camera, - ps : b2.Vec2, -) -> b2.Vec2 { + ps : [2]f32, + ) -> [2]f32 { ps :[2]f32= {f32(ps.x), f32(ps.y)} @@ -71,13 +104,14 @@ camera_convert_screen_to_world_32 :: proc( u := ps.x / w v := (h - ps.y) / h + ratio := w / h - extents : b2.Vec2 = {cam.zoom * ratio, cam.zoom} + extents : [2]f32 = {cam.zoom * ratio, cam.zoom} lower := cam.center - extents upper := cam.center + extents - pw : b2.Vec2 = { + pw : [2]f32 = { (1.0 - u) * lower.x + u * upper.x, (1.0 - v) * lower.y + v * upper.y, } @@ -92,8 +126,8 @@ camera_convert_screen_to_world :: proc { camera_convert_world_to_screen :: proc( cam : ^Camera, - pw : b2.Vec2, -) -> b2.Vec2 { + pw : [2]f32, + ) -> [2]f32 { cam := cam pw := pw @@ -101,15 +135,15 @@ camera_convert_world_to_screen :: proc( h := f32(cam.height) switch cam.rotation { - case 1 ..= 90, 180 ..= 270: + case 1 ..= 90, 180 ..= 270: pw = swizzle(pw, 1, 0) pw.y = -pw.y - case 180: + case 180: pw.y = -pw.y } ratio := w / h - extents : b2.Vec2 = {cam.zoom * ratio, cam.zoom} + extents : [2]f32 = {cam.zoom * ratio, cam.zoom} rotated_pw := pw @@ -120,7 +154,7 @@ camera_convert_world_to_screen :: proc( u := (rotated_pw.x - lower.x) / (upper.x - lower.x) v := (rotated_pw.y - lower.y) / (upper.y - lower.y) - ps : b2.Vec2 = {u * w, (1.0 - v) * h} + ps : [2]f32 = {u * w, (1.0 - v) * h} return ps } @@ -129,19 +163,57 @@ DEG2RAD :: PI / 180.0 RAD2DEG :: 180.0 / PI +orthographic_matrix :: proc(cam: ^Camera) -> matrix[4,4]f32{ + + left :f32= 0 + right :f32= f32(cam.width) + + bottom :f32= f32(cam.height) + top :f32= 0 + + near : f32 = 1 + far : f32 = -1 + + m: matrix[4,4]f32 + + m[0][0] = 2.0 / (right - left) + m[0][1] = 0.0 + m[0][2] = 0.0 + m[0][3] = 0.0 + + m[1][0] = 0.0 + m[1][1] = 2.0 / (top - bottom) + m[1][2] = 0.0 + m[1][3] = 0.0 + + m[2][0] = 0.0 + m[2][1] = 0.0 + m[2][2] = -2.0 / (far - near) + m[2][3] = 0.0 + + m[3][0] = -(right + left) / (right - left) + m[3][1] = -(top + bottom) / (top - bottom) + m[3][2] = -(far + near) / (far - near) + m[3][3] = 1.0 + return m +} + + //Convert from world coordinates to normalized device coordinates // http://www.songho.ca/opengl/gl_projectionmatrix.html camera_build_project_matrix :: proc( cam : ^Camera, z_bias : f32, -) -> matrix[4, 4]f32 { + ) -> matrix[4, 4]f32 { + + if cam.type == .ORTHO do return orthographic_matrix(cam) m : matrix[4, 4]f32 mat_rot := linalg.matrix4_rotate_f32(DEG2RAD * cam.rotation, {0, 0, 1}) ratio := f32(cam.width) / f32(cam.height) - extents : b2.Vec2 = {cam.zoom * ratio, cam.zoom} + extents : [2]f32 = {cam.zoom * ratio, cam.zoom} lower := cam.center - extents upper := cam.center + extents @@ -161,8 +233,8 @@ camera_build_project_matrix :: proc( camera_get_view_bounds :: proc(cam : ^Camera) -> b2.AABB { return b2.AABB { - lowerBound = camera_convert_screen_to_world(cam, b2.Vec2{0, f32(cam.height)}), - upperBound = camera_convert_screen_to_world(cam, b2.Vec2{f32(cam.width), 0}), + lowerBound = camera_convert_screen_to_world(cam, [2]f32{0, f32(cam.height)}), + upperBound = camera_convert_screen_to_world(cam, [2]f32{f32(cam.width), 0}), } } @@ -180,14 +252,13 @@ check_opengl :: proc() { } } - background_create :: proc(back : ^Background) { ok : bool back.program, ok = gl.load_shaders_source( #load("shaders/background.vs"), #load("shaders/background.fs"), - ) + ) check_opengl() back.uniforms = gl.get_uniforms_from_program(back.program) @@ -201,14 +272,14 @@ background_create :: proc(back : ^Background) { gl.EnableVertexAttribArray(vertex_attribute) //Single quad - vertices : [4]b2.Vec2 = {{-1.0, 1.0}, {-1.0, -1.0}, {1.0, 1.0}, {1.0, -1}} + vertices : [4][2]f32 = {{-1.0, 1.0}, {-1.0, -1.0}, {1.0, 1.0}, {1.0, -1}} gl.BindBuffer(gl.ARRAY_BUFFER, back.vbo) gl.BufferData( gl.ARRAY_BUFFER, size_of(vertices), &vertices[0], gl.STATIC_DRAW, - ) + ) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, false, 0, 0) check_opengl() @@ -238,7 +309,7 @@ background_draw :: proc(back : ^Background, cam : ^Camera) { back.uniforms["resolution"].location, f32(cam.width), f32(cam.height), - ) + ) gl.Uniform3f(back.uniforms["baseColor"].location, 0.4, 0.4, 0.2) @@ -252,7 +323,7 @@ background_draw :: proc(back : ^Background, cam : ^Camera) { PointData :: struct { - pos : b2.Vec2, + pos : [2]f32, size : f32, rgba : RGBA8, } @@ -309,7 +380,7 @@ points_create :: proc(point : ^Point) { 2048 * size_of(PointData), nil, gl.DYNAMIC_DRAW, - ) + ) gl.VertexAttribPointer( vertex_attribute, @@ -318,7 +389,7 @@ points_create :: proc(point : ^Point) { gl.FALSE, size_of(PointData), offset_of(PointData, pos), - ) + ) gl.VertexAttribPointer( size_attribute, @@ -327,7 +398,7 @@ points_create :: proc(point : ^Point) { gl.FALSE, size_of(PointData), offset_of(PointData, size), - ) + ) gl.VertexAttribPointer( color_attribute, @@ -336,7 +407,7 @@ points_create :: proc(point : ^Point) { gl.TRUE, size_of(PointData), offset_of(PointData, rgba), - ) + ) check_opengl() @@ -359,7 +430,7 @@ points_destroy :: proc(point : ^Point) { } } -points_add :: proc(point : ^Point, v : b2.Vec2, size : f32, c : b2.HexColor) { +points_add :: proc(point : ^Point, v : [2]f32, size : f32, c : b2.HexColor) { rgba := make_rgba(c, 1.0) append(&point.points, PointData{v, size, rgba}) } @@ -373,12 +444,7 @@ points_flush :: proc(point : ^Point, cam : ^Camera) { proj := camera_build_project_matrix(cam, 0) - gl.UniformMatrix4fv( - point.uniforms["projectionMatrix"].location, - 1, - gl.FALSE, - &proj[0][0], - ) + gl.UniformMatrix4fv(point.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0]) gl.BindVertexArray(point.vao) @@ -391,11 +457,8 @@ points_flush :: proc(point : ^Point, cam : ^Camera) { batch_count : i32 = min(count, 2048) gl.BufferSubData( - gl.ARRAY_BUFFER, - 0, - int(batch_count * size_of(PointData)), - &point.points[base], - ) + gl.ARRAY_BUFFER, 0, int(batch_count * size_of(PointData)), &point.points[base]) + gl.DrawArrays(gl.POINTS, 0, batch_count) check_opengl() @@ -413,7 +476,7 @@ points_flush :: proc(point : ^Point, cam : ^Camera) { } VertexData :: struct { - pos : b2.Vec2, + pos : [2]f32, rgba : RGBA8, } @@ -427,26 +490,26 @@ Lines :: struct { lines_create :: proc(line : ^Lines) { vs : string = ` - #version 330 - uniform mat4 projectionMatrix; - layout(location = 0) in vec2 v_position; - layout(location = 1) in vec4 v_color; - out vec4 f_color; + #version 330 + uniform mat4 projectionMatrix; + layout(location = 0) in vec2 v_position; + layout(location = 1) in vec4 v_color; + out vec4 f_color; - void main(void){ - f_color = v_color; - gl_Position = projectionMatrix * vec4(v_position , 0.0f, 1.0f); - } + void main(void){ + f_color = v_color; + gl_Position = projectionMatrix * vec4(v_position , 0.0f, 1.0f); + } ` fs : string = ` - #version 330 - in vec4 f_color; - out vec4 color; + #version 330 + in vec4 f_color; + out vec4 color; - void main(void){ - color = f_color; - } + void main(void){ + color = f_color; + } ` ok := false @@ -471,7 +534,7 @@ lines_create :: proc(line : ^Lines) { 2048 * size_of(VertexData), nil, gl.DYNAMIC_DRAW, - ) + ) gl.VertexAttribPointer( vertex_attribute, @@ -480,7 +543,7 @@ lines_create :: proc(line : ^Lines) { gl.FALSE, size_of(VertexData), offset_of(VertexData, pos), - ) + ) gl.VertexAttribPointer( color_attribute, 4, @@ -488,7 +551,7 @@ lines_create :: proc(line : ^Lines) { gl.TRUE, size_of(VertexData), offset_of(VertexData, rgba), - ) + ) check_opengl() gl.BindBuffer(gl.ARRAY_BUFFER, 0) @@ -510,7 +573,7 @@ lines_destroy :: proc(line : ^Lines) { } -lines_add :: proc(line : ^Lines, p1, p2 : b2.Vec2, c : b2.HexColor) { +lines_add :: proc(line : ^Lines, p1, p2 : [2]f32, c : b2.HexColor) { rgba := make_rgba(c, 1.0) append(&line.points, VertexData{p1, rgba}) append(&line.points, VertexData{p2, rgba}) @@ -536,7 +599,7 @@ lines_flush :: proc(line : ^Lines, cam : ^Camera) { 1, gl.FALSE, &proj[0][0], - ) + ) gl.BindVertexArray(line.vao) gl.BindBuffer(gl.ARRAY_BUFFER, line.vbo) @@ -563,7 +626,7 @@ lines_flush :: proc(line : ^Lines, cam : ^Camera) { CircleData :: struct { - pos : b2.Vec2, + pos : [2]f32, radius : f32, rgba : RGBA8, } @@ -583,7 +646,7 @@ circle_create :: proc(circle : ^Circles) { circle.program, _ = gl.load_shaders_source( #load("shaders/circle.vs"), #load("shaders/circle.fs"), - ) + ) check_opengl() circle.uniforms = gl.get_uniforms_from_program(circle.program) @@ -604,7 +667,7 @@ circle_create :: proc(circle : ^Circles) { //vertex buffer for single quad a : f32 = 1.1 - vertices : []b2.Vec2 = { + vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, @@ -616,10 +679,10 @@ circle_create :: proc(circle : ^Circles) { gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbos[0]) gl.BufferData( gl.ARRAY_BUFFER, - size_of(b2.Vec2) * 6, + size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW, - ) + ) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // @@ -629,7 +692,7 @@ circle_create :: proc(circle : ^Circles) { batch_size * size_of(CircleData), nil, gl.DYNAMIC_DRAW, - ) + ) gl.VertexAttribPointer( position_instance, @@ -638,7 +701,7 @@ circle_create :: proc(circle : ^Circles) { gl.FALSE, size_of(CircleData), offset_of(CircleData, pos), - ) + ) gl.VertexAttribPointer( radiusInstance, 1, @@ -646,7 +709,7 @@ circle_create :: proc(circle : ^Circles) { gl.FALSE, size_of(CircleData), offset_of(CircleData, radius), - ) + ) gl.VertexAttribPointer( colorInstance, 4, @@ -654,7 +717,7 @@ circle_create :: proc(circle : ^Circles) { gl.TRUE, size_of(CircleData), offset_of(CircleData, rgba), - ) + ) gl.VertexAttribDivisor(position_instance, 1) gl.VertexAttribDivisor(radiusInstance, 1) @@ -682,10 +745,10 @@ circle_destroy :: proc(circle : ^Circles) { circle_add :: proc( circle : ^Circles, - center : b2.Vec2, + center : [2]f32, radius : f32, color : b2.HexColor, -) { + ) { rgba := make_rgba(color, 1.0) append(&circle.circles, CircleData{center, radius, rgba}) } @@ -704,11 +767,11 @@ circle_flush :: proc(circle : ^Circles, cam : ^Camera) { 1, gl.FALSE, &proj[0][0], - ) + ) gl.Uniform1f( circle.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom, - ) + ) gl.BindVertexArray(circle.vao) @@ -726,7 +789,7 @@ circle_flush :: proc(circle : ^Circles, cam : ^Camera) { 0, int(batch_count * size_of(CircleData)), &circle.circles[base], - ) + ) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, batch_count) check_opengl() @@ -744,13 +807,15 @@ circle_flush :: proc(circle : ^Circles, cam : ^Camera) { } -SolidCircleData :: struct { - transform : b2.Transform, +SolidCircleData :: struct +{ + transform : Transform, radius : f32, rgba : RGBA8, } -SolidCircle :: struct { +SolidCircle :: struct +{ circles : [dynamic]SolidCircleData, program, vao : u32, vbo : [2]u32, @@ -758,22 +823,20 @@ SolidCircle :: struct { } -solid_circle_create :: proc(circle : ^SolidCircle) { - circle.program, _ = gl.load_shaders_source( - #load("shaders/solid_circle.vs"), - #load("shaders/solid_circle.fs"), - ) - circle.uniforms = gl.get_uniforms_from_program(circle.program) +solid_circle_create :: proc(circle : ^SolidCircle) +{ + + circle.program, _ = gl.load_shaders_source(#load("shaders/solid_circle.vs"), #load("shaders/solid_circle.fs")) + circle.uniforms = gl.get_uniforms_from_program(circle.program) gl.GenVertexArrays(1, &circle.vao) gl.GenBuffers(2, &circle.vbo[0]) - gl.BindVertexArray(circle.vao) - vertex_attribute : u32 = 0 + vertex_attribute : u32 = 0 transform_instance : u32 = 1 - radius_instance : u32 = 2 - color_instance : u32 = 3 + radius_instance : u32 = 2 + color_instance : u32 = 3 gl.EnableVertexAttribArray(vertex_attribute) gl.EnableVertexAttribArray(transform_instance) @@ -785,7 +848,7 @@ solid_circle_create :: proc(circle : ^SolidCircle) { //Vertex buffer for single quad a : f32 = 1.1 - vertices : []b2.Vec2 = { + vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, @@ -795,51 +858,20 @@ solid_circle_create :: proc(circle : ^SolidCircle) { } gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbo[0]) - gl.BufferData( - gl.ARRAY_BUFFER, - size_of(b2.Vec2) * 6, - &vertices[0], - gl.STATIC_DRAW, - ) + gl.BufferData(gl.ARRAY_BUFFER, size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // gl.BindBuffer(gl.ARRAY_BUFFER, circle.vbo[1]) - gl.BufferData( - gl.ARRAY_BUFFER, - int(batch_size * size_of(SolidCircleData)), - nil, - gl.DYNAMIC_DRAW, - ) + gl.BufferData(gl.ARRAY_BUFFER, int(batch_size * size_of(SolidCircleData)), nil, gl.DYNAMIC_DRAW) - gl.VertexAttribPointer( - transform_instance, - 4, - gl.FLOAT, - gl.FALSE, - size_of(SolidCircleData), - offset_of(SolidCircleData, transform), - ) - gl.VertexAttribPointer( - radius_instance, - 1, - gl.FLOAT, - gl.FALSE, - size_of(SolidCircleData), - offset_of(SolidCircleData, radius), - ) - gl.VertexAttribPointer( - color_instance, - 4, - gl.UNSIGNED_BYTE, - gl.TRUE, - size_of(SolidCircleData), - offset_of(SolidCircleData, rgba), - ) + gl.VertexAttribPointer(transform_instance, 4, gl.FLOAT, gl.FALSE, size_of(SolidCircleData), offset_of(SolidCircleData, transform)) + gl.VertexAttribPointer(radius_instance, 1, gl.FLOAT, gl.FALSE, size_of(SolidCircleData), offset_of(SolidCircleData, radius)) + gl.VertexAttribPointer(color_instance, 4, gl.UNSIGNED_BYTE, gl.TRUE, size_of(SolidCircleData), offset_of(SolidCircleData, rgba)) gl.VertexAttribDivisor(transform_instance, 1) - gl.VertexAttribDivisor(radius_instance, 1) - gl.VertexAttribDivisor(color_instance, 1) + gl.VertexAttribDivisor(radius_instance, 1) + gl.VertexAttribDivisor(color_instance, 1) check_opengl() @@ -865,12 +897,11 @@ solid_circle_destroy :: proc(circle : ^SolidCircle) { solid_circle_add :: proc( circle : ^SolidCircle, - transform : b2.Transform, + transform : Transform, radius : f32, - color : b2.HexColor, -) { - rgba := make_rgba(color, 1.0) - append(&circle.circles, SolidCircleData{transform, radius, rgba}) + color : RGBA8, + ) { + append(&circle.circles, SolidCircleData{transform, radius, color}) } solid_circle_flush :: proc(circle : ^SolidCircle, cam : ^Camera) { @@ -887,11 +918,11 @@ solid_circle_flush :: proc(circle : ^SolidCircle, cam : ^Camera) { 1, gl.FALSE, &proj[0][0], - ) + ) gl.Uniform1f( circle.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom, - ) + ) gl.BindVertexArray(circle.vao) @@ -909,7 +940,7 @@ solid_circle_flush :: proc(circle : ^SolidCircle, cam : ^Camera) { 0, int(batch_count * size_of(SolidCircleData)), &circle.circles[base], - ) + ) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, batch_count) check_opengl() @@ -928,7 +959,7 @@ solid_circle_flush :: proc(circle : ^SolidCircle, cam : ^Camera) { CapsuleData :: struct { - transform : b2.Transform, + transform : Transform, radius, length : f32, rgba : RGBA8, } @@ -948,7 +979,7 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { capsule.program, _ = gl.load_shaders_source( #load("shaders/solid_capsule.vs"), #load("shaders/solid_capsule.fs"), - ) + ) check_opengl() capsule.uniforms = gl.get_uniforms_from_program(capsule.program) @@ -977,7 +1008,7 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { //Vertex buffer for single quad a : f32 = 1.1 - vertices : []b2.Vec2 = { + vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, @@ -989,10 +1020,10 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { gl.BindBuffer(gl.ARRAY_BUFFER, capsule.vbo[0]) gl.BufferData( gl.ARRAY_BUFFER, - size_of(b2.Vec2) * 6, + size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW, - ) + ) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // @@ -1002,7 +1033,7 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { int(batch_size * size_of(CapsuleData)), nil, gl.DYNAMIC_DRAW, - ) + ) gl.VertexAttribPointer( transform_instance, @@ -1011,7 +1042,7 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { gl.FALSE, size_of(CapsuleData), offset_of(CapsuleData, transform), - ) + ) gl.VertexAttribPointer( radius_instance, 1, @@ -1019,7 +1050,7 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { gl.FALSE, size_of(CapsuleData), offset_of(CapsuleData, radius), - ) + ) gl.VertexAttribPointer( length_instance, 1, @@ -1027,7 +1058,7 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { gl.FALSE, size_of(CapsuleData), offset_of(CapsuleData, length), - ) + ) gl.VertexAttribPointer( color_instance, 4, @@ -1035,7 +1066,7 @@ solid_capsules_create :: proc(capsule : ^SolidCapsules) { gl.TRUE, size_of(CapsuleData), offset_of(CapsuleData, rgba), - ) + ) gl.VertexAttribDivisor(transform_instance, 1) gl.VertexAttribDivisor(radius_instance, 1) @@ -1065,10 +1096,10 @@ solid_capsules_destroy :: proc(capsule : ^SolidCapsules) { solid_capsules_add :: proc( capsule : ^SolidCapsules, - p1, p2 : b2.Vec2, + p1, p2 : [2]f32, radius : f32, c : b2.HexColor, -) { + ) { d := p2 - p1 length := b2.Length(d) @@ -1076,7 +1107,7 @@ solid_capsules_add :: proc( axis := d / length - transform : b2.Transform = { + transform : Transform = { p = 0.5 * (p1 + p2), q = {c = axis.x, s = axis.y}, } @@ -1102,11 +1133,11 @@ solid_capsules_flush :: proc(capsule : ^SolidCapsules, cam : ^Camera) { 1, gl.FALSE, &proj[0][0], - ) + ) gl.Uniform1f( capsule.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom, - ) + ) gl.BindVertexArray(capsule.vao) @@ -1119,12 +1150,7 @@ solid_capsules_flush :: proc(capsule : ^SolidCapsules, cam : ^Camera) { for count > 0 { batch_count := min(count, 2048) - gl.BufferSubData( - gl.ARRAY_BUFFER, - 0, - int(batch_count * size_of(CapsuleData)), - &capsule.capsules[base], - ) + gl.BufferSubData(gl.ARRAY_BUFFER, 0, int(batch_count * size_of(CapsuleData)), &capsule.capsules[base]) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, batch_count) check_opengl() @@ -1143,8 +1169,8 @@ solid_capsules_flush :: proc(capsule : ^SolidCapsules, cam : ^Camera) { PolygonData :: struct #packed { - transform : b2.Transform, - p1, p2, p3, p4, p5, p6, p7, p8 : b2.Vec2, + transform : Transform, + p1, p2, p3, p4, p5, p6, p7, p8 : [2]f32, count : i32, radius : f32, @@ -1165,7 +1191,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { polygon.program, _ = gl.load_shaders_source( #load("shaders/solid_polygons.vs"), #load("shaders/solid_polygons.fs"), - ) + ) batch_size : i32 = 512 @@ -1199,7 +1225,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { a : f32 = 1.1 - vertices : []b2.Vec2 = { + vertices : [][2]f32 = { {-a, -a}, {a, -a}, {-a, a}, @@ -1211,10 +1237,10 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { gl.BindBuffer(gl.ARRAY_BUFFER, polygon.vbo[0]) gl.BufferData( gl.ARRAY_BUFFER, - size_of(b2.Vec2) * 6, + size_of([2]f32) * 6, &vertices[0], gl.STATIC_DRAW, - ) + ) gl.VertexAttribPointer(vertex_attribute, 2, gl.FLOAT, gl.FALSE, 0, 0) // @@ -1224,7 +1250,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { int(batch_size * size_of(PolygonData)), nil, gl.DYNAMIC_DRAW, - ) + ) gl.VertexAttribPointer( instance_transform, @@ -1233,7 +1259,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { false, size_of(PolygonData), offset_of(PolygonData, transform), - ) + ) gl.VertexAttribPointer( instance_point12, 4, @@ -1241,7 +1267,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { false, size_of(PolygonData), offset_of(PolygonData, p1), - ) + ) gl.VertexAttribPointer( instance_point34, 4, @@ -1249,7 +1275,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { false, size_of(PolygonData), offset_of(PolygonData, p3), - ) + ) gl.VertexAttribPointer( instance_point56, 4, @@ -1257,7 +1283,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { false, size_of(PolygonData), offset_of(PolygonData, p5), - ) + ) gl.VertexAttribPointer( instance_point78, 4, @@ -1265,14 +1291,14 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { false, size_of(PolygonData), offset_of(PolygonData, p7), - ) + ) gl.VertexAttribIPointer( instance_point_count, 1, gl.INT, size_of(PolygonData), offset_of(PolygonData, count), - ) + ) gl.VertexAttribPointer( instance_radius, 1, @@ -1280,7 +1306,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { false, size_of(PolygonData), offset_of(PolygonData, radius), - ) + ) gl.VertexAttribPointer( instance_color, 4, @@ -1288,7 +1314,7 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { true, size_of(PolygonData), offset_of(PolygonData, color), - ) + ) gl.VertexAttribDivisor(instance_transform, 1) @@ -1309,12 +1335,12 @@ solid_polygon_create :: proc(polygon : ^SolidPolygon) { solid_polygon_add :: proc( polygon : ^SolidPolygon, - transform : b2.Transform, - points : [^]b2.Vec2, + transform : Transform, + points : [^][2]f32, count : i32, radius : f32, color : b2.HexColor, -) { + ) { data : PolygonData @@ -1323,7 +1349,7 @@ solid_polygon_add :: proc( n := min(count, 8) - ps := cast([^]b2.Vec2)&data.p1 + ps := cast([^][2]f32)&data.p1 for i in 0 ..< count { ps[i] = points[i] @@ -1349,14 +1375,8 @@ solid_polygon_flush :: proc(polygon : ^SolidPolygon, cam : ^Camera) { proj := camera_build_project_matrix(cam, 0.2) - //proj := linalg.MATRIX4F32_IDENTITY - gl.UniformMatrix4fv(polygon.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0]) gl.Uniform1f(polygon.uniforms["pixelScale"].location, f32(cam.height) / cam.zoom) - /* - gl.UniformMatrix4fv(polygon.uniforms["projectionMatrix"].location, 1, gl.FALSE, &proj[0][0]) - gl.Uniform1f(polygon.uniforms["pixelScale"].location, 0.001) - */ gl.BindVertexArray(polygon.vao) @@ -1389,7 +1409,15 @@ solid_polygon_flush :: proc(polygon : ^SolidPolygon, cam : ^Camera) { } -Draw :: struct { +TextItem :: struct +{ + str : string, + pos : [2]f32, + color : [4]u8, +} + +Draw :: struct +{ show_ui : bool, debug_draw : b2.DebugDraw, cam : Camera, @@ -1403,6 +1431,9 @@ Draw :: struct { drawCounters : bool, //regular_font : im.Font, frame_buffer : u32, + glyph : e2_glyph.GlyphState, + + texts : [dynamic]TextItem, } draw_aabb :: proc(draw : ^Draw, aabb : b2.AABB, c : b2.HexColor) { @@ -1419,11 +1450,11 @@ draw_aabb :: proc(draw : ^Draw, aabb : b2.AABB, c : b2.HexColor) { } DrawPolygonFcn :: proc "c" ( - vertices : [^]b2.Vec2, + vertices : [^][2]f32, vertexCount : i32, color : b2.HexColor, ctx : rawptr, -) { + ) { context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx @@ -1437,13 +1468,13 @@ DrawPolygonFcn :: proc "c" ( } DrawSolidPolygonFcn :: proc "c" ( - transform : b2.Transform, - vertices : [^]b2.Vec2, + transform : Transform, + vertices : [^][2]f32, vertexCount : i32, radius : f32, color : b2.HexColor, ctx : rawptr, -) { + ) { context = runtime.default_context() @@ -1456,16 +1487,16 @@ DrawSolidPolygonFcn :: proc "c" ( vertexCount, radius, color, - ) + ) } DrawCircleFcn :: proc "c" ( - center : b2.Vec2, + center : [2]f32, radius : f32, color : b2.HexColor, ctx : rawptr, -) { + ) { context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx circle_add(&draw.circles, center, radius, color) @@ -1473,20 +1504,22 @@ DrawCircleFcn :: proc "c" ( DrawSolidCircle :: proc( circle : ^SolidCircle, - transform : b2.Transform, - center : b2.Vec2, + transform : Transform, + center : [2]f32, radius : f32, color : b2.HexColor, -) { + ) { context = runtime.default_context() transform := transform + rgba := make_rgba(color, 1.0) + transform.p = b2.TransformPoint(transform, center) - solid_circle_add(circle, transform, radius, color) + solid_circle_add(circle, transform, radius, rgba) } -DrawTransform :: proc "c" (lines : ^Lines, transform : b2.Transform) { +DrawTransform :: proc "c" (lines : ^Lines, transform : Transform) { context = runtime.default_context() k_axis_scale : f32 = 0.2 p1 := transform.p @@ -1499,87 +1532,114 @@ DrawTransform :: proc "c" (lines : ^Lines, transform : b2.Transform) { } DrawSolidCircleFcn :: proc "c" ( - transform : b2.Transform, + transform : Transform, radius : f32, color : b2.HexColor, ctx : rawptr, -) { + ) { + context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx DrawSolidCircle( &draw.solid_circles, transform, - b2.Vec2_zero, + {0, 0}, radius, color, - ) + ) } DrawSolidCapsuleFcn :: proc "c" ( - p1, p2 : b2.Vec2, + p1, p2 : [2]f32, radius : f32, color : b2.HexColor, ctx : rawptr, -) { + ) { context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx solid_capsules_add(&draw.solid_capsules, p1, p2, radius, color) } DrawSegmentFcn :: proc "c" ( - p1, p2 : b2.Vec2, + p1, p2 : [2]f32, color : b2.HexColor, ctx : rawptr, -) { + ) { context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx lines_add(&draw.lines, p1, p2, color) } -DrawTransformFcn :: proc "c" (transform : b2.Transform, ctx : rawptr) { +DrawTransformFcn :: proc "c" (transform : Transform, ctx : rawptr) { context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx DrawTransform(&draw.lines, transform) } DrawPointFcn :: proc "c" ( - p : b2.Vec2, + p : [2]f32, size : f32, color : b2.HexColor, ctx : rawptr, -) { + ) { context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx points_add(&draw.points, p, size, color) } -DrawString :: proc(draw : ^Draw, x, y : int, str: cstring) +DrawString :: proc(draw : ^Draw, x, y : int, cstr: cstring) { + ps := camera_convert_world_to_screen(&draw.cam, {f32(x), f32(y)}) + str := strings.clone_from_cstring(cstr) + str = strings.trim_left_space(str) + str = strings.trim_right_space(str) + str = strings.trim_null(str) + append(&draw.texts, TextItem{"0.00", {f32(ps.x), f32(ps.y)}, {1,1,1,1}}) /* draw_list := im.GetForegroundDrawList() im.DrawList_AddText(draw_list, {f32(x), f32(y)},im.GetColorU32(.Text), str) */ } -DrawStringVec :: proc(draw : ^Draw, p : b2.Vec2, str: cstring) +DrawStringVec :: proc(draw : ^Draw, p : [2]f32, cstr: cstring) { - /* + + p := p + ps := camera_convert_world_to_screen(&draw.cam, p) + + str := strings.clone_from_cstring(cstr) + str = strings.trim_left_space(str) + str = strings.trim_right_space(str) + str = strings.trim_null(str) + + + append(&draw.texts, TextItem{str, {f32(ps.x), f32(ps.y)}, {200,200,200,255}}) + /* im.DrawList_AddText(im.GetForegroundDrawList(), ps,im.GetColorU32(.Text), str) */ } DrawStringFcn :: proc "c" ( - p : b2.Vec2, + p : [2]f32, s : cstring, color : b2.HexColor, ctx : rawptr, -) { + ) { context = runtime.default_context() draw : ^Draw = cast(^Draw)ctx DrawStringVec(draw, p, s) } +text_flush :: proc(draw: ^Draw) +{ + for &t in &draw.texts{ + e2_glyph.glyph_draw_font(&draw.glyph, t.str, t.pos, t.color) + } + + clear(&draw.texts) +} + draw_flush :: proc(draw : ^Draw) { background_draw(&draw.background, &draw.cam) @@ -1587,15 +1647,18 @@ draw_flush :: proc(draw : ^Draw) { solid_circle_flush(&draw.solid_circles, &draw.cam) solid_polygon_flush(&draw.polygons, &draw.cam) solid_capsules_flush(&draw.solid_capsules, &draw.cam) - circle_flush(&draw.circles, &draw.cam) lines_flush(&draw.lines, &draw.cam) points_flush(&draw.points, &draw.cam) + circle_flush(&draw.circles, &draw.cam) + text_flush(draw) + check_opengl() } -draw_create :: proc(draw : ^Draw, camera : ^Camera) { +//This should be handled in engine code separately +draw_create :: proc(draw : ^Draw, camera : ^Camera, font_path : string = "") { background_create(&draw.background) points_create(&draw.points) @@ -1604,6 +1667,17 @@ draw_create :: proc(draw : ^Draw, camera : ^Camera) { circle_create(&draw.circles) solid_circle_create(&draw.solid_circles) solid_polygon_create(&draw.polygons) + + { + draw.glyph.atlas_width = draw.cam.width + draw.glyph.atlas_height = draw.cam.height + draw.glyph.font_size_pt = 9 + draw.glyph.width = draw.cam.width + draw.glyph.height = draw.cam.height + + e2_glyph.glyph_init(&draw.glyph, font_path) + } + check_opengl() @@ -1625,7 +1699,7 @@ draw_create :: proc(draw : ^Draw, camera : ^Camera) { draw.debug_draw.drawJoints = true draw.debug_draw.drawJointExtras = false draw.debug_draw.drawBounds = false - draw.debug_draw.drawMass = false + draw.debug_draw.drawMass = true draw.debug_draw.drawContacts = false draw.debug_draw.drawGraphColors = false draw.debug_draw.drawContactNormals = false diff --git a/edit2d.odin b/edit2d.odin index 4929d4a..88b34f8 100644 --- a/edit2d.odin +++ b/edit2d.odin @@ -1,23 +1,26 @@ package edit2d +import "core:fmt" import "base:intrinsics" import "core:encoding/cbor" +import b2 "vendor:box2d" import "base:runtime" import im "shared:odin-imgui" import "shared:odin-imgui/imgui_impl_glfw" import "shared:odin-imgui/imgui_impl_opengl3" import gl "vendor:OpenGL" import "vendor:glfw" -import draw "shared:Edit2D/draw" +import e2_draw "shared:Edit2D/draw" import "core:reflect" +import mu "vendor:microui" engine_state :: struct { window : glfw.WindowHandle, - draw : draw.Draw, + draw : e2_draw.Draw, restart, pause : bool, substep_count : u32, @@ -29,7 +32,8 @@ engine_state :: struct drop_callback : glfw.DropProc, input : input_state, - + + mu_ctx : mu.Context, } MAX_KEYS :: 512 @@ -95,17 +99,24 @@ engine_check_types :: proc($Game: typeid) This will only be called once to initilize the engine initilize graphics library, glfw, callbacks */ -engine_init :: proc($GameType : typeid, state: ^engine_state) +engine_init :: proc($GameType : typeid, state: ^engine_state, font_path : string="") { - engine_check_types(GameType) assert(glfw.Init() == true) glfw.WindowHint(glfw.SCALE_TO_MONITOR, 1) - + + glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 4) + glfw.WindowHint(glfw.OPENGL_DEBUG_CONTEXT, true) + glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 5) + glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) + state.window = glfw.CreateWindow(state.width, state.height, state.title, nil, nil) + + + //gl.DebugMessageControl(gl.DONT_CARE, gl.DONT_CARE, gl.DONT_CARE, 0, nil, gl.TRUE); assert(state.window != nil) @@ -139,7 +150,7 @@ engine_init :: proc($GameType : typeid, state: ^engine_state) imgui_impl_glfw.InitForOpenGL(state.window, true) imgui_impl_opengl3.Init("#version 150") - state.draw.cam = draw.camera_init() + state.draw.cam = e2_draw.camera_init() display_w, display_h := glfw.GetFramebufferSize(state.window) state.draw.cam.width = display_w @@ -147,7 +158,7 @@ engine_init :: proc($GameType : typeid, state: ^engine_state) state.draw.cam.zoom = 15 state.draw.show_ui = true - draw.draw_create(&state.draw, &state.draw.cam) + e2_draw.draw_create(&state.draw, &state.draw.cam, font_path) cbor.tag_register_type({ marshal = proc(_: ^cbor.Tag_Implementation, e: cbor.Encoder, v: any) -> cbor.Marshal_Error { @@ -159,6 +170,16 @@ engine_init :: proc($GameType : typeid, state: ^engine_state) }, }, 201, rawptr) + + //Glyph and microui + { + + mu.init(&state.mu_ctx) + state.mu_ctx.style.font = cast(mu.Font)&state.draw.glyph + state.mu_ctx.text_width = mui_text_width + state.mu_ctx.text_height = mui_text_height + + } } @@ -168,6 +189,25 @@ update_frame :: proc(state: ^engine_state) glfw.PollEvents() keyboard_update(state) + + x, y:= glfw.GetCursorPos(state.window) + + mu.input_mouse_move(&state.mu_ctx, i32(x), i32(y)) + { + for key in key_map + { + if is_key_pressed(state, key) do mu.input_key_down(&state.mu_ctx, key_map[key]) + if is_key_released(state, key) do mu.input_key_up(&state.mu_ctx, key_map[key]) + } + + for key in mouse_map + { + if is_key_pressed(state, key) do mu.input_mouse_down(&state.mu_ctx, i32(x), i32(y), mouse_map[key]) + if is_key_released(state, key) do mu.input_mouse_up(&state.mu_ctx,i32(x), i32(y), mouse_map[key]) + } + + } + gl.ClearColor(0.4, 0.5, 0.6, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) @@ -187,6 +227,68 @@ end_frame :: proc(state: ^engine_state) { im.Render() imgui_impl_opengl3.RenderDrawData(im.GetDrawData()) + + + //Microui + { + + cmd: ^mu.Command + + draw := &state.draw + for mu.next_command(&state.mu_ctx, &cmd) + { + #partial switch c in cmd.variant{ + case ^mu.Command_Text: + { + color := c.color + append(&draw.texts, e2_draw.TextItem{c.str, {f32(c.pos.x), f32(c.pos.y)}, {c.color.r, color.g, color.b, color.a}}) + } + case ^mu.Command_Rect: + { + rect := c.rect + pos :[2]f32= {f32(rect.x), f32(rect.y)} + wh :[2]f32= {f32(rect.w), f32(rect.h)} + w, h := wh.x, wh.y + + + points : [4][2]f32 = { + {pos.x + w, pos.y}, + {pos.x, pos.y}, + {pos.x, pos.y + h}, + {pos.x + w, pos.y + h}, + } + + for &p, i in &points do points[i] = e2_draw.camera_convert_screen_to_world(&draw.cam, p) + color := c.color + hex_color := e2_draw.make_hex_color({color.r, color.g, color.b, color.a}) + e2_draw.solid_polygon_add(&draw.polygons, {q = b2.Rot{c = 1}}, &points[0], 4, 0, hex_color) + } + case ^mu.Command_Clip: + { + rect := c.rect + gl.Scissor(rect.x, state.height - (rect.y + rect.h), rect.w, rect.h) + } + case ^mu.Command_Icon:{ + + if c.id == .CHECK + { + pos :[2]f32= {f32(c.rect.x), f32(c.rect.y)} + + pos.x += f32(c.rect.w)/2.0 + pos.y += f32(c.rect.h)/2.0 + pos = e2_draw.camera_convert_screen_to_world(&draw.cam, pos) + + e2_draw.solid_circle_add(&draw.solid_circles,{p = pos, q = b2.Rot{c = 1}} , 0.1,{255, 255, 255, 255}) + }else{ + fmt.println(c.id) + } + + } + } + + } + + } glfw.SwapBuffers(state.window) } @@ -236,9 +338,9 @@ is_key_released :: #force_inline proc(state: ^engine_state, key : i32) -> bool{ } -draw_flush :: proc(d: ^draw.Draw) +draw_flush :: proc(d: ^e2_draw.Draw) { - draw.draw_flush(d) + e2_draw.draw_flush(d) } diff --git a/glyph/glyph.odin b/glyph/glyph.odin index a54922b..7ddde9d 100644 --- a/glyph/glyph.odin +++ b/glyph/glyph.odin @@ -1,5 +1,6 @@ package e2glyph +import "core:fmt" /* Provides text rendering using glfw, stb_truetype for the engine @@ -52,11 +53,13 @@ GlyphState :: struct { } -glyph_init :: proc(glyph: ^GlyphState, filepath : string) { +glyph_init :: proc(glyph: ^GlyphState, font_path : string = "") { ok : bool glyph.program_id, ok = gl.load_shaders_source(#load("./shaders/font_vert.glsl"), #load("./shaders/font_frag.glsl"), ) + gl.UseProgram(glyph.program_id) + gl.CreateBuffers(1, &glyph.rect_instances_vbo) gl.CreateVertexArrays(1, &glyph.vao) @@ -82,9 +85,18 @@ glyph_init :: proc(glyph: ^GlyphState, filepath : string) { gl.CreateTextures(gl.TEXTURE_RECTANGLE, 1, &glyph.atlas_texture) gl.TextureStorage2D(glyph.atlas_texture, 1, gl.RGB8, glyph.atlas_width, glyph.atlas_height) - font_data, _ := os.read_entire_file_from_path(filepath, context.allocator) - stbtt.InitFont(&glyph.font_info, &font_data[0], 0) + if !os.exists(font_path) + { + fmt.eprintln("Font not provided or not exist using default font") + font_data := #load("./mononoki-Regular.ttf", []byte) + stbtt.InitFont(&glyph.font_info, &font_data[0], 0) + }else + { + font_data, _ := os.read_entire_file_from_path(font_path, context.allocator) + stbtt.InitFont(&glyph.font_info, &font_data[0], 0) + } + gl.UseProgram(0) } @@ -98,6 +110,7 @@ glyph_draw_font :: proc(glyph_state: ^GlyphState, text: string, pos : [2]f32, co coverage_adjustment: f32 = 0.0 text_color: [4]f32 = {f32( color.r)/ 255.0 ,f32(color.g)/255.0, f32(color.b)/255.0, f32(color.a)/255.0} + glyph_state.rect_buffer = make([dynamic]GlyphRectInstance, 0, 1000) { //put every glyph in text into rect_buffer @@ -138,13 +151,15 @@ glyph_draw_font :: proc(glyph_state: ^GlyphState, text: string, pos : [2]f32, co glyph_state.curr.y += math.round(line_height) } else if c == '\t' { - glyph_state.curr.x += 2 * glyph_state.font_size_pt } else { horizontal_filter_padding, subpixel_positioning_left_padding: i32 = 1, 1 - assert(codepoint <= 127) + if codepoint >= 127{ + return + } + assert(codepoint < 127) glyph_atlas := glyph_state.atlas_items[codepoint] if !glyph_atlas.filled @@ -283,17 +298,19 @@ glyph_draw_font :: proc(glyph_state: ^GlyphState, text: string, pos : [2]f32, co gl.Enable(gl.BLEND) gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC1_COLOR) - gl.BindVertexArray(glyph_state.vao) - gl.UseProgram(glyph_state.program_id) - gl.ProgramUniform2f(glyph_state.program_id, 0, f32(glyph_state.width) / 2.0, f32(glyph_state.height) / 2.0) - gl.ProgramUniform1ui(glyph_state.program_id, 1, u32(coverage_adjustment)) - gl.ProgramUniform1ui(glyph_state.program_id, 2, 0) - gl.BindTextureUnit(0, glyph_state.atlas_texture) + gl.UseProgram( glyph_state.program_id) + gl.BindVertexArray( glyph_state.vao) + gl.ProgramUniform2f( glyph_state.program_id, 0, f32(glyph_state.width) / 2.0, f32(glyph_state.height) / 2.0) + gl.ProgramUniform1ui( glyph_state.program_id, 1, u32(coverage_adjustment)) + gl.ProgramUniform1ui( glyph_state.program_id, 2, 0) + gl.BindTextureUnit(0, glyph_state.atlas_texture) gl.DrawArraysInstanced(gl.TRIANGLES, 0, 6, i32(len(glyph_state.rect_buffer))) + gl.Disable(gl.BLEND) - gl.UseProgram(0) - gl.BindVertexArray(0) + + //gl.UseProgram(0) + //gl.BindVertexArray(0) gl.InvalidateBufferData(glyph_state.rect_instances_vbo) } diff --git a/glyph/mononoki-Regular.ttf b/glyph/mononoki-Regular.ttf new file mode 100644 index 0000000..084c49d Binary files /dev/null and b/glyph/mononoki-Regular.ttf differ diff --git a/interface.odin b/interface.odin index f2ab33c..40836bc 100644 --- a/interface.odin +++ b/interface.odin @@ -1,15 +1,23 @@ +#+feature dynamic-literals + package edit2d import im "shared:odin-imgui" +import mu "vendor:microui" +import e2_glyph "./glyph" import "core:fmt" +import "vendor:glfw" import b2 "vendor:box2d" /* -This library will only account for box2d's entities editing + This library will only account for box2d's entities editing -It only deals with one world_id, which means typically one level + It only deals with one world_id, which means typically one level -All the interface follows a pattern -i.e. It takes interface_state pointer and returns a boolean indicating weather the world needs to be reloaded + All the interface follows a pattern + i.e. It takes interface_state pointer and returns a boolean indicating weather the world needs to be reloaded + + + Interface should handle microui */ @@ -41,6 +49,51 @@ interface_state :: struct curr_static_index : static_index_global, } + + +key_map : map[i32]mu.Key = { + glfw.KEY_LEFT_SHIFT = .SHIFT, + glfw.KEY_RIGHT_SHIFT = .SHIFT, + glfw.KEY_LEFT_CONTROL = .CTRL, + glfw.KEY_RIGHT = .CTRL, + glfw.KEY_LEFT_ALT = .ALT, + glfw.KEY_RIGHT_ALT = .ALT, + glfw.KEY_BACKSPACE = .BACKSPACE, + glfw.KEY_DELETE = .DELETE, + glfw.KEY_ENTER = .RETURN, + glfw.KEY_LEFT = .LEFT, + glfw.KEY_RIGHT = .RIGHT, + glfw.KEY_HOME = .HOME, + glfw.KEY_END = .END, + glfw.KEY_A = .A, + glfw.KEY_X = .X, + glfw.KEY_C = .C, + glfw.KEY_V = .V +} + +mouse_map : map[i32]mu.Mouse = { + glfw.MOUSE_BUTTON_LEFT = .LEFT, + glfw.MOUSE_BUTTON_RIGHT = .RIGHT, + glfw.MOUSE_BUTTON_MIDDLE= .MIDDLE, +} + + + +//interface_get_font + +mui_text_width :: proc(font: mu.Font, str: string) -> i32 +{ + glyph := cast(^e2_glyph.GlyphState)font + return i32(len(str)) * i32(glyph.font_size_pt) +} + +mui_text_height :: proc(font: mu.Font) -> i32 +{ + glyph := cast(^e2_glyph.GlyphState)font + return i32(glyph.font_size_pt) +} + + interface_get_default :: proc(interface: ^interface_state) { interface.selected_entity = 0 @@ -50,7 +103,8 @@ interface_get_default :: proc(interface: ^interface_state) interface.chain_index^ = 0 } -interface_draw_options :: proc(state: ^engine_state) { +interface_draw_options :: proc(state: ^engine_state) +{ debug_draw := &state.draw.debug_draw im.SliderFloat("Zoom", &state.draw.cam.zoom, 0, 100) @@ -70,6 +124,48 @@ interface_draw_options :: proc(state: ^engine_state) { im.SliderFloat("Rotation", &state.draw.cam.rotation, 0, 360) } +mu_interface_game_mode :: proc(state: ^engine_state, interface: ^interface_state) +{ + if mu.begin_window(&state.mu_ctx, "Edit Mode", {1700, 0, 250, 170}) + { + for type in EditMode + { + b : bool = interface.edit_mode == type + if .CHANGE in mu.checkbox(&state.mu_ctx, fmt.tprint(type), &b) do interface.edit_mode = type + } + mu.end_window(&state.mu_ctx) + } +} + +mu_interface_draw_options :: proc(state: ^engine_state) +{ + if mu.begin_window(&state.mu_ctx, "Options", {200, 150, 200, 400}){ + debug_draw := &state.draw.debug_draw + + mu.label(&state.mu_ctx, "Zoom") + mu.slider(&state.mu_ctx, &state.draw.cam.zoom,0, 100) + + mu.checkbox(&state.mu_ctx, "Shapes", &debug_draw.drawShapes) + mu.checkbox(&state.mu_ctx, "Joints", &debug_draw.drawJoints) + mu.checkbox(&state.mu_ctx, "Joint Extras", &debug_draw.drawJointExtras) + mu.checkbox(&state.mu_ctx, "Bounds", &debug_draw.drawBounds) + mu.checkbox(&state.mu_ctx, "Contact Points", &debug_draw.drawContacts) + mu.checkbox(&state.mu_ctx, "Contact Normals", &debug_draw.drawContactNormals) + mu.checkbox(&state.mu_ctx, "Contact Inpulses", &debug_draw.drawContactImpulses) + mu.checkbox(&state.mu_ctx, "Contact Features", &debug_draw.drawContactFeatures) + mu.checkbox(&state.mu_ctx, "Friction Inpulses", &debug_draw.drawFrictionImpulses) + mu.checkbox(&state.mu_ctx, "Mass ", &debug_draw.drawMass) + mu.checkbox(&state.mu_ctx, "Body Names", &debug_draw.drawBodyNames) + mu.checkbox(&state.mu_ctx, "Graph Colors", &debug_draw.drawGraphColors) + mu.checkbox(&state.mu_ctx, "Islands ", &debug_draw.drawIslands) + + mu.label(&state.mu_ctx, "Rotation") + mu.slider(&state.mu_ctx, &state.draw.cam.rotation, 0, 360) + + mu.end_window(&state.mu_ctx) + } +} + interface_all :: proc(interface: ^interface_state) -> bool { @@ -79,27 +175,26 @@ interface_all :: proc(interface: ^interface_state) -> bool interface.selected_entity = 0 } - if im.Begin("Edit Mode", nil) - { - for type in EditMode - { - if im.RadioButton(fmt.ctprint(type), interface.edit_mode == type) - { - interface.edit_mode = type - } - } - } - im.End() + state := interface.state + mu.begin(&state.mu_ctx) + + //test_window(&state.mu_ctx) + mu_interface_draw_options(interface.state) + mu_interface_game_mode(state, interface) if im.Begin("Box2d interface") { if im.BeginTabBar("Tabs") { - if im.BeginTabItem("Entity", nil, {.Leading}) + if mu.begin_window(&state.mu_ctx, "B2d Interface", {1700, 150, 250, 250}) { - if interface_entity(interface) do ret = true - im.EndTabItem() + if .ACTIVE in mu.header(&state.mu_ctx, "Entity", {.AUTO_SIZE}) + { + if interface_entity(interface) do ret = true + + } + mu.end_window(&state.mu_ctx) } if im.BeginTabItem("Joints", nil, {}) @@ -108,15 +203,10 @@ interface_all :: proc(interface: ^interface_state) -> bool im.EndTabItem() } - if im.BeginTabItem("Draw Options", nil, {}) - { - interface_draw_options(interface.state) - im.EndTabItem() - } - im.EndTabBar() } } + mu.end(&state.mu_ctx) im.End() return ret } diff --git a/interface_entity.odin b/interface_entity.odin index 07f0c6c..0d8d53d 100644 --- a/interface_entity.odin +++ b/interface_entity.odin @@ -2,6 +2,7 @@ package edit2d import b2 "vendor:box2d" import im "shared:odin-imgui" +import mu "vendor:microui" import "core:slice" import draw "./draw" import "core:fmt" @@ -122,6 +123,92 @@ interface_edit_static_index :: proc(interface:^interface_state, def: ^engine_ent return false } +mu_interface_shape_def_editor :: proc( + state : ^engine_state, + def : ^engine_entity_def) -> bool +{ + shape_def := &def.shape_def + + mu.label(&state.mu_ctx, "Shape Type") + for type in b2.ShapeType + { + b := def.shape_type == type + if .CHANGE in mu.checkbox(&state.mu_ctx, fmt.tprint(type), &b) do def.shape_type = type + } + + switch def.shape_type + { + case .circleShape:{ + mu.label(&state.mu_ctx, "Radius") + mu.slider(&state.mu_ctx, &def.radius, 0, 40) + } + + case .polygonShape:{ + mu.label(&state.mu_ctx, "Size") + mu.slider(&state.mu_ctx, &def.size[0], -500, 500) + mu.slider(&state.mu_ctx, &def.size[1], -500, 500) + } + + case .capsuleShape:{ + mu.label(&state.mu_ctx, "Center 1") + mu.slider(&state.mu_ctx, &def.centers[0][0], -100, 100) + mu.slider(&state.mu_ctx, &def.centers[0][1], -100, 100) + mu.label(&state.mu_ctx, "Center 2") + mu.slider(&state.mu_ctx, &def.centers[1][0], -100, 100) + mu.slider(&state.mu_ctx, &def.centers[1][1], -100, 100) + mu.label(&state.mu_ctx, "Radius") + mu.slider(&state.mu_ctx, &def.radius, 0, 40) + } + + case .chainSegmentShape:{ + mu.checkbox(&state.mu_ctx, "Is Loop", &def.is_loop) + } + case .segmentShape:{ + + } + } + + mu.label(&state.mu_ctx, "Density") + mu.slider(&state.mu_ctx, &def.shape_def.density, 0, 100) + + if .SUBMIT in mu.button(&state.mu_ctx, "Flip horizontally") do flip_points(def.vertices[:], .Horizontal) + if .SUBMIT in mu.button(&state.mu_ctx, "Flip Vertically ") do flip_points(def.vertices[:], .Vertical) + + if .ACTIVE in mu.begin_treenode(&state.mu_ctx, "Events and contacts") + { + mu.checkbox(&state.mu_ctx, "Is sensor", &def.shape_def.isSensor) + mu.checkbox(&state.mu_ctx, "Enable Sensor Events", &def.shape_def.enableSensorEvents) + mu.checkbox(&state.mu_ctx, "Enable Contact Events", &def.shape_def.enableContactEvents) + mu.checkbox(&state.mu_ctx, "Enable Hit Events", &def.shape_def.enableHitEvents) + mu.checkbox(&state.mu_ctx, "Enable Presolve Events", &def.shape_def.enablePreSolveEvents) + mu.checkbox(&state.mu_ctx, "Invoke contact Creation", &def.shape_def.invokeContactCreation) + mu.checkbox(&state.mu_ctx, "Update body mass ", &def.shape_def.updateBodyMass) + + mu.end_treenode(&state.mu_ctx) + } + + if .ACTIVE in mu.begin_treenode(&state.mu_ctx, "Material") + { + mu.label(&state.mu_ctx, "Friction") + mu.slider(&state.mu_ctx, &def.shape_def.material.friction, 0, 1) + + mu.label(&state.mu_ctx, "Restitution") + mu.slider(&state.mu_ctx, &def.shape_def.material.restitution, 0, 1) + + mu.label(&state.mu_ctx, "Rolling Resistance") + mu.slider(&state.mu_ctx, &def.shape_def.material.rollingResistance, 0, 1) + + mu.label(&state.mu_ctx, "Tangent Speed") + mu.slider(&state.mu_ctx, &def.shape_def.material.tangentSpeed, 0, 1) + + //if .ACTIVE in mu.begin_treenode(&state.mu_ctx, "Color") + + mu.end_treenode(&state.mu_ctx) + } + + return false +} + interface_shape_def_editor :: proc(def: ^engine_entity_def) -> bool { @@ -211,7 +298,6 @@ interface_shape_def_editor :: proc(def: ^engine_entity_def) -> bool im.Separator() im.TreePop() } - return false } @@ -238,8 +324,9 @@ interface_entity :: proc(interface: ^interface_state) -> bool if im.CollapsingHeader("Shape Edit") { - interface_shape_def_editor(def) + //interface_shape_def_editor(def) } + mu_interface_shape_def_editor(interface.state, def) im.Separator()