Skip to content

Instantly share code, notes, and snippets.

@Tekkunsan
Last active January 12, 2024 17:31
Show Gist options
  • Save Tekkunsan/4f41b1cf41acd6a69f30acacc43f5176 to your computer and use it in GitHub Desktop.
Save Tekkunsan/4f41b1cf41acd6a69f30acacc43f5176 to your computer and use it in GitHub Desktop.
Basic Ogmo Loader for Odin
/// Bindings for Ogmo3
package ogmo
import "core:encoding/json"
import "core:log"
import "core:os"
loadProject :: proc(filename: string, allocator := context.allocator) -> (res: Project, ok: bool) {
data, dOk := os.read_entire_file(filename, allocator)
if !dOk {
log.error("Failed to load:", filename)
return
}
return loadProjectFromMemory(data, allocator)
}
loadProjectFromMemory :: proc(
data: []byte,
allocator := context.allocator,
) -> (
res: Project,
ok: bool,
) {
err := json.unmarshal(data, &res, json.DEFAULT_SPECIFICATION, allocator)
if err != nil {
log.error("Failed to parse project json:", err)
return
}
return res, true
}
loadLevel :: proc(filename: string, allocator := context.allocator) -> (res: Level, ok: bool) {
data, dOk := os.read_entire_file(filename, allocator)
if !dOk {
log.error("Failed to load:", filename)
return
}
return loadLevelFromMemory(data, allocator)
}
loadLevelFromMemory :: proc(
data: []byte,
allocator := context.allocator,
) -> (
res: Level,
ok: bool,
) {
err := json.unmarshal(data, &res, json.DEFAULT_SPECIFICATION, allocator)
if err != nil {
log.error("Failed to parse project json:", err)
return
}
return res, true
}
Project :: struct {
name: string,
ogmoVersion: string,
levelPaths: []string,
backgroundColor: string,
gridColor: string,
anglesRadians: bool,
directoryDepth: u32,
layerGridDefaultSize: Point2,
levelDefaultSize: Point2,
levelMinSize: Point2,
levelMaxSize: Point2,
layers: []ProjectLayer,
entities: []ProjectEntity,
tilesets: []Tileset,
}
ProjectLayer :: struct {
definition: string,
name: string,
gridSize: Point2,
exportID: string,
exportMode: Maybe(u32),
arrayMode: Maybe(u32),
defaultTileset: Maybe(string),
}
ProjectEntity :: struct {
name: string,
limit: int,
size: Point2,
origin: Point2,
shape: Shape,
color: string,
rotatable: bool,
tags: []string,
values: []Value,
exportID: string,
originAnchored: bool,
tileX: bool,
tileY: bool,
tileSize: Point2,
resizeableX: bool,
resizeableY: bool,
rotationDegrees: int,
canFlipX: bool,
canFlipY: bool,
canSetColor: bool,
hasNodes: bool,
nodeLimit: u32,
nodeDisplay: u32,
nodeGhost: bool,
}
Shape :: struct {
label: string,
points: []Point2,
}
Value :: struct {
name: string,
definition: string,
defaults: string,
bounded: Maybe(bool),
min: Maybe(u32),
max: Maybe(u32),
trimWhitespace: Maybe(bool),
maxLength: Maybe(u32),
}
Tileset :: struct {
label: string,
path: string,
tileWidth: u32,
tileHeight: u32,
tileSeparationX: u32,
tileSeparationY: u32,
}
Level :: struct {
ogmoVersion: string,
width: u32,
height: u32,
offsetX: u32,
offsetY: u32,
layers: []LevelLayer,
}
LevelLayer :: struct {
name: string,
entities: Maybe([]LevelEntity),
tileset: Maybe(string),
data: Maybe([]i32),
_eid: string,
offsetX: u32,
offsetY: u32,
gridCellWidth: u32,
gridCellHeight: u32,
gridCellsX: u32,
gridCellsY: u32,
exportMode: Maybe(u32),
arrayMode: Maybe(u32),
}
LevelEntity :: struct {
name: string,
id: u32,
_eid: string,
x: int,
y: int,
originX: int,
originY: int,
values: Maybe(map[string]string),
}
Point2 :: struct {
x, y: i32,
}
package ogmo
import "core:log"
import "core:strings"
import rl "vendor:raylib"
lvl_drawToTexture :: proc(project: Project, level: Level) -> (res: rl.RenderTexture2D) {
res = rl.LoadRenderTexture(i32(level.width), i32(level.height))
rl.BeginTextureMode(res)
rl.ClearBackground(rl.BLANK)
for levelLayer in level.layers {
if lvlData, ok := levelLayer.data.?; ok {
assert(levelLayer.arrayMode.? == 0, "Level's Array mode must be 1D")
// Yeah... THis leaks...
tilesetTexture, found := getTilesetTexture(project, levelLayer.tileset.?)
assert(found, "Failed to found the tileset")
// defer rl.UnloadTexture(tilesetTexture)
draw(
project,
level.width,
level.height,
levelLayer.gridCellWidth,
levelLayer.gridCellHeight,
lvlData,
tilesetTexture,
)
} else do continue
}
rl.EndTextureMode()
return res
}
lvl_getSrcAndDest :: proc(
levelTexture: rl.RenderTexture2D,
x, y: f32,
) -> (
src, dest: rl.Rectangle,
) {
src = {0, 0, f32(levelTexture.texture.width), -f32(levelTexture.texture.height)}
dest = {x, y, f32(levelTexture.texture.width), f32(levelTexture.texture.height)}
return
}
@(private)
draw :: proc(
project: Project,
width, height, cellSizeW, cellSizeH: u32,
lvlData: []i32,
tilesetTexture: rl.Texture2D,
) {
w := width / cellSizeW
h := height / cellSizeH
for y in 0 ..< h {
for x in 0 ..< w {
data := lvlData[x + w * y]
srcRect: rl.Rectangle
srcRect.width = f32(cellSizeW)
srcRect.height = f32(cellSizeH)
switch data {
case -1 ..= 0:
continue
case:
// TODO: This will definetely not work all the time please future proof this
srcRect.x = f32(data * i32(cellSizeW))
}
log.info(srcRect)
destRect := rl.Rectangle {
f32(x * cellSizeW),
f32(y * cellSizeH),
f32(cellSizeW),
f32(cellSizeH),
}
rl.DrawTexturePro(tilesetTexture, srcRect, destRect, {0, 0}, 0, rl.WHITE)
}
}
}
@(private)
getTilesetTexture :: proc(project: Project, label: string) -> (res: rl.Texture2D, found: bool) {
for tileset in project.tilesets {
if tileset.label == label {
concat := strings.concatenate({"assets/data/", tileset.path}, context.temp_allocator)
cStr := strings.clone_to_cstring(concat, context.temp_allocator)
res = rl.LoadTexture(cStr)
return res, true
}
}
return res, false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment