Documentation and some API cleaning
- Fleshing out Readme - Renaming pong to breakout - Renaming triangleGLES to triangle - Move cheatsheets to their own directory - Cleanup clock example to use in QuickStart - QuickStart.md - Some API cleaning: - oc_font_create_from_file()/oc_font_create_from_path() - oc_image_create_from_file/oc_image_create_from_path - oc_canvas_set_current() -> oc_canvas_select() - some matrix helpers
56
Readme.md
|
@ -1,21 +1,35 @@
|
|||
# Installing
|
||||
--------
|
||||
**DISCLAIMER: This project is very much a Work In Progress. We're making it accessible in this very early state so that participants to the [Wheel Reinvention Jam 2023](https://handmade.network/jam/2023) can try it out and maybe use it as their jamming platform. Expect bugs, missing and/or incomplete features, unstable APIs, and sparse documentation. Some current issues might be a show stopper for you, so make sure you can build and run the sample apps before jumping in.**
|
||||
|
||||
**If you do choose to try out Orca anyway, well thanks! We'll do our best to answer your questions, and we'd really appreciate to hear your feedback!**
|
||||
|
||||
--------
|
||||
|
||||
# Orca
|
||||
--
|
||||
|
||||
Orca is a devlopment platform and runtime environment for cross-platform, sandboxed graphical WebAssembly applications.
|
||||
|
||||
## Installing
|
||||
|
||||
_//TODO: Ben can you complete this section?_
|
||||
|
||||
Clone the repo: `git clone git@git.handmade.network:hmn/orca.git`.
|
||||
|
||||
Cd to orca and build the runtime:
|
||||
Cd to the orca directory and build the Orca runtime:
|
||||
|
||||
```
|
||||
cd orca
|
||||
./orca dev build-runtime
|
||||
```
|
||||
|
||||
Install the orca tools:
|
||||
Install the orca dev tools:
|
||||
|
||||
```
|
||||
./orca dev install
|
||||
```
|
||||
|
||||
# Building the sample orca apps:
|
||||
### Building the sample Orca apps
|
||||
|
||||
Cd to the sample project directory and run its build script:
|
||||
|
||||
|
@ -24,5 +38,35 @@ cd samples/pong
|
|||
./build.sh
|
||||
```
|
||||
|
||||
On macOS this creates a `Pong.app` bundle in `samples/pong` that you can double click to run.
|
||||
On Windows this creates a `Pong` directory in `samples/pong`. You can launch the app by running `Pong/bin/orca.exe`.
|
||||
On macOS this creates a `Pong.app` bundle in `samples/pong` that you can double click to run. On Windows this creates a `Pong` directory in `samples/pong`. You can launch the app by running `Pong/bin/Pong.exe`.
|
||||
|
||||
## Writing an Orca app
|
||||
|
||||
The following documents can help you write an application using the Orca APIs:
|
||||
|
||||
- The [Quick Start Guide](./doc/QuickStart.md) will walk you through writing and building a simple example application.
|
||||
- The [samples folder](./samples) contains sample applications that show various aspects of the Orca API and support library:
|
||||
- [clock](./samples/clock) is a simple clock showcasing vector graphics and the time API.
|
||||
- [breakout](./samples/breakout) is a mini breakout game making use of the vector graphics API.
|
||||
- [triangle](./samples/triangle) shows how to draw a spining triangle using the GLES API.
|
||||
- [fluid](./samples/fluid) is a fluid simulation using a more complex GLES setup.
|
||||
- [ui](./samples/ui) showcases the UI API and Orca's default UI widgets.
|
||||
- The [API Cheatsheets](./doc/cheatsheets) provide a list of Orca API functions, grouped by topic.
|
||||
|
||||
## Building and bundling an Orca app
|
||||
|
||||
_//TODO: Ben, can you complete this section? Or should that be in the QuickStart guide?_
|
||||
|
||||
You must compile your application along with the Orca support code, into a WebAssembly module. The command `orca src cflags` can help you set up your compiler's flags to do so.
|
||||
|
||||
Once you have built your WebAssembly module, you can invoke the command `orca bundle` to bundle it with your apps resources and the Orca runtime to produce an application.
|
||||
|
||||
## License
|
||||
|
||||
Orca is distributed under the terms of the GNU Affero General Public License version 3, with additional terms in accordance with section 7 of AGPLv3. These additional terms ensure that:
|
||||
|
||||
- Modified versions of Orca must reasonably inform users that they are modified.
|
||||
- You can distribute your application's WebAssembly modules under the terms of your choice, and are not required to license them under the terms of the AGPLv3.
|
||||
|
||||
Copyright and License details can be found in [LICENSE.txt](./LICENSE.txt)
|
||||
|
||||
|
|
|
@ -0,0 +1,180 @@
|
|||
--------
|
||||
**DISCLAIMER: This project is very much a Work In Progress. We're making it accessible in this very early state so that participants to the [Wheel Reinvention Jam 2023](https://handmade.network/jam/2023) can try it out and maybe use it as their jamming platform. Expect bugs, missing and/or incomplete features, unstable APIs, and sparse documentation. Some current issues might be a show stopper for you, so make sure you can build and run the sample apps before jumping in.**
|
||||
|
||||
**If you do choose to try out Orca anyway, well thanks! We'll do our best to answer your questions, and we'd really appreciate to hear your feedback!**
|
||||
|
||||
--------
|
||||
|
||||
# Orca Quick Start Guide
|
||||
--
|
||||
|
||||
This is a short introduction to developping an application that can be run by the Orca runtime. We'll present the basic structure of an Orca application, and walk through a simple example in C.
|
||||
|
||||
## Basic structure
|
||||
|
||||
Orca exposes a number of types and functions to applications. In order to use them the first thing to do is to include `orca.h`.
|
||||
|
||||
```
|
||||
#include<orca.h>
|
||||
```
|
||||
|
||||
The Orca runtime manages the application's window and event loop. In order to receive a specific kind of event, you can define an associated _event handler_ and export it to the runtime. For instance, to be notified when your application's window is resized, you should define the `oc_on_resize()` handler:
|
||||
|
||||
```
|
||||
ORCA_EXPORT void oc_on_resize(u32 width, u32 height)
|
||||
{
|
||||
// handle the window resize event
|
||||
}
|
||||
```
|
||||
|
||||
The `ORCA_EXPORT` macro makes the handler visible to the orca runtime, which automatically binds it to the window resize event.
|
||||
|
||||
Handlers are optional. If you don't care about an event, you can just omit the associated handler. You probably want to define at least two important handlers:
|
||||
|
||||
- `oc_on_init()` is called once when your application starts and can be use to initialize your application's resources
|
||||
- `oc_on_frame_refresh()` is called when your application needs to render a new frame, typically tied to the refresh rate of the monitor.
|
||||
|
||||
For a list of available handlers and their signatures, see the [app cheatsheet](../doc/cheatsheets/cheatsheet_app.h).
|
||||
|
||||
|
||||
## Clock Example
|
||||
|
||||
Let's look at the [clock example](../samples/clock). This is a simple app that shows an analog clock and showcases a couple of interesting Orca APIs.
|
||||
|
||||
Open [`main.c`](../samples/clock/src/main.c) and look at the definition of `oc_on_init()`. This handler is called when the applications starts, right after the application window has been created.
|
||||
|
||||
The first thing we do here is set the title and dimensions of the window. We then create the graphics resources that we'll use to draw the clock onto the window.
|
||||
|
||||
### Graphics surfaces
|
||||
|
||||
The next line of `oc_init()` creates a _graphics surface_. A surface represents a destination you can draw into using a specific API. In this sample, we're going to use a canvas surface, which allows drawing with a 2D vector graphics API. Other samples use a GLES surface to draw with the OpenGL ES API.
|
||||
|
||||
Before drawing into it, the surface must be selected as the current surface by calling `oc_surface_select()`. Once all drawing is done you can display the result by calling `oc_surface_present()`.
|
||||
|
||||
### Canvas
|
||||
|
||||
After creating the surface, we create a _canvas_. A canvas holds some context for drawing commands, like the current color or stroke width, as well as a command buffer that records all drawing commands. All canvas drawing functions use an implicit _current canvas_. You can select a canvas to be the current canvas by calling `oc_canvas_select()`, as seen at the begining of `oc_on_frame_refresh()`.
|
||||
|
||||
Canvas drawing functions like `oc_fill()` or `oc_stroke` merely add to the current canvas command buffer. You can later render those commands onto a canvas surface by calling `oc_render()`.
|
||||
|
||||
To summarize, the general structure of canvas drawing code is like the following:
|
||||
|
||||
```
|
||||
oc_canvas_select(canvas); // make the canvas current
|
||||
|
||||
//... add commands to the canvas command buffer using drawing functions
|
||||
|
||||
oc_surface_select(surface); // select the canvas surface
|
||||
oc_render(surface, canvas); // render the canvas commands into it
|
||||
oc_surface_present(surface); // display the result
|
||||
```
|
||||
|
||||
### Drawing
|
||||
|
||||
Canvas drawing functions can be roughly divided into three groups:
|
||||
|
||||
- Path functions like `oc_line_to()` or `oc_cubic_to()` are used to specify paths using lines and curves.
|
||||
- Attributes setup functions like `oc_set_color()` or `oc_set_width()` are used to set attributes used by subsequent commands.
|
||||
- Command functions like `oc_stroke()` and `oc_fill()` encode commands into the canvas command buffer using the current path and attributes.
|
||||
|
||||
Some helpers combine a path specification and a command, like `oc_circle_fill()`.
|
||||
|
||||
As an example, the back of the clock is drawn using these two calls:
|
||||
|
||||
```
|
||||
// clock backing
|
||||
oc_set_color_rgba(1, 1, 1, 1);
|
||||
oc_circle_fill(centerX, centerY, clockRadius);
|
||||
```
|
||||
|
||||
For a list of canvas drawing functions, see the [graphics API cheatsheet](../doc/cheatsheets/cheatsheet_graphics.h).
|
||||
|
||||
#### Transforms
|
||||
|
||||
A special case of attribute setting function is the pair `oc_matrix_push()` and `oc_matrix_pop()`, which are used to manipulate a stack of transform matrices:
|
||||
|
||||
- `oc_matrix_push()` multiplies the matrix currently on top of the stack with its argument, and pushes the result on the stack.
|
||||
- `oc_matrix_pop()` pops a matrix from the stack.
|
||||
|
||||
The matrix on the top of the stack at the time a command is encoded is used to transform the path of that command.
|
||||
|
||||
You can see an example of using transform matrices when drawing the clock's hands:
|
||||
|
||||
```
|
||||
// hours hand
|
||||
oc_matrix_push(mat_transform(centerX, centerY, hoursRotation));
|
||||
{
|
||||
oc_set_color_rgba(.2, 0.2, 0.2, 1);
|
||||
oc_rounded_rectangle_fill(0, -7.5 * uiScale, clockRadius * 0.5f, 15 * uiScale, 5 * uiScale);
|
||||
}
|
||||
oc_matrix_pop();
|
||||
```
|
||||
|
||||
### Fonts and text
|
||||
|
||||
Going back to `oc_init()`, after creating a surface and a canvas, we create a font that we will use to draw the numbers on the clock's face:
|
||||
|
||||
```
|
||||
oc_unicode_range ranges[5] = {
|
||||
OC_UNICODE_BASIC_LATIN,
|
||||
OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
OC_UNICODE_LATIN_EXTENDED_A,
|
||||
OC_UNICODE_LATIN_EXTENDED_B,
|
||||
OC_UNICODE_SPECIALS
|
||||
};
|
||||
|
||||
font = oc_font_create_from_path(OC_STR8("/segoeui.ttf"), 5, ranges);
|
||||
```
|
||||
|
||||
The font is loaded from a font file located in a data folder inside the app bundle. By default, Orca apps use this data folder as their "root" for file operations.
|
||||
|
||||
Along with the path of the font file, we pass to the creation function the unicode ranges we want to load.
|
||||
|
||||
We then use the font to draw the clock's face:
|
||||
|
||||
```
|
||||
// clock face
|
||||
for(int i = 0; i < oc_array_size(clockNumberStrings); ++i)
|
||||
{
|
||||
oc_rect textRect = oc_text_bounding_box(font, fontSize, clockNumberStrings[i]);
|
||||
textRect.h -= 10 * uiScale; // oc_text_bounding_box height doesn't seem to be a tight fit around the glyph
|
||||
|
||||
const f32 angle = i * ((M_PI * 2) / 12.0f) - (M_PI / 2);
|
||||
oc_mat2x3 transform = mat_transform(centerX - (textRect.w / 2), centerY + (textRect.h / 2), angle);
|
||||
oc_vec2 pos = oc_mat2x3_mul(transform, (oc_vec2){ clockRadius * 0.8f, 0 });
|
||||
|
||||
oc_set_color_rgba(0.2, 0.2, 0.2, 1);
|
||||
oc_text_fill(pos.x, pos.y, clockNumberStrings[i]);
|
||||
}
|
||||
```
|
||||
|
||||
### Logging and asserts
|
||||
|
||||
The runtime has a console overlay whose visiblity can be toggled on and off with `Shift + Cmd + D` on macOS, or `Shift + Ctrl + D` on Windows. Your application can log messages, warnings or errors to that console using the following functions:
|
||||
|
||||
```
|
||||
void oc_log_info(const char* fmt, ...); // informational messages
|
||||
void oc_log_warning(const char* fmt, ...); // warnings, displayed in orange.
|
||||
void oc_log_error(const char* fmt, ...); // errors, displayed in red.
|
||||
```
|
||||
|
||||
If you started the application from a terminal, the log entries are also duplicated there.
|
||||
|
||||
You can assert on a condition using `OC_ASSERT(test, fmt, ...)`. If the test fails, the runtime displays a message box including your message, and terminates the application.
|
||||
|
||||
You can unconditionally abort the application with a message box using `OC_ABORT(fmt, ...)`.
|
||||
|
||||
## Where to go next?
|
||||
|
||||
For more examples of how to use Orca APIs, you can look at the other [sample apps](../samples):
|
||||
|
||||
- [breakout](./samples/breakout) is a mini breakout game making use of the vector graphics API. It demonstrates using input and drawing images.
|
||||
- [triangle](./samples/triangle) shows how to draw a spining triangle using the GLES API.
|
||||
- [fluid](./samples/fluid) is a fluid simulation using a more complex GLES setup.
|
||||
- [ui](./samples/ui) showcases the UI API and Orca's default UI widgets.
|
||||
|
||||
For a list of Orca APIs, you can look at the [API cheatsheets](../doc/cheatsheets).
|
||||
|
||||
You can also ask questions in the [Handmade Network Discord](https://discord.gg/hmn), in particular in the [#orca](https://discord.com/channels/239737791225790464/1121811864066732082) channel.
|
||||
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
@echo off
|
||||
|
||||
:: compile wasm module
|
||||
set wasmFlags=--target=wasm32^
|
||||
--no-standard-libraries ^
|
||||
-fno-builtin ^
|
||||
-Wl,--no-entry ^
|
||||
-Wl,--export-dynamic ^
|
||||
-g ^
|
||||
-O2 ^
|
||||
-mbulk-memory ^
|
||||
-D__ORCA__ ^
|
||||
-isystem ..\..\src\libc-shim\include ^
|
||||
-I..\..\ext -I ..\..\src
|
||||
|
||||
clang %wasmFlags% -o .\module.wasm ..\..\src\orca.c ..\..\src\libc-shim\src\*.c src\main.c
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
|
||||
orca bundle --orca-dir ..\.. --name Pong --icon icon.png --resource-dir data module.wasm
|
||||
@echo off
|
||||
|
||||
:: compile wasm module
|
||||
set wasmFlags=--target=wasm32^
|
||||
--no-standard-libraries ^
|
||||
-fno-builtin ^
|
||||
-Wl,--no-entry ^
|
||||
-Wl,--export-dynamic ^
|
||||
-g ^
|
||||
-O2 ^
|
||||
-mbulk-memory ^
|
||||
-D__ORCA__ ^
|
||||
-isystem ..\..\src\libc-shim\include ^
|
||||
-I..\..\ext -I ..\..\src
|
||||
|
||||
clang %wasmFlags% -o .\module.wasm ..\..\src\orca.c ..\..\src\libc-shim\src\*.c src\main.c
|
||||
IF %ERRORLEVEL% NEQ 0 EXIT /B %ERRORLEVEL%
|
||||
|
||||
orca bundle --orca-dir ..\.. --name Breakout --icon icon.png --resource-dir data module.wasm
|
|
@ -29,4 +29,4 @@ wasmFlags="--target=wasm32 \
|
|||
|
||||
$CLANG $wasmFlags -o ./module.wasm ../../src/orca.c $STDLIB_DIR/src/*.c src/main.c
|
||||
|
||||
orca bundle --orca-dir ../.. --name Pong --icon icon.png --resource-dir data module.wasm
|
||||
orca bundle --orca-dir ../.. --name Breakout --icon icon.png --resource-dir data module.wasm
|
After Width: | Height: | Size: 490 KiB |
Before Width: | Height: | Size: 314 KiB After Width: | Height: | Size: 314 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
@ -5,20 +5,13 @@
|
|||
#define NUM_BLOCKS_PER_ROW 7
|
||||
#define NUM_BLOCKS 42 // 7 * 6
|
||||
#define NUM_BLOCKS_TO_WIN (NUM_BLOCKS - 2)
|
||||
|
||||
#define BLOCKS_WIDTH 810.0f
|
||||
#define BLOCK_HEIGHT 30.0f
|
||||
#define BLOCKS_PADDING 15.0f
|
||||
#define BLOCKS_BOTTOM 300.0f
|
||||
const f32 BLOCK_WIDTH = (BLOCKS_WIDTH - ((NUM_BLOCKS_PER_ROW + 1) * BLOCKS_PADDING)) / NUM_BLOCKS_PER_ROW;
|
||||
|
||||
#define PADDLE_MAX_LAUNCH_ANGLE 0.7f
|
||||
|
||||
const oc_color paddleColor = { 1, 0, 0, 1 };
|
||||
oc_rect paddle = { 300, 50, 200, 24 };
|
||||
|
||||
const oc_color ballColor = { 1, 1, 0, 1 };
|
||||
oc_rect ball = { 200, 200, 20, 20 };
|
||||
const f32 BLOCK_WIDTH = (BLOCKS_WIDTH - ((NUM_BLOCKS_PER_ROW + 1) * BLOCKS_PADDING)) / NUM_BLOCKS_PER_ROW;
|
||||
|
||||
oc_vec2 velocity = { 5, 5 };
|
||||
|
||||
|
@ -33,74 +26,77 @@ int blockHealth[NUM_BLOCKS] = {
|
|||
};
|
||||
int score = 0;
|
||||
|
||||
oc_vec2 frameSize = { 100, 100 };
|
||||
|
||||
bool leftDown = false;
|
||||
bool rightDown = false;
|
||||
|
||||
oc_vec2 frameSize = { 100, 100 };
|
||||
|
||||
oc_surface surface;
|
||||
oc_canvas canvas;
|
||||
|
||||
oc_image waterImage;
|
||||
oc_image brickImage;
|
||||
oc_image ballImage;
|
||||
oc_image paddleImage;
|
||||
oc_font pongFont;
|
||||
oc_font font;
|
||||
|
||||
f32 lerp(f32 a, f32 b, f32 t);
|
||||
oc_rect blockRect(int i);
|
||||
int checkCollision(oc_rect block);
|
||||
oc_mat2x3 flipY(oc_rect r);
|
||||
oc_mat2x3 flipYAt(oc_vec2 pos);
|
||||
const oc_color paddleColor = { 1, 0, 0, 1 };
|
||||
oc_rect paddle = { 300, 50, 200, 24 };
|
||||
oc_rect ball = { 200, 200, 20, 20 };
|
||||
|
||||
oc_str8 loadFile(oc_arena* arena, oc_str8 filename)
|
||||
f32 lerp(f32 a, f32 b, f32 t)
|
||||
{
|
||||
oc_file file = oc_file_open(filename, OC_FILE_ACCESS_READ, 0);
|
||||
if(oc_file_last_error(file) != OC_IO_OK)
|
||||
{
|
||||
oc_arena_scope scope = oc_arena_scope_begin(arena);
|
||||
oc_log_error("Couldn't open file %s\n", oc_str8_to_cstring(arena, filename));
|
||||
oc_arena_scope_end(scope);
|
||||
}
|
||||
u64 size = oc_file_size(file);
|
||||
char* buffer = oc_arena_push(arena, size);
|
||||
oc_file_read(file, size, buffer);
|
||||
oc_file_close(file);
|
||||
return oc_str8_from_buffer(size, buffer);
|
||||
return (1 - t) * a + t * b;
|
||||
}
|
||||
|
||||
oc_mat2x3 flip_y(oc_rect r)
|
||||
{
|
||||
return (oc_mat2x3){
|
||||
1, 0, 0,
|
||||
0, -1, 2 * r.y + r.h
|
||||
};
|
||||
}
|
||||
|
||||
oc_mat2x3 flip_y_at(oc_vec2 pos)
|
||||
{
|
||||
return (oc_mat2x3){
|
||||
1, 0, 0,
|
||||
0, -1, 2 * pos.y
|
||||
};
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_init(void)
|
||||
{
|
||||
oc_window_set_title(OC_STR8("pong"));
|
||||
oc_window_set_title(OC_STR8("Breakout"));
|
||||
|
||||
surface = oc_surface_canvas();
|
||||
canvas = oc_canvas_create();
|
||||
|
||||
waterImage = oc_image_create_from_memory(surface, loadFile(oc_scratch(), OC_STR8("/underwater.jpg")), false);
|
||||
ballImage = oc_image_create_from_memory(surface, loadFile(oc_scratch(), OC_STR8("/ball.png")), false);
|
||||
paddleImage = oc_image_create_from_memory(surface, loadFile(oc_scratch(), OC_STR8("/wall.png")), false);
|
||||
waterImage = oc_image_create_from_path(surface, OC_STR8("/underwater.jpg"), false);
|
||||
brickImage = oc_image_create_from_path(surface, OC_STR8("/brick.png"), false);
|
||||
ballImage = oc_image_create_from_path(surface, OC_STR8("/ball.png"), false);
|
||||
|
||||
if(oc_image_is_nil(waterImage))
|
||||
{
|
||||
oc_log_error("couldn't load water image\n");
|
||||
}
|
||||
if(oc_image_is_nil(brickImage))
|
||||
{
|
||||
oc_log_error("couldn't load brick image\n");
|
||||
}
|
||||
if(oc_image_is_nil(ballImage))
|
||||
{
|
||||
oc_log_error("couldn't load ball image\n");
|
||||
}
|
||||
if(oc_image_is_nil(paddleImage))
|
||||
{
|
||||
oc_log_error("couldn't load paddle image\n");
|
||||
}
|
||||
|
||||
oc_str8 fontStr = loadFile(oc_scratch(), OC_STR8("/Literata-SemiBoldItalic.ttf"));
|
||||
oc_unicode_range ranges[5] = { OC_UNICODE_BASIC_LATIN,
|
||||
OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
OC_UNICODE_LATIN_EXTENDED_A,
|
||||
OC_UNICODE_LATIN_EXTENDED_B,
|
||||
OC_UNICODE_SPECIALS };
|
||||
oc_unicode_range ranges[5] = {
|
||||
OC_UNICODE_BASIC_LATIN,
|
||||
OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
OC_UNICODE_LATIN_EXTENDED_A,
|
||||
OC_UNICODE_LATIN_EXTENDED_B,
|
||||
OC_UNICODE_SPECIALS
|
||||
};
|
||||
|
||||
pongFont = oc_font_create_from_memory(fontStr, 5, ranges);
|
||||
|
||||
oc_arena_clear(oc_scratch());
|
||||
font = oc_font_create_from_path(OC_STR8("/Literata-SemiBoldItalic.ttf"), 5, ranges);
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_terminate(void)
|
||||
|
@ -122,24 +118,8 @@ ORCA_EXPORT void oc_on_resize(u32 width, u32 height)
|
|||
frameSize.y = height;
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_mouse_down(int button)
|
||||
{
|
||||
oc_log_info("mouse down!");
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_key_down(int key)
|
||||
{
|
||||
if(key == OC_KEY_SPACE)
|
||||
{
|
||||
oc_log_error("(this is just for testing errors)");
|
||||
return;
|
||||
}
|
||||
if(key == OC_KEY_ENTER)
|
||||
{
|
||||
oc_log_warning("(this is just for testing warning)");
|
||||
return;
|
||||
}
|
||||
|
||||
oc_log_info("key down: %i", key);
|
||||
if(key == OC_KEY_LEFT)
|
||||
{
|
||||
|
@ -153,11 +133,6 @@ ORCA_EXPORT void oc_on_key_down(int key)
|
|||
|
||||
ORCA_EXPORT void oc_on_key_up(int key)
|
||||
{
|
||||
if(key == OC_KEY_ENTER || key == OC_KEY_SPACE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
oc_log_info("key up: %i", key);
|
||||
if(key == OC_KEY_LEFT)
|
||||
{
|
||||
|
@ -169,192 +144,7 @@ ORCA_EXPORT void oc_on_key_up(int key)
|
|||
}
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_frame_refresh(void)
|
||||
{
|
||||
f32 aspect = frameSize.x / frameSize.y;
|
||||
|
||||
if(leftDown)
|
||||
{
|
||||
paddle.x -= 10;
|
||||
}
|
||||
else if(rightDown)
|
||||
{
|
||||
paddle.x += 10;
|
||||
}
|
||||
paddle.x = oc_clamp(paddle.x, 0, frameSize.x - paddle.w);
|
||||
|
||||
ball.x += velocity.x;
|
||||
ball.y += velocity.y;
|
||||
ball.x = oc_clamp(ball.x, 0, frameSize.x - ball.w);
|
||||
ball.y = oc_clamp(ball.y, 0, frameSize.y - ball.h);
|
||||
|
||||
if(ball.x + ball.w >= frameSize.x)
|
||||
{
|
||||
velocity.x = -velocity.x;
|
||||
}
|
||||
if(ball.x <= 0)
|
||||
{
|
||||
velocity.x = -velocity.x;
|
||||
}
|
||||
if(ball.y + ball.h >= frameSize.y)
|
||||
{
|
||||
velocity.y = -velocity.y;
|
||||
}
|
||||
|
||||
if(
|
||||
ball.y <= paddle.y + paddle.h && ball.x + ball.w >= paddle.x && ball.x <= paddle.x + paddle.w && velocity.y < 0)
|
||||
{
|
||||
f32 t = ((ball.x + ball.w / 2) - paddle.x) / paddle.w;
|
||||
f32 launchAngle = lerp(-PADDLE_MAX_LAUNCH_ANGLE, PADDLE_MAX_LAUNCH_ANGLE, t);
|
||||
f32 speed = sqrtf(velocity.x * velocity.x + velocity.y * velocity.y);
|
||||
velocity = (oc_vec2){
|
||||
sinf(launchAngle) * speed,
|
||||
cosf(launchAngle) * speed,
|
||||
};
|
||||
ball.y = paddle.y + paddle.h;
|
||||
|
||||
oc_log_info("PONG!");
|
||||
}
|
||||
|
||||
if(ball.y <= 0)
|
||||
{
|
||||
ball.x = frameSize.x / 2. - ball.w;
|
||||
ball.y = frameSize.y / 2. - ball.h;
|
||||
}
|
||||
|
||||
for(int i = 0; i < NUM_BLOCKS; i++)
|
||||
{
|
||||
if(blockHealth[i] <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
oc_rect r = blockRect(i);
|
||||
int result = checkCollision(r);
|
||||
if(result)
|
||||
{
|
||||
oc_log_info("Collision! direction=%d", result);
|
||||
blockHealth[i] -= 1;
|
||||
|
||||
if(blockHealth[i] == 0)
|
||||
{
|
||||
++score;
|
||||
}
|
||||
|
||||
f32 vx = velocity.x;
|
||||
f32 vy = velocity.y;
|
||||
|
||||
switch(result)
|
||||
{
|
||||
case 1:
|
||||
case 5:
|
||||
velocity.y = -vy;
|
||||
break;
|
||||
case 3:
|
||||
case 7:
|
||||
velocity.x = -vx;
|
||||
break;
|
||||
case 2:
|
||||
case 6:
|
||||
velocity.x = -vy;
|
||||
velocity.y = -vx;
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
velocity.x = vy;
|
||||
velocity.y = vx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(score == NUM_BLOCKS_TO_WIN)
|
||||
{
|
||||
oc_request_quit();
|
||||
}
|
||||
|
||||
oc_canvas_set_current(canvas);
|
||||
|
||||
oc_set_color_rgba(10.0f / 255.0f, 31.0f / 255.0f, 72.0f / 255.0f, 1);
|
||||
oc_clear();
|
||||
|
||||
oc_image_draw(waterImage, (oc_rect){ 0, 0, frameSize.x, frameSize.y });
|
||||
|
||||
oc_mat2x3 yUp = {
|
||||
1, 0, 0,
|
||||
0, -1, frameSize.y
|
||||
};
|
||||
|
||||
oc_matrix_push(yUp);
|
||||
{
|
||||
for(int i = 0; i < NUM_BLOCKS; i++)
|
||||
{
|
||||
if(blockHealth[i] <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
oc_rect r = blockRect(i);
|
||||
oc_set_color_rgba(0, 0, 0, 0.2);
|
||||
oc_rounded_rectangle_fill(r.x, r.y - 2, r.w, r.h, 4);
|
||||
oc_set_color_rgba(0.9, 0.9, 0.9, 1);
|
||||
oc_rounded_rectangle_fill(r.x, r.y, r.w, r.h, 4);
|
||||
|
||||
int fontSize = 18;
|
||||
oc_str8 text = oc_str8_pushf(oc_scratch(),
|
||||
"%d", blockHealth[i]);
|
||||
oc_rect textRect = oc_text_bounding_box(pongFont, fontSize, text);
|
||||
|
||||
oc_vec2 textPos = {
|
||||
r.x + r.w / 2 - textRect.w / 2,
|
||||
r.y + 9, // TODO: oc_text_bounding_box is returning extremely wack results for height.
|
||||
};
|
||||
|
||||
oc_set_color_rgba(0, 0, 0, 1);
|
||||
oc_set_font(pongFont);
|
||||
oc_set_font_size(18);
|
||||
oc_move_to(textPos.x, textPos.y);
|
||||
oc_matrix_push(flipYAt(textPos));
|
||||
{
|
||||
oc_text_outlines(text);
|
||||
oc_fill();
|
||||
}
|
||||
oc_matrix_pop();
|
||||
}
|
||||
|
||||
oc_set_color_rgba(0.9, 0.9, 0.9, 1);
|
||||
oc_rounded_rectangle_fill(paddle.x, paddle.y, paddle.w, paddle.h, 4);
|
||||
|
||||
oc_matrix_push(flipY(ball));
|
||||
{
|
||||
oc_image_draw(ballImage, ball);
|
||||
}
|
||||
oc_matrix_pop();
|
||||
|
||||
// draw score text
|
||||
{
|
||||
oc_move_to(10, 10);
|
||||
oc_str8 text = oc_str8_pushf(oc_scratch(), "Destroy all %d blocks to win! Current score: %d", NUM_BLOCKS_TO_WIN, score);
|
||||
oc_rect textRect = oc_text_bounding_box(pongFont, 20, text);
|
||||
oc_vec2 textPos = { 10, 10 };
|
||||
oc_matrix_push(flipYAt(textPos));
|
||||
{
|
||||
oc_text_outlines(text);
|
||||
oc_fill();
|
||||
}
|
||||
oc_matrix_pop();
|
||||
}
|
||||
}
|
||||
oc_matrix_pop();
|
||||
|
||||
oc_surface_select(surface);
|
||||
oc_render(surface, canvas);
|
||||
oc_surface_present(surface);
|
||||
|
||||
oc_arena_clear(oc_scratch());
|
||||
}
|
||||
|
||||
oc_rect blockRect(int i)
|
||||
oc_rect block_rect(int i)
|
||||
{
|
||||
int row = i / NUM_BLOCKS_PER_ROW;
|
||||
int col = i % NUM_BLOCKS_PER_ROW;
|
||||
|
@ -368,7 +158,7 @@ oc_rect blockRect(int i)
|
|||
|
||||
// Returns a cardinal direction 1-8 for the collision with the block, or zero
|
||||
// if no collision. 1 is straight up and directions proceed clockwise.
|
||||
int checkCollision(oc_rect block)
|
||||
int check_collision(oc_rect block)
|
||||
{
|
||||
// Note that all the logic for this game has the origin in the bottom left.
|
||||
|
||||
|
@ -383,13 +173,6 @@ int checkCollision(oc_rect block)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// if ((block.x <= ball.x && ballx2 <= blockx2)
|
||||
// && (block.y <= ball.y && bally2 <= blocky2))
|
||||
// {
|
||||
// // Ball is fully inside block; do not consider as a collision
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// If moving right, the ball can bounce off its top right corner, right
|
||||
// side, or bottom right corner. Corner bounces occur if the block's bottom
|
||||
// left corner is in the ball's top right quadrant, or if the block's top
|
||||
|
@ -495,23 +278,195 @@ int checkCollision(oc_rect block)
|
|||
return 0;
|
||||
}
|
||||
|
||||
f32 lerp(f32 a, f32 b, f32 t)
|
||||
ORCA_EXPORT void oc_on_frame_refresh(void)
|
||||
{
|
||||
return (1 - t) * a + t * b;
|
||||
}
|
||||
f32 aspect = frameSize.x / frameSize.y;
|
||||
|
||||
oc_mat2x3 flipY(oc_rect r)
|
||||
{
|
||||
return (oc_mat2x3){
|
||||
1, 0, 0,
|
||||
0, -1, 2 * r.y + r.h
|
||||
};
|
||||
}
|
||||
if(leftDown)
|
||||
{
|
||||
paddle.x -= 10;
|
||||
}
|
||||
else if(rightDown)
|
||||
{
|
||||
paddle.x += 10;
|
||||
}
|
||||
paddle.x = oc_clamp(paddle.x, 0, frameSize.x - paddle.w);
|
||||
|
||||
oc_mat2x3 flipYAt(oc_vec2 pos)
|
||||
{
|
||||
return (oc_mat2x3){
|
||||
ball.x += velocity.x;
|
||||
ball.y += velocity.y;
|
||||
ball.x = oc_clamp(ball.x, 0, frameSize.x - ball.w);
|
||||
ball.y = oc_clamp(ball.y, 0, frameSize.y - ball.h);
|
||||
|
||||
if(ball.x + ball.w >= frameSize.x)
|
||||
{
|
||||
velocity.x = -velocity.x;
|
||||
}
|
||||
if(ball.x <= 0)
|
||||
{
|
||||
velocity.x = -velocity.x;
|
||||
}
|
||||
if(ball.y + ball.h >= frameSize.y)
|
||||
{
|
||||
velocity.y = -velocity.y;
|
||||
}
|
||||
|
||||
if(
|
||||
ball.y <= paddle.y + paddle.h && ball.x + ball.w >= paddle.x && ball.x <= paddle.x + paddle.w && velocity.y < 0)
|
||||
{
|
||||
f32 t = ((ball.x + ball.w / 2) - paddle.x) / paddle.w;
|
||||
f32 launchAngle = lerp(-PADDLE_MAX_LAUNCH_ANGLE, PADDLE_MAX_LAUNCH_ANGLE, t);
|
||||
f32 speed = sqrtf(velocity.x * velocity.x + velocity.y * velocity.y);
|
||||
velocity = (oc_vec2){
|
||||
sinf(launchAngle) * speed,
|
||||
cosf(launchAngle) * speed,
|
||||
};
|
||||
ball.y = paddle.y + paddle.h;
|
||||
|
||||
oc_log_info("PONG!");
|
||||
}
|
||||
|
||||
if(ball.y <= 0)
|
||||
{
|
||||
ball.x = frameSize.x / 2. - ball.w;
|
||||
ball.y = frameSize.y / 2. - ball.h;
|
||||
}
|
||||
|
||||
for(int i = 0; i < NUM_BLOCKS; i++)
|
||||
{
|
||||
if(blockHealth[i] <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
oc_rect r = block_rect(i);
|
||||
int result = check_collision(r);
|
||||
if(result)
|
||||
{
|
||||
oc_log_info("Collision! direction=%d", result);
|
||||
blockHealth[i] -= 1;
|
||||
|
||||
if(blockHealth[i] == 0)
|
||||
{
|
||||
++score;
|
||||
}
|
||||
|
||||
f32 vx = velocity.x;
|
||||
f32 vy = velocity.y;
|
||||
|
||||
switch(result)
|
||||
{
|
||||
case 1:
|
||||
case 5:
|
||||
velocity.y = -vy;
|
||||
break;
|
||||
case 3:
|
||||
case 7:
|
||||
velocity.x = -vx;
|
||||
break;
|
||||
case 2:
|
||||
case 6:
|
||||
velocity.x = -vy;
|
||||
velocity.y = -vx;
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
velocity.x = vy;
|
||||
velocity.y = vx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(score == NUM_BLOCKS_TO_WIN)
|
||||
{
|
||||
oc_request_quit();
|
||||
}
|
||||
|
||||
oc_canvas_select(canvas);
|
||||
|
||||
oc_set_color_rgba(10.0f / 255.0f, 31.0f / 255.0f, 72.0f / 255.0f, 1);
|
||||
oc_clear();
|
||||
|
||||
oc_image_draw(waterImage, (oc_rect){ 0, 0, frameSize.x, frameSize.y });
|
||||
|
||||
oc_mat2x3 yUp = {
|
||||
1, 0, 0,
|
||||
0, -1, 2 * pos.y
|
||||
0, -1, frameSize.y
|
||||
};
|
||||
|
||||
oc_matrix_push(yUp);
|
||||
{
|
||||
for(int i = 0; i < NUM_BLOCKS; i++)
|
||||
{
|
||||
if(blockHealth[i] <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
oc_rect r = block_rect(i);
|
||||
|
||||
oc_set_image(brickImage);
|
||||
oc_set_color_rgba(0.9, 0.9, 0.9, 1);
|
||||
oc_rounded_rectangle_fill(r.x, r.y, r.w, r.h, 4);
|
||||
oc_set_image(oc_image_nil());
|
||||
|
||||
oc_set_color_rgba(0.6, 0.6, 0.6, 1);
|
||||
oc_set_width(2);
|
||||
oc_rounded_rectangle_stroke(r.x, r.y, r.w, r.h, 4);
|
||||
|
||||
int fontSize = 18;
|
||||
oc_str8 text = oc_str8_pushf(oc_scratch(), "%d", blockHealth[i]);
|
||||
oc_rect textRect = oc_text_bounding_box(font, fontSize, text);
|
||||
|
||||
oc_vec2 textPos = {
|
||||
r.x + r.w / 2 - textRect.w / 2,
|
||||
r.y + 9, // TODO: oc_text_bounding_box is returning extremely wack results for height.
|
||||
};
|
||||
|
||||
oc_set_color_rgba(0.9, 0.9, 0.9, 1);
|
||||
oc_circle_fill(r.x + r.w / 2, r.y + r.h / 2, r.h / 2.5);
|
||||
|
||||
oc_set_color_rgba(0, 0, 0, 1);
|
||||
oc_set_font(font);
|
||||
oc_set_font_size(18);
|
||||
oc_move_to(textPos.x, textPos.y);
|
||||
oc_matrix_push(flip_y_at(textPos));
|
||||
{
|
||||
oc_text_outlines(text);
|
||||
oc_fill();
|
||||
}
|
||||
oc_matrix_pop();
|
||||
}
|
||||
|
||||
oc_set_color(paddleColor);
|
||||
oc_rounded_rectangle_fill(paddle.x, paddle.y, paddle.w, paddle.h, 4);
|
||||
|
||||
oc_matrix_push(flip_y(ball));
|
||||
{
|
||||
oc_image_draw(ballImage, ball);
|
||||
}
|
||||
oc_matrix_pop();
|
||||
|
||||
// draw score text
|
||||
{
|
||||
oc_move_to(10, 10);
|
||||
oc_str8 text = oc_str8_pushf(oc_scratch(), "Destroy all %d blocks to win! Current score: %d", NUM_BLOCKS_TO_WIN, score);
|
||||
oc_rect textRect = oc_text_bounding_box(font, 20, text);
|
||||
oc_vec2 textPos = { 10, 10 };
|
||||
oc_matrix_push(flip_y_at(textPos));
|
||||
{
|
||||
oc_set_color_rgba(0.9, 0.9, 0.9, 1);
|
||||
oc_text_outlines(text);
|
||||
oc_fill();
|
||||
}
|
||||
oc_matrix_pop();
|
||||
}
|
||||
}
|
||||
oc_matrix_pop();
|
||||
|
||||
oc_surface_select(surface);
|
||||
oc_render(surface, canvas);
|
||||
oc_surface_present(surface);
|
||||
|
||||
oc_arena_clear(oc_scratch());
|
||||
}
|
|
@ -1,21 +1,33 @@
|
|||
#include <math.h>
|
||||
|
||||
#include <orca.h>
|
||||
|
||||
#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
const oc_str8 clockNumberStrings[] = {
|
||||
OC_STR8_LIT("12"),
|
||||
OC_STR8_LIT("1"),
|
||||
OC_STR8_LIT("2"),
|
||||
OC_STR8_LIT("3"),
|
||||
OC_STR8_LIT("4"),
|
||||
OC_STR8_LIT("5"),
|
||||
OC_STR8_LIT("6"),
|
||||
OC_STR8_LIT("7"),
|
||||
OC_STR8_LIT("8"),
|
||||
OC_STR8_LIT("9"),
|
||||
OC_STR8_LIT("10"),
|
||||
OC_STR8_LIT("11"),
|
||||
};
|
||||
|
||||
oc_surface surface = { 0 };
|
||||
oc_canvas canvas = { 0 };
|
||||
oc_font font = { 0 };
|
||||
oc_vec2 frameSize = { 100, 100 };
|
||||
|
||||
oc_surface surface;
|
||||
oc_canvas canvas;
|
||||
oc_font font;
|
||||
|
||||
f64 lastSeconds = 0;
|
||||
|
||||
oc_mat2x3 mat_rotation(f32 radians);
|
||||
oc_mat2x3 mat_translation(f32 x, f32 y);
|
||||
oc_mat2x3 mat_transform(f32 x, f32 y, f32 radians);
|
||||
f32 minf(f32 a, f32 b);
|
||||
oc_mat2x3 mat_transform(f32 x, f32 y, f32 radians)
|
||||
{
|
||||
oc_mat2x3 rotation = oc_mat2x3_rotate(radians);
|
||||
oc_mat2x3 translation = oc_mat2x3_translate(x, y);
|
||||
return oc_mat2x3_mul_m(translation, rotation);
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_init(void)
|
||||
{
|
||||
|
@ -25,27 +37,15 @@ ORCA_EXPORT void oc_on_init(void)
|
|||
surface = oc_surface_canvas();
|
||||
canvas = oc_canvas_create();
|
||||
|
||||
{
|
||||
oc_str8 filename = OC_STR8("/segoeui.ttf");
|
||||
oc_file file = oc_file_open(filename, OC_FILE_ACCESS_READ, 0);
|
||||
if(oc_file_last_error(file) != OC_IO_OK)
|
||||
{
|
||||
oc_log_error("Couldn't open file %s\n", oc_str8_to_cstring(oc_scratch(), filename));
|
||||
}
|
||||
u64 size = oc_file_size(file);
|
||||
char* buffer = oc_arena_push(oc_scratch(), size);
|
||||
oc_file_read(file, size, buffer);
|
||||
oc_file_close(file);
|
||||
oc_unicode_range ranges[5] = {
|
||||
OC_UNICODE_BASIC_LATIN,
|
||||
OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
OC_UNICODE_LATIN_EXTENDED_A,
|
||||
OC_UNICODE_LATIN_EXTENDED_B,
|
||||
OC_UNICODE_SPECIALS
|
||||
};
|
||||
|
||||
oc_unicode_range ranges[5] = { OC_UNICODE_BASIC_LATIN,
|
||||
OC_UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT,
|
||||
OC_UNICODE_LATIN_EXTENDED_A,
|
||||
OC_UNICODE_LATIN_EXTENDED_B,
|
||||
OC_UNICODE_SPECIALS };
|
||||
font = oc_font_create_from_memory(oc_str8_from_buffer(size, buffer), 5, ranges);
|
||||
}
|
||||
|
||||
oc_arena_clear(oc_scratch());
|
||||
font = oc_font_create_from_path(OC_STR8("/segoeui.ttf"), 5, ranges);
|
||||
}
|
||||
|
||||
ORCA_EXPORT void oc_on_resize(u32 width, u32 height)
|
||||
|
@ -56,23 +56,7 @@ ORCA_EXPORT void oc_on_resize(u32 width, u32 height)
|
|||
|
||||
ORCA_EXPORT void oc_on_frame_refresh(void)
|
||||
{
|
||||
const oc_str8 clock_number_strings[] = {
|
||||
OC_STR8("12"),
|
||||
OC_STR8("1"),
|
||||
OC_STR8("2"),
|
||||
OC_STR8("3"),
|
||||
OC_STR8("4"),
|
||||
OC_STR8("5"),
|
||||
OC_STR8("6"),
|
||||
OC_STR8("7"),
|
||||
OC_STR8("8"),
|
||||
OC_STR8("9"),
|
||||
OC_STR8("10"),
|
||||
OC_STR8("11"),
|
||||
};
|
||||
|
||||
oc_canvas_set_current(canvas);
|
||||
oc_surface_select(surface);
|
||||
oc_canvas_select(canvas);
|
||||
oc_set_color_rgba(.05, .05, .05, 1);
|
||||
oc_clear();
|
||||
|
||||
|
@ -94,7 +78,7 @@ ORCA_EXPORT void oc_on_frame_refresh(void)
|
|||
|
||||
const f32 centerX = frameSize.x / 2;
|
||||
const f32 centerY = frameSize.y / 2;
|
||||
const f32 clockRadius = minf(frameSize.x, frameSize.y) * 0.5f * 0.85f;
|
||||
const f32 clockRadius = oc_min(frameSize.x, frameSize.y) * 0.5f * 0.85f;
|
||||
|
||||
const f32 DEFAULT_CLOCK_RADIUS = 260;
|
||||
const f32 uiScale = clockRadius / DEFAULT_CLOCK_RADIUS;
|
||||
|
@ -108,83 +92,47 @@ ORCA_EXPORT void oc_on_frame_refresh(void)
|
|||
oc_circle_fill(centerX, centerY, clockRadius);
|
||||
|
||||
// clock face
|
||||
for(int i = 0; i < ARRAYSIZE(clock_number_strings); ++i)
|
||||
for(int i = 0; i < oc_array_size(clockNumberStrings); ++i)
|
||||
{
|
||||
const f32 rot = -i * ((M_PI * 2) / 12.0f) + (M_PI / 2);
|
||||
const f32 sinRot = sinf(rot);
|
||||
const f32 cosRot = cosf(rot);
|
||||
|
||||
oc_rect textRect = oc_text_bounding_box(font, fontSize, clock_number_strings[i]);
|
||||
oc_rect textRect = oc_text_bounding_box(font, fontSize, clockNumberStrings[i]);
|
||||
textRect.h -= 10 * uiScale; // oc_text_bounding_box height doesn't seem to be a tight fit around the glyph
|
||||
|
||||
const f32 x = cosRot * clockRadius * 0.8f - (textRect.w / 2) + centerX;
|
||||
const f32 y = -sinRot * clockRadius * 0.8f + (textRect.h / 2) + centerY;
|
||||
const f32 angle = i * ((M_PI * 2) / 12.0f) - (M_PI / 2);
|
||||
oc_mat2x3 transform = mat_transform(centerX - (textRect.w / 2), centerY + (textRect.h / 2), angle);
|
||||
oc_vec2 pos = oc_mat2x3_mul(transform, (oc_vec2){ clockRadius * 0.8f, 0 });
|
||||
|
||||
oc_set_color_rgba(0.2, 0.2, 0.2, 1);
|
||||
oc_move_to(x, y);
|
||||
oc_text_outlines(clock_number_strings[i]);
|
||||
oc_fill();
|
||||
oc_text_fill(pos.x, pos.y, clockNumberStrings[i]);
|
||||
}
|
||||
|
||||
// hours hand
|
||||
oc_matrix_push(mat_transform(centerX, centerY, hoursRotation));
|
||||
{
|
||||
oc_set_color_rgba(.2, 0.2, 0.2, 1);
|
||||
oc_rounded_rectangle_fill(0, -7.5 * uiScale, clockRadius * 0.5f, 15 * uiScale, 5 * uiScale);
|
||||
|
||||
oc_matrix_pop();
|
||||
}
|
||||
oc_matrix_pop();
|
||||
|
||||
// minutes hand
|
||||
oc_matrix_push(mat_transform(centerX, centerY, minutesRotation));
|
||||
{
|
||||
oc_set_color_rgba(.2, 0.2, 0.2, 1);
|
||||
oc_rounded_rectangle_fill(0, -5 * uiScale, clockRadius * 0.7f, 10 * uiScale, 5 * uiScale);
|
||||
|
||||
oc_matrix_pop();
|
||||
}
|
||||
oc_matrix_pop();
|
||||
|
||||
// seconds hand
|
||||
oc_matrix_push(mat_transform(centerX, centerY, secondsRotation));
|
||||
{
|
||||
oc_set_color_rgba(1, 0.2, 0.2, 1);
|
||||
oc_rounded_rectangle_fill(0, -2.5 * uiScale, clockRadius * 0.8f, 5 * uiScale, 5 * uiScale);
|
||||
|
||||
oc_matrix_pop();
|
||||
}
|
||||
oc_matrix_pop();
|
||||
|
||||
oc_set_color_rgba(.2, 0.2, 0.2, 1);
|
||||
oc_circle_fill(centerX, centerY, 10 * uiScale);
|
||||
|
||||
oc_surface_select(surface);
|
||||
oc_render(surface, canvas);
|
||||
oc_surface_present(surface);
|
||||
}
|
||||
|
||||
oc_mat2x3 mat_rotation(f32 radians)
|
||||
{
|
||||
const f32 sinRot = sinf(radians);
|
||||
const f32 cosRot = cosf(radians);
|
||||
oc_mat2x3 rot = {
|
||||
cosRot, -sinRot, 0,
|
||||
sinRot, cosRot, 0
|
||||
};
|
||||
return rot;
|
||||
}
|
||||
|
||||
oc_mat2x3 mat_translation(f32 x, f32 y)
|
||||
{
|
||||
oc_mat2x3 translation = {
|
||||
1, 0, x,
|
||||
0, 1, y
|
||||
};
|
||||
return translation;
|
||||
}
|
||||
|
||||
oc_mat2x3 mat_transform(f32 x, f32 y, f32 radians)
|
||||
{
|
||||
oc_mat2x3 rotation = mat_rotation(radians);
|
||||
oc_mat2x3 translation = mat_translation(x, y);
|
||||
return oc_mat2x3_mul_m(translation, rotation);
|
||||
}
|
||||
|
||||
f32 minf(f32 a, f32 b)
|
||||
{
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 60 KiB |
|
@ -352,7 +352,7 @@ ORCA_EXPORT void oc_on_frame_refresh(void)
|
|||
}
|
||||
}
|
||||
|
||||
oc_canvas_set_current(canvas);
|
||||
oc_canvas_select(canvas);
|
||||
oc_surface_select(surface);
|
||||
oc_ui_draw();
|
||||
oc_render(surface, canvas);
|
||||
|
|
|
@ -87,7 +87,7 @@ int main()
|
|||
}
|
||||
|
||||
oc_surface_select(surface1);
|
||||
oc_canvas_set_current(canvas1);
|
||||
oc_canvas_select(canvas1);
|
||||
|
||||
oc_set_color_rgba(0, 0, 0.5, 0.5);
|
||||
oc_clear();
|
||||
|
@ -99,7 +99,7 @@ int main()
|
|||
|
||||
//*
|
||||
oc_surface_select(surface2);
|
||||
oc_canvas_set_current(canvas2);
|
||||
oc_canvas_select(canvas2);
|
||||
|
||||
oc_set_color_rgba(0, 0, 0, 0);
|
||||
oc_clear();
|
||||
|
|
|
@ -28,7 +28,7 @@ i32 render_thread(void* user)
|
|||
}
|
||||
|
||||
oc_surface_select(surface);
|
||||
oc_canvas_set_current(canvas);
|
||||
oc_canvas_select(canvas);
|
||||
|
||||
oc_set_color_rgba(0, 0, 0.5, 0.5);
|
||||
oc_clear();
|
||||
|
|
|
@ -213,7 +213,7 @@ ORCA_API bool oc_canvas_is_nil(oc_canvas canvas); //DOC: true if canvas is nil
|
|||
|
||||
ORCA_API oc_canvas oc_canvas_create(void); //DOC: create a new canvas
|
||||
ORCA_API void oc_canvas_destroy(oc_canvas canvas); //DOC: destroys canvas
|
||||
ORCA_API oc_canvas oc_canvas_set_current(oc_canvas canvas); //DOC: selects canvas in the current thread
|
||||
ORCA_API oc_canvas oc_canvas_select(oc_canvas canvas); //DOC: selects canvas in the current thread
|
||||
ORCA_API void oc_render(oc_surface surface, oc_canvas canvas); //DOC: renders all canvas commands onto surface
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
@ -221,6 +221,9 @@ ORCA_API void oc_render(oc_surface surface, oc_canvas canvas); //DOC: renders al
|
|||
//------------------------------------------------------------------------------------------
|
||||
ORCA_API oc_font oc_font_nil(void);
|
||||
ORCA_API oc_font oc_font_create_from_memory(oc_str8 mem, u32 rangeCount, oc_unicode_range* ranges);
|
||||
ORCA_API oc_font oc_font_create_from_file(oc_file file, u32 rangeCount, oc_unicode_range* ranges);
|
||||
ORCA_API oc_font oc_font_create_from_path(oc_str8 path, u32 rangeCount, oc_unicode_range* ranges);
|
||||
|
||||
ORCA_API void oc_font_destroy(oc_font font);
|
||||
|
||||
//NOTE(martin): the following int valued functions return -1 if font is invalid or codepoint is not present in font//
|
||||
|
@ -253,7 +256,8 @@ ORCA_API bool oc_image_is_nil(oc_image a);
|
|||
ORCA_API oc_image oc_image_create(oc_surface surface, u32 width, u32 height);
|
||||
ORCA_API oc_image oc_image_create_from_rgba8(oc_surface surface, u32 width, u32 height, u8* pixels);
|
||||
ORCA_API oc_image oc_image_create_from_memory(oc_surface surface, oc_str8 mem, bool flip);
|
||||
ORCA_API oc_image oc_image_create_from_file(oc_surface surface, oc_str8 path, bool flip);
|
||||
ORCA_API oc_image oc_image_create_from_file(oc_surface surface, oc_file file, bool flip);
|
||||
ORCA_API oc_image oc_image_create_from_path(oc_surface surface, oc_str8 path, bool flip);
|
||||
|
||||
ORCA_API void oc_image_destroy(oc_image image);
|
||||
|
||||
|
@ -356,6 +360,8 @@ ORCA_API void oc_circle_fill(f32 x, f32 y, f32 r);
|
|||
ORCA_API void oc_circle_stroke(f32 x, f32 y, f32 r);
|
||||
ORCA_API void oc_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle);
|
||||
|
||||
ORCA_API void oc_text_fill(f32 x, f32 y, oc_str8 text);
|
||||
|
||||
//NOTE: image helpers
|
||||
ORCA_API void oc_image_draw(oc_image image, oc_rect rect);
|
||||
ORCA_API void oc_image_draw_region(oc_image image, oc_rect srcRegion, oc_rect dstRegion);
|
||||
|
|
|
@ -542,6 +542,46 @@ oc_font oc_font_create_from_memory(oc_str8 mem, u32 rangeCount, oc_unicode_range
|
|||
return (fontHandle);
|
||||
}
|
||||
|
||||
oc_font oc_font_create_from_file(oc_file file, u32 rangeCount, oc_unicode_range* ranges)
|
||||
{
|
||||
oc_font font = oc_font_nil();
|
||||
oc_arena_scope scratch = oc_scratch_begin();
|
||||
|
||||
u64 size = oc_file_size(file);
|
||||
char* buffer = oc_arena_push(scratch.arena, size);
|
||||
u64 read = oc_file_read(file, size, buffer);
|
||||
|
||||
if(read != size)
|
||||
{
|
||||
oc_log_error("Couldn't read font data\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
font = oc_font_create_from_memory(oc_str8_from_buffer(size, buffer), rangeCount, ranges);
|
||||
}
|
||||
|
||||
oc_scratch_end(scratch);
|
||||
return (font);
|
||||
}
|
||||
|
||||
oc_font oc_font_create_from_path(oc_str8 path, u32 rangeCount, oc_unicode_range* ranges)
|
||||
{
|
||||
oc_font font = oc_font_nil();
|
||||
|
||||
oc_file file = oc_file_open(path, OC_FILE_ACCESS_READ, OC_FILE_OPEN_NONE);
|
||||
if(oc_file_last_error(file) != OC_IO_OK)
|
||||
{
|
||||
oc_log_error("Could not open file %*.s\n", oc_str8_ip(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
font = oc_font_create_from_file(file, rangeCount, ranges);
|
||||
}
|
||||
oc_file_close(file);
|
||||
|
||||
return (font);
|
||||
}
|
||||
|
||||
void oc_font_destroy(oc_font fontHandle)
|
||||
{
|
||||
oc_font_data* fontData = oc_font_data_from_handle(fontHandle);
|
||||
|
@ -858,7 +898,7 @@ oc_canvas oc_canvas_create()
|
|||
|
||||
canvasHandle = oc_canvas_handle_alloc(canvas);
|
||||
|
||||
oc_canvas_set_current(canvasHandle);
|
||||
oc_canvas_select(canvasHandle);
|
||||
}
|
||||
return (canvasHandle);
|
||||
}
|
||||
|
@ -878,7 +918,7 @@ void oc_canvas_destroy(oc_canvas handle)
|
|||
}
|
||||
}
|
||||
|
||||
oc_canvas oc_canvas_set_current(oc_canvas canvas)
|
||||
oc_canvas oc_canvas_select(oc_canvas canvas)
|
||||
{
|
||||
oc_canvas old = __mgCurrentCanvasHandle;
|
||||
__mgCurrentCanvasHandle = canvas;
|
||||
|
@ -1595,6 +1635,13 @@ void oc_arc(f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle)
|
|||
}
|
||||
}
|
||||
|
||||
void oc_text_fill(f32 x, f32 y, oc_str8 text)
|
||||
{
|
||||
oc_move_to(x, y);
|
||||
oc_text_outlines(text);
|
||||
oc_fill();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
//NOTE(martin): images
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
@ -1633,34 +1680,44 @@ oc_image oc_image_create_from_memory(oc_surface surface, oc_str8 mem, bool flip)
|
|||
return (image);
|
||||
}
|
||||
|
||||
#if !OC_PLATFORM_ORCA
|
||||
|
||||
oc_image oc_image_create_from_file(oc_surface surface, oc_str8 path, bool flip)
|
||||
oc_image oc_image_create_from_file(oc_surface surface, oc_file file, bool flip)
|
||||
{
|
||||
oc_image image = oc_image_nil();
|
||||
int width, height, channels;
|
||||
|
||||
oc_arena_scope scratch = oc_scratch_begin();
|
||||
|
||||
const char* cpath = oc_str8_to_cstring(scratch.arena, path);
|
||||
u64 size = oc_file_size(file);
|
||||
char* buffer = oc_arena_push(scratch.arena, size);
|
||||
u64 read = oc_file_read(file, size, buffer);
|
||||
|
||||
stbi_set_flip_vertically_on_load(flip ? 1 : 0);
|
||||
u8* pixels = stbi_load(cpath, &width, &height, &channels, 4);
|
||||
if(pixels)
|
||||
if(read != size)
|
||||
{
|
||||
image = oc_image_create_from_rgba8(surface, width, height, pixels);
|
||||
free(pixels);
|
||||
oc_log_error("Couldn't read image data\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
oc_log_error("stbi_load() failed: %s\n", stbi_failure_reason());
|
||||
image = oc_image_create_from_memory(surface, oc_str8_from_buffer(size, buffer), flip);
|
||||
}
|
||||
oc_scratch_end(scratch);
|
||||
|
||||
oc_scratch_end(scratch);
|
||||
return (image);
|
||||
}
|
||||
|
||||
#endif // !OC_PLATFORM_ORCA
|
||||
oc_image oc_image_create_from_path(oc_surface surface, oc_str8 path, bool flip)
|
||||
{
|
||||
oc_image image = oc_image_nil();
|
||||
|
||||
oc_file file = oc_file_open(path, OC_FILE_ACCESS_READ, OC_FILE_OPEN_NONE);
|
||||
if(oc_file_last_error(file) != OC_IO_OK)
|
||||
{
|
||||
oc_log_error("Could not open file %*.s\n", oc_str8_ip(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
image = oc_image_create_from_file(surface, file, flip);
|
||||
}
|
||||
oc_file_close(file);
|
||||
return (image);
|
||||
}
|
||||
|
||||
void oc_image_draw_region(oc_image image, oc_rect srcRegion, oc_rect dstRegion)
|
||||
{
|
||||
|
|
|
@ -695,7 +695,7 @@ i32 orca_runloop(void* user)
|
|||