Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
'
329 lines
12 KiB
JavaScript
329 lines
12 KiB
JavaScript
var graphics = this
|
||
|
||
graphics[prosperon.DOC] = `
|
||
Provides functionality for loading and managing images, fonts, textures, and sprite meshes.
|
||
Includes both JavaScript and C-implemented routines for creating geometry buffers, performing
|
||
rectangle packing, etc.
|
||
`
|
||
|
||
var io = use('io')
|
||
var res = use('resources')
|
||
var render = use('render')
|
||
|
||
function calc_image_size(img) {
|
||
if (!img.texture || !img.rect) return
|
||
return [img.texture.width * img.rect.width, img.texture.height * img.rect.height]
|
||
}
|
||
|
||
/**
|
||
Internally loads image data from disk and prepares a GPU texture. Used by graphics.texture().
|
||
Not intended for direct user calls.
|
||
*/
|
||
function create_image(path) {
|
||
try {
|
||
var data = io.slurpbytes(path);
|
||
var newimg;
|
||
|
||
switch (path.ext()) {
|
||
case 'gif':
|
||
newimg = graphics.make_gif(data);
|
||
if (newimg.surface)
|
||
render.load_texture(newimg)
|
||
else {
|
||
for (var frame of newimg.frames)
|
||
render.load_texture(frame)
|
||
}
|
||
break;
|
||
|
||
case 'ase':
|
||
case 'aseprite':
|
||
newimg = graphics.make_aseprite(data);
|
||
if (newimg.surface)
|
||
render.load_texture(newimg)
|
||
else {
|
||
for (var anim in newimg) {
|
||
var a = newimg[anim];
|
||
for (var frame of a.frames)
|
||
render.load_texture(frame)
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
newimg = {
|
||
surface: graphics.make_texture(data)
|
||
};
|
||
render.load_texture(newimg)
|
||
break;
|
||
}
|
||
|
||
return newimg;
|
||
|
||
} catch (e) {
|
||
// Add the path to the error message for better debugging
|
||
console.error(`Error loading image from path: ${path}`);
|
||
console.error(e.message);
|
||
if (e.stack) {
|
||
console.error(e.stack);
|
||
}
|
||
// Optionally, you can throw the error again to let it propagate
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
|
||
var image = {}
|
||
image.dimensions = function() {
|
||
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]])
|
||
}
|
||
image.dimensions[prosperon.DOC] = `
|
||
:return: A 2D array [width, height] that is the scaled size of this image (texture size * rect size).
|
||
`
|
||
|
||
var spritesheet
|
||
var sheet_frames = []
|
||
var sheetsize = 1024
|
||
|
||
/**
|
||
Pack multiple images into a single texture sheet for efficiency.
|
||
Currently unimplemented (returns immediately).
|
||
*/
|
||
function pack_into_sheet(images) {
|
||
return
|
||
// This code is currently disabled with an immediate return.
|
||
// Implementation details commented out below.
|
||
}
|
||
|
||
graphics.is_image = function(obj) {
|
||
if (obj.texture && obj.rect) return true
|
||
}
|
||
graphics.is_image[prosperon.DOC] = `
|
||
:param obj: An object to check.
|
||
:return: True if 'obj' has a .texture and a .rect property, indicating it's an image object.
|
||
`
|
||
|
||
graphics.texture = function texture(path) {
|
||
if (typeof path !== 'string') {
|
||
return path // fallback if already an image object
|
||
throw new Error('need a string for graphics.texture')
|
||
}
|
||
var parts = path.split(':')
|
||
var ipath = res.find_image(parts[0])
|
||
graphics.texture.cache[ipath] ??= create_image(ipath)
|
||
return graphics.texture.cache[ipath]
|
||
}
|
||
graphics.texture[prosperon.DOC] = `
|
||
:param path: A string path to an image file or an already-loaded image object.
|
||
:return: An image object with {surface, texture, frames?, etc.} depending on the format.
|
||
Load or retrieve a cached image, converting it into a GPU texture. If 'path' is already an object, it’s returned directly.
|
||
`
|
||
|
||
graphics.texture.cache = {}
|
||
graphics.texture.time_cache = {}
|
||
|
||
graphics.texture.total_size = function() {
|
||
var size = 0
|
||
// Not yet implemented, presumably sum of (texture.width * texture.height * 4) for images in RAM
|
||
return size
|
||
}
|
||
graphics.texture.total_size[prosperon.DOC] = `
|
||
:return: The total estimated memory size of all cached textures in RAM, in bytes. (Not yet implemented.)
|
||
`
|
||
|
||
graphics.texture.total_vram = function() {
|
||
var vram = 0
|
||
// Not yet implemented, presumably sum of GPU memory usage
|
||
return vram
|
||
}
|
||
graphics.texture.total_vram[prosperon.DOC] = `
|
||
:return: The total estimated GPU memory usage of all cached textures, in bytes. (Not yet implemented.)
|
||
`
|
||
|
||
graphics.tex_hotreload = function tex_hotreload(file) {
|
||
console.log(`hot reloading ${file}`)
|
||
if (!(file in graphics.texture.cache)) return
|
||
console.log('really doing it')
|
||
|
||
var img = create_image(file)
|
||
var oldimg = graphics.texture.cache[file]
|
||
console.log(`new image:${json.encode(img)}`)
|
||
console.log(`old image: ${json.encode(oldimg)}`)
|
||
|
||
merge_objects(oldimg, img, ['surface', 'texture', 'loop', 'time'])
|
||
}
|
||
graphics.tex_hotreload[prosperon.DOC] = `
|
||
:param file: The file path that was changed on disk.
|
||
:return: None
|
||
Reload the image for the given file, updating the cached copy in memory and GPU.
|
||
`
|
||
|
||
/**
|
||
Merges specific properties from nv into ov, using an array of property names.
|
||
*/
|
||
function merge_objects(ov, nv, arr) {
|
||
arr.forEach(x => ov[x] = nv[x])
|
||
}
|
||
|
||
/**
|
||
Unimplemented function for creating a spritesheet out of multiple images.
|
||
*/
|
||
function make_spritesheet(paths, width, height) {
|
||
return
|
||
}
|
||
|
||
/**
|
||
Stores previously loaded fonts. Keyed by e.g. "path.ttf.16" -> fontObject.
|
||
*/
|
||
var fontcache = {}
|
||
var datas = []
|
||
|
||
graphics.get_font = function get_font(path, size) {
|
||
var parts = path.split('.')
|
||
if (!isNaN(parts[1])) {
|
||
path = parts[0]
|
||
size = Number(parts[1])
|
||
}
|
||
var fullpath = res.find_font(path)
|
||
if (!fullpath) throw new Error(`Cannot load font ${path}`)
|
||
|
||
var fontstr = `${fullpath}.${size}`
|
||
if (fontcache[fontstr]) return fontcache[fontstr]
|
||
|
||
var data = io.slurpbytes(fullpath)
|
||
fontcache[fontstr] = graphics.make_font(data, size)
|
||
render.load_texture(fontcache[fontstr])
|
||
return fontcache[fontstr]
|
||
}
|
||
graphics.get_font[prosperon.DOC] = `
|
||
:param path: A string path to a font file, optionally with ".size" appended.
|
||
:param size: Pixel size of the font, if not included in 'path'.
|
||
:return: A font object with .surface and .texture for rendering text.
|
||
Load a font from file if not cached, or retrieve from cache if already loaded.
|
||
`
|
||
|
||
graphics.queue_sprite_mesh = function(queue) {
|
||
var sprites = queue.filter(x => x.type === 'sprite')
|
||
if (sprites.length === 0) return []
|
||
var mesh = graphics.make_sprite_mesh(sprites)
|
||
for (var i = 0; i < sprites.length; i++) {
|
||
sprites[i].mesh = mesh
|
||
sprites[i].first_index = i*6
|
||
sprites[i].num_indices = 6
|
||
}
|
||
return [mesh.pos, mesh.uv, mesh.color, mesh.indices]
|
||
}
|
||
graphics.queue_sprite_mesh[prosperon.DOC] = `
|
||
:param queue: An array of draw commands, some of which are {type:'sprite'} objects.
|
||
:return: An array of references to GPU buffers [pos,uv,color,indices].
|
||
Builds a single geometry mesh for all sprite-type commands in the queue, storing first_index/num_indices
|
||
so they can be rendered in one draw call.
|
||
`
|
||
|
||
graphics.make_sprite_mesh[prosperon.DOC] = `
|
||
:param sprites: An array of sprite objects, each containing .rect (or transform), .src (UV region), .color, etc.
|
||
:param oldMesh (optional): An existing mesh object to reuse/resize if possible.
|
||
:return: A GPU mesh object with pos, uv, color, and indices buffers for all sprites.
|
||
Given an array of sprites, build a single geometry mesh for rendering them.
|
||
`
|
||
|
||
graphics.make_sprite_queue[prosperon.DOC] = `
|
||
:param sprites: An array of sprite objects.
|
||
:param camera: (unused in the C code example) Typically a camera or transform for sorting?
|
||
:param pipeline: A pipeline object for rendering.
|
||
:param sort: An integer or boolean for whether to sort sprites; if truthy, sorts by layer & texture.
|
||
:return: An array of pipeline commands: geometry with mesh references, grouped by image.
|
||
Given an array of sprites, optionally sort them, then build a queue of pipeline commands.
|
||
Each group with a shared image becomes one command.
|
||
`
|
||
|
||
graphics.make_text_buffer[prosperon.DOC] = `
|
||
:param text: The string to render.
|
||
:param rect: A rectangle specifying position and possibly wrapping.
|
||
:param angle: Rotation angle (unused or optional).
|
||
:param color: A color for the text (could be a vec4).
|
||
:param wrap: The width in pixels to wrap text, or 0 for no wrap.
|
||
:param font: A font object created by graphics.make_font or graphics.get_font.
|
||
:return: A geometry buffer mesh (pos, uv, color, indices) for rendering text.
|
||
Generate a GPU buffer mesh of text quads for rendering with a font, etc.
|
||
`
|
||
|
||
graphics.rectpack[prosperon.DOC] = `
|
||
:param width: The width of the area to pack into.
|
||
:param height: The height of the area to pack into.
|
||
:param sizes: An array of [w,h] pairs for the rectangles to pack.
|
||
:return: An array of [x,y] coordinates placing each rect, or null if they don't fit.
|
||
Perform a rectangle packing using the stbrp library. Return positions for each rect.
|
||
`
|
||
|
||
graphics.make_rtree[prosperon.DOC] = `
|
||
:return: An R-Tree object for quickly querying many rectangles or sprite bounds.
|
||
Create a new R-Tree for geometry queries.
|
||
`
|
||
|
||
graphics.make_texture[prosperon.DOC] = `
|
||
:param data: Raw image bytes (PNG, JPG, etc.) as an ArrayBuffer.
|
||
:return: An SDL_Surface object representing the decoded image in RAM, for use with GPU or software rendering.
|
||
Convert raw image bytes into an SDL_Surface object.
|
||
`
|
||
|
||
graphics.make_gif[prosperon.DOC] = `
|
||
:param data: An ArrayBuffer containing GIF data.
|
||
:return: An object with frames[], each frame having its own .surface. Some also have a .texture for GPU use.
|
||
Load a GIF, returning its frames. If it's a single-frame GIF, the result may have .surface only.
|
||
`
|
||
|
||
graphics.make_aseprite[prosperon.DOC] = `
|
||
:param data: An ArrayBuffer containing Aseprite (ASE) file data.
|
||
:return: An object containing frames or animations, each with .surface. May also have top-level .surface for a single-layer case.
|
||
Load an Aseprite/ASE file from an array of bytes, returning frames or animations.
|
||
`
|
||
|
||
graphics.cull_sprites[prosperon.DOC] = `
|
||
:param sprites: An array of sprite objects (each has rect or transform).
|
||
:param camera: A camera or bounding rectangle defining the view area.
|
||
:return: A new array of sprites that are visible in the camera's view.
|
||
Filter an array of sprites to only those visible in the provided camera’s view.
|
||
`
|
||
|
||
graphics.rects_to_sprites[prosperon.DOC] = `
|
||
:param rects: An array of rect coords or objects.
|
||
:param image: An image object (with .texture).
|
||
:return: An array of sprite objects referencing the 'image' and each rect for UV or position.
|
||
Convert an array of rect coords into sprite objects referencing a single image.
|
||
`
|
||
|
||
graphics.make_surface[prosperon.DOC] = `
|
||
:param dimensions: The size object {width, height}, or an array [w,h].
|
||
:return: A blank RGBA surface with the given dimensions, typically for software rendering or icons.
|
||
Create a blank surface in RAM.
|
||
`
|
||
|
||
graphics.make_cursor[prosperon.DOC] = `
|
||
:param opts: An object with {surface, hotx, hoty} or similar.
|
||
:return: An SDL_Cursor object referencing the given surface for a custom mouse cursor.
|
||
`
|
||
|
||
graphics.make_font[prosperon.DOC] = `
|
||
:param data: TTF/OTF file data as an ArrayBuffer.
|
||
:param size: Pixel size for rendering glyphs.
|
||
:return: A font object with surface, texture, and glyph data, for text rendering with make_text_buffer.
|
||
Load a font from TTF/OTF data at the given size.
|
||
`
|
||
|
||
graphics.make_sprite[prosperon.DOC] = `
|
||
:return: A new sprite object, which typically has .rect, .color, .layer, .image, etc.
|
||
Create a new sprite object, storing default properties.
|
||
`
|
||
|
||
graphics.make_line_prim[prosperon.DOC] = `
|
||
:param points: An array of [x,y] points forming the line.
|
||
:param thickness: The thickness (width) of the polyline.
|
||
:param startCap: (Unused) Possibly the type of cap for the start.
|
||
:param endCap: (Unused) Possibly the type of cap for the end.
|
||
:param color: A color to apply to the line.
|
||
:return: A geometry mesh object suitable for rendering the line via a pipeline command.
|
||
Build a GPU mesh representing a thick polyline from an array of points, using parsl or a similar library under the hood.
|
||
`
|
||
|
||
return graphics
|