Begin cross platform support

This commit is contained in:
2025-10-10 03:00:00 +04:00
parent a2a9343c27
commit 7698d0e657
25 changed files with 757 additions and 545 deletions

7
.gitignore vendored
View File

@@ -1,2 +1,5 @@
main.exe
*.o
src/main.exe
src/main
*.wo
*.uo
*.mo

View File

@@ -1,29 +0,0 @@
CC = gcc
CFLAGS = -std=gnu23 -Wall -Wextra
LIBS = -lgdi32
TARGET = main.exe
SOURCES = main.c utils/utils.c
OBJECTS = $(SOURCES:.c=.o)
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) -o $@ $^ $(LIBS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
@if command -v rm >/dev/null 2>&1; then \
rm -f $(TARGET) main.o utils/utils.o; \
else \
powershell -Command "Remove-Item -ErrorAction SilentlyContinue '$(TARGET)', 'main.o', 'utils/utils.o'"; \
fi
rebuild: clean all
run: $(TARGET)
.\$(TARGET)
.PHONY: all clean rebuild run

188
main.c
View File

@@ -1,188 +0,0 @@
#include <math.h>
#include <stdio.h>
#include <windows.h>
#include "utils/utils.h"
#define WINDOW_WIDTH 800.0f
#define WINDOW_HEIGHT 600.0f
#define BG_COLOR BLACK_BRUSH
#define COLOR RGB(255, 255, 255)
Point3D cube_points[] = {{0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f},
{1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 1.0f},
{1.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 1.0f}};
int cube_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6},
{6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}};
Point3D cube_position = {.coordinates = {3.0f, 6.0f, 5.0f}};
float cube_speed[] = {0.03f, 0.0f, 0.0f};
const Object cube = {.points = cube_points,
.edges = cube_edges,
.number_of_points = 8,
.number_of_edges = 12,
.position = &cube_position,
.rotate_speed = &cube_speed};
Point3D pyramid_points[] = {{-1.0f, 0.0f, -1.0f},
{1.0f, 0.0f, -1.0f},
{1.0f, 0.0f, 1.0f},
{-1.0f, 0.0f, 1.0f},
{0.0f, 1.5f, 0.0f}};
int pyramid_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0},
{0, 4}, {1, 4}, {2, 4}, {3, 4}};
Point3D pyramid_position = {.coordinates = {6.0f, 3.0f, 5.0f}};
float pyramid_speed[] = {0.0f, 0.0f, 0.03f};
const Object pyramid = {.points = pyramid_points,
.edges = pyramid_edges,
.number_of_points = 5,
.number_of_edges = 8,
.position = &pyramid_position,
.rotate_speed = &pyramid_speed};
Point3D prism_points[] = {{0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f},
{0.5f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f},
{1.0f, 1.0f, 0.0f}, {0.5f, 1.0f, 1.0f}};
int prism_edges[][2] = {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5},
{5, 3}, {0, 3}, {1, 4}, {2, 5}};
Point3D prism_position = {.coordinates = {10.0f, 10.0f, 10.0f}};
float prism_speed[] = {0.0f, 0.03f, 0.0f};
const Object prism = {.points = prism_points,
.edges = prism_edges,
.number_of_points = 6,
.number_of_edges = 9,
.position = &prism_position,
.rotate_speed = &prism_speed};
const int number_of_objects = 3;
Object objects[] = {cube, pyramid, prism};
Camera camera = {.position = {0.0f, 0.0f, 0.0f},
.target = {1.0f, 1.0f, 1.0f},
.up = {-1.0f, -1.0f, 1.0f},
.fov = 120.0f,
.max_distance = 20.0f,
.min_distance = .0f,
.aspect_ratio = WINDOW_WIDTH / WINDOW_HEIGHT};
float render_matrix[4][4];
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
SetTimer(hwnd, 1, 50, NULL);
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
FillRect(hdc, &rect, (HBRUSH)GetStockObject(BG_COLOR));
float width = rect.right - rect.left;
float height = rect.bottom - rect.top;
Screen screen = {.width = width,
.height = height,
.hdc = &hdc,
.render_matrix = &render_matrix};
HPEN hPen = CreatePen(PS_SOLID, 1, COLOR);
HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
for (int i = 0; i < number_of_objects; i++) {
draw_object(&(objects[i]), &screen);
}
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_TIMER:
for (int i = 0; i < number_of_objects; i++) {
float reverse_translate_matrix[4][4];
create_translate_matrix(objects[i].position, reverse_translate_matrix,
-1);
transform_object(&(objects[i]), 4, reverse_translate_matrix);
Point3D center_of_object = calculate_centroid(&(objects[i]));
float reverse_center_translate_matrix[4][4];
create_translate_matrix(&center_of_object,
reverse_center_translate_matrix, -1);
transform_object(&(objects[i]), 4, reverse_center_translate_matrix);
float rotate_matrix[3][3];
create_rotate_matrix(*(objects[i].rotate_speed), rotate_matrix);
transform_object(&(objects[i]), 3, rotate_matrix);
float center_translate_matrix[4][4];
create_translate_matrix(&center_of_object, center_translate_matrix, 1);
transform_object(&(objects[i]), 4, center_translate_matrix);
float translate_matrix[4][4];
create_translate_matrix(objects[i].position, translate_matrix, 1);
transform_object(&(objects[i]), 4, translate_matrix);
}
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_KEYDOWN:
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
const char CLASS_NAME[] = "C3DGraphicEngine";
WNDCLASS wc = {0};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, CLASS_NAME, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
return 0;
}
float view_matrix[4][4];
create_view_matrix(&camera, view_matrix);
float projection_matrix[4][4];
create_projection_matrix(&camera, projection_matrix);
matrix_mult_matrix(4, projection_matrix, view_matrix, render_matrix);
for (int i = 0; i < number_of_objects; i++) {
float translate_matrix[4][4];
create_translate_matrix(objects[i].position, translate_matrix, 1);
transform_object(&(objects[i]), 4, translate_matrix);
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}

38
src/Makefile Normal file
View File

@@ -0,0 +1,38 @@
CC = gcc
CFLAGS = -std=gnu23 -Wall -Wextra -O2
LIBS = -lgdi32
WIN_TARGET = main.exe
UNIX_TARGET = main
BASE_SOURCES = main.c engine/engine.c engine/vector.c engine/matrix.c engine/point.c engine/camera.c engine/object.c
WIN_SOURCES = $(BASE_SOURCES) platforms/win.c
WIN_OBJECTS = $(WIN_SOURCES:.c=.wo)
UNIX_SOURCES = $(BASE_SOURCES) platforms/unix.c
UNIX_OBJECTS = $(UNIX_SOURCES:.c=.uo)
MICRO_SOURCES = $(BASE_SOURCES) platforms/micro.c
MICRO_OBJECTS = $(MICRO_SOURCES:.c=.mo)
win: $(WIN_TARGET)
$(WIN_TARGET): $(WIN_OBJECTS)
$(CC) -o $@ $^ $(LIBS)
%.wo: %.c
$(CC) $(CFLAGS) -DWIN -c $< -o $@
%.uo: %.c
$(CC) $(CFLAGS) -DUNIX -c $< -o $@
%.mo: %.c
$(CC) $(CFLAGS) -DMICRO -c $< -o $@
clean:
rm -f $(WIN_TARGET) $(WIN_OBJECTS) $(UNIX_OBJECTS) $(MICRO_OBJECTS);
run_win: $(WIN_TARGET)
.\$(WIN_TARGET)
run_unix: $(UNIX_TARGET)
.\$(UNIX_TARGET)
.PHONY: all clean rebuild run

54
src/engine/camera.c Normal file
View File

@@ -0,0 +1,54 @@
#include "camera.h"
#include "math.h"
#include "vector.h"
void camera_get_view_matrix(const Camera *const camera,
float view_matrix[4][4]) {
float forward[3];
vector_substruct(3, camera->position->coordinates,
camera->target->coordinates, forward);
float normal_forward[3];
vector_normalize(3, forward, normal_forward);
float right[3];
cross_product_vectors(camera->up, forward, right);
float normal_right[3];
vector_normalize(3, right, normal_right);
float up[3];
cross_product_vectors(normal_forward, normal_right, up);
float normal_up[3];
vector_normalize(3, up, normal_up);
float *vectors[] = {normal_right, normal_up, normal_forward};
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
view_matrix[i][j] = 0;
if (i == 3) {
if (j == 3)
view_matrix[i][j] = 1;
} else if (j == 3) {
view_matrix[i][j] =
-1 * dot_product_vectors(vectors[i], camera->position->coordinates);
} else {
view_matrix[i][j] = vectors[i][j];
}
}
}
}
void camera_get_projection_matrix(const Camera *const camera,
float projection_matrix[4][4]) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
projection_matrix[i][j] = 0;
}
}
float f = 1 / tan(camera->fov / 2);
projection_matrix[0][0] = f / camera->aspect_ratio;
projection_matrix[1][1] = f;
projection_matrix[2][2] = (camera->max_distance + camera->min_distance) /
(camera->min_distance - camera->max_distance);
projection_matrix[3][2] = -1;
projection_matrix[2][3] = (2 * camera->max_distance * camera->min_distance) /
(camera->min_distance - camera->max_distance);
}

21
src/engine/camera.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef CAMERA_H
#define CAMERA_H
#include "point.h"
typedef struct Camera {
Point *const position;
Point *const target;
float up[3];
const float fov;
const float max_distance;
const float min_distance;
const float aspect_ratio;
} Camera;
void camera_get_view_matrix(const Camera *const camera,
float view_matrix[4][4]);
void camera_get_projection_matrix(const Camera *const camera,
float projection_matrix[4][4]);
#endif

1
src/engine/engine.c Normal file
View File

@@ -0,0 +1 @@
#include "engine.h"

10
src/engine/engine.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef ENGINE_H
#define ENGINE_H
#include "camera.h"
#include "matrix.h"
#include "object.h"
#include "point.h"
#include "vector.h"
#endif

87
src/engine/matrix.c Normal file
View File

@@ -0,0 +1,87 @@
#include "matrix.h"
#include <math.h>
void matrix_mult_matrix(int size, const float matrix1[size][size],
const float matrix2[size][size],
float result[size][size]) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
result[i][j] = 0;
}
}
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
for (int k = 0; k < size; k++) {
result[i][j] += matrix1[i][k] * matrix2[k][j];
}
}
}
}
void matrix_mult_vector(int size, const float matrix[size][size],
const float vector[size], float result[size]) {
for (int i = 0; i < size; i++)
result[i] = 0;
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
result[i] += matrix[i][j] * vector[j];
}
}
}
void create_axis_rotate_matrix(int axis, float angle,
float rotate_matrix[3][3]) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
rotate_matrix[i][j] = 0;
}
}
float cos_angle = cosf(angle);
float sin_angle = sinf(angle);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == axis || j == axis) {
if (i == j)
rotate_matrix[i][j] = 1;
else
rotate_matrix[i][j] = 0;
} else {
if (i == j) {
rotate_matrix[i][j] = cos_angle;
} else {
if ((i < j && axis == 2) || (i > j && axis != 2)) {
rotate_matrix[i][j] = sin_angle;
} else {
rotate_matrix[i][j] = -sin_angle;
}
}
}
}
}
}
void create_rotate_matrix(const float rotate_speed[3],
float rotate_matrix[3][3]) {
float x_rotate[3][3];
create_axis_rotate_matrix(0, rotate_speed[0], x_rotate);
float y_rotate[3][3];
create_axis_rotate_matrix(1, rotate_speed[1], y_rotate);
float z_rotate[3][3];
create_axis_rotate_matrix(2, rotate_speed[2], z_rotate);
float xy_rotate[3][3];
matrix_mult_matrix(3, x_rotate, y_rotate, xy_rotate);
matrix_mult_matrix(3, xy_rotate, z_rotate, rotate_matrix);
}
// void print_matrix(int size, float matrix[size][size]) {
// for (int i = 0; i < size; i++) {
// printf("(");
// for (int j = 0; j < size; j++) {
// printf("%f", matrix[i][j]);
// if (j != size - 1)
// printf(",\t");
// }
// printf(")\n");
// }
// }

16
src/engine/matrix.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef MATRIX_H
#define MATRIX_H
void matrix_mult_matrix(int size, const float matrix1[size][size],
const float matrix2[size][size],
float result[size][size]);
void matrix_mult_vector(int size, const float matrix[size][size],
const float vector[size], float result[size]);
void create_axis_rotate_matrix(int axis, float angle,
float rotate_matrix[3][3]);
void create_rotate_matrix(const float rotate_speed[3],
float rotate_matrix[3][3]);
// void print_matrix(int size, float matrix[size][size]);
#endif

41
src/engine/object.c Normal file
View File

@@ -0,0 +1,41 @@
#include "object.h"
#include "../utils/screen.h"
#include "vector.h"
void object_transform(Object *const object, int size,
float translate_matrix[size][size]) {
for (int i = 0; i < object->number_of_points; i++) {
point_transform(&(object->points[i]), size, translate_matrix);
}
}
Point object_get_centroid(const Object *const object) {
Point centroid = {{0.0f, 0.0f, 0.0f}};
for (int i = 0; i < object->number_of_points; i++) {
centroid.coordinates[0] += object->points[i].coordinates[0];
centroid.coordinates[1] += object->points[i].coordinates[1];
centroid.coordinates[2] += object->points[i].coordinates[2];
}
float inv_n = 1.0f / (float)object->number_of_points;
centroid.coordinates[0] *= inv_n;
centroid.coordinates[1] *= inv_n;
centroid.coordinates[2] *= inv_n;
return centroid;
}
void object_draw(const Object *object, Screen *screen,
const float render_matrix[4][4]) {
for (int i = 0; i < object->number_of_edges; i++) {
Point point1 = object->points[object->edges[i][0]];
ScreenPoint screen_point1 =
point_convert_to_screen_point(&point1, screen, render_matrix);
Point point2 = object->points[object->edges[i][1]];
ScreenPoint screen_point2 =
point_convert_to_screen_point(&point2, screen, render_matrix);
screen->draw_line(&screen_point1, &screen_point2);
}
}

23
src/engine/object.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef OBJECT_H
#define OBJECT_H
#include "../utils/screen.h"
#include "point.h"
typedef struct Object {
const char *const name;
Point *const points;
const int (*const edges)[2];
const int number_of_points;
const int number_of_edges;
Point *const position;
const float (*const rotate_speed)[3];
} Object;
void object_transform(Object *const object, int size,
float translate_matrix[size][size]);
Point object_get_centroid(const Object *const object);
void object_draw(const Object *object, Screen *screen,
const float render_matrix[4][4]);
#endif

46
src/engine/point.c Normal file
View File

@@ -0,0 +1,46 @@
#include "point.h"
#include "../utils/screen.h"
#include "matrix.h"
void point_transform(Point *const point, int size,
const float translate_matrix[size][size]) {
float new_coordinates[3];
float old_coordinates[] = {point->coordinates[0], point->coordinates[1],
point->coordinates[2], 1.0f};
matrix_mult_vector(size, translate_matrix, old_coordinates, new_coordinates);
for (int i = 0; i < 3; i++)
point->coordinates[i] = new_coordinates[i];
}
void point_create_translate_matrix(const Point *const position,
float translate_matrix[4][4], int k) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
translate_matrix[i][j] = 0;
if (i == j)
translate_matrix[i][j] = 1;
else if (j == 3)
translate_matrix[i][j] = position->coordinates[i] * k;
}
}
}
ScreenPoint point_convert_to_screen_point(Point *point, Screen *screen,
const float render_matrix[4][4]) {
float tmp[] = {point->coordinates[0], point->coordinates[1],
point->coordinates[2], 1.0f};
float point_projection_view[4];
matrix_mult_vector(4, render_matrix, tmp, point_projection_view);
float perspective_coordinates[3] = {
point_projection_view[0] / point_projection_view[3],
point_projection_view[1] / point_projection_view[3],
point_projection_view[2] / point_projection_view[3],
};
ScreenPoint spoint = {
.coordinates = {((perspective_coordinates[0] + 1.0f) / 2.0f) *
(float)screen->width,
((perspective_coordinates[1] + 1.0f) / 2.0f) *
(float)screen->height}};
return spoint;
}

17
src/engine/point.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef POINT_H
#define POINT_H
#include "../utils/screen.h"
typedef struct Point {
float coordinates[3];
} Point;
void point_transform(Point *const point, int size,
const float translate_matrix[size][size]);
void point_create_translate_matrix(const Point *const position,
float translate_matrix[4][4], int k);
ScreenPoint point_convert_to_screen_point(Point *point, Screen *screen,
const float render_matrix[4][4]);
#endif

35
src/engine/vector.c Normal file
View File

@@ -0,0 +1,35 @@
#include "vector.h"
#include <math.h>
void vector_substruct(int size, const float v1[size], const float v2[size],
float r[size]) {
for (int i = 0; i < size; i++)
r[i] = v1[i] - v2[i];
}
void cross_product_vectors(const float v1[3], const float v2[3], float r[3]) {
r[0] = v1[1] * v2[2] - v1[2] * v2[1];
r[1] = v1[2] * v2[0] - v1[0] * v2[2];
r[2] = v1[0] * v2[1] - v1[1] * v2[0];
}
float dot_product_vectors(const float v1[3], const float v2[3]) {
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
void vector_normalize(int size, const float v[size], float r[size]) {
float divider = 0;
for (int i = 0; i < size; i++)
divider += v[i] * v[i];
divider = sqrt(divider);
for (int i = 0; i < size; i++)
r[i] = v[i] / divider;
}
// void print_vector(int size, float vector[size]) {
// printf("(");
// for (int i = 0; i < size; i++) {
// printf("%f", vector[i]);
// if (i != size - 1)
// printf(",\t");
// }
// printf(")\n");
// }

11
src/engine/vector.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef VECTOR_H
#define VECTOR_H
void vector_substruct(int size, const float v1[size], const float v2[size],
float r[size]);
void cross_product_vectors(const float v1[3], const float v2[3], float r[3]);
float dot_product_vectors(const float v1[3], const float v2[3]);
void vector_normalize(int size, const float v[size], float r[size]);
// void print_vector(int size, float vector[size]);
#endif

75
src/main.c Normal file
View File

@@ -0,0 +1,75 @@
#include <math.h>
#include <stdio.h>
#include "engine/engine.h"
// #define WIN
// #define UNIX
// #define MICRO
#include "platforms/platform.h"
#include "objects.h"
float render_matrix[4][4];
void init_engine() {
printff(BLUE ITALIC, "Engine init...");
float view_matrix[4][4];
camera_get_view_matrix(&camera, view_matrix);
printf("View matrix created\n");
float projection_matrix[4][4];
camera_get_projection_matrix(&camera, projection_matrix);
printf("Projection matrix created\n");
matrix_mult_matrix(4, projection_matrix, view_matrix, render_matrix);
printf("Render matrix created\n");
for (int i = 0; i < number_of_objects; i++) {
float translate_matrix[4][4];
point_create_translate_matrix(objects[i].position, translate_matrix, 1);
object_transform(&(objects[i]), 4, translate_matrix);
printf("Go %s to (%f, %f, %f)\n", objects[i].name,
objects[i].position->coordinates[0],
objects[i].position->coordinates[1],
objects[i].position->coordinates[2]);
}
printff(BLUE BOLD UNDERLINE, "Engine init complete!");
}
void render(Screen screen) {
for (int i = 0; i < number_of_objects; i++) {
object_draw(&(objects[i]), &screen, render_matrix);
}
}
void tic() {
for (int i = 0; i < number_of_objects; i++) {
float reverse_translate_matrix[4][4];
point_create_translate_matrix(objects[i].position, reverse_translate_matrix,
-1);
object_transform(&(objects[i]), 4, reverse_translate_matrix);
Point center_of_object = object_get_centroid(&(objects[i]));
float reverse_center_translate_matrix[4][4];
point_create_translate_matrix(&center_of_object,
reverse_center_translate_matrix, -1);
object_transform(&(objects[i]), 4, reverse_center_translate_matrix);
float rotate_matrix[3][3];
create_rotate_matrix(*(objects[i].rotate_speed), rotate_matrix);
object_transform(&(objects[i]), 3, rotate_matrix);
float center_translate_matrix[4][4];
point_create_translate_matrix(&center_of_object, center_translate_matrix,
1);
object_transform(&(objects[i]), 4, center_translate_matrix);
float translate_matrix[4][4];
point_create_translate_matrix(objects[i].position, translate_matrix, 1);
object_transform(&(objects[i]), 4, translate_matrix);
}
}
void destroy() {}

70
src/objects.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef OBJECTS_H
#define OBJECTS_H
#include "engine/engine.h"
Point cube_points[] = {
{.coordinates = {0.0f, 0.0f, 0.0f}}, {.coordinates = {1.0f, 0.0f, 0.0f}},
{.coordinates = {1.0f, 1.0f, 0.0f}}, {.coordinates = {0.0f, 1.0f, 0.0f}},
{.coordinates = {0.0f, 0.0f, 1.0f}}, {.coordinates = {1.0f, 0.0f, 1.0f}},
{.coordinates = {1.0f, 1.0f, 1.0f}}, {.coordinates = {0.0f, 1.0f, 1.0f}}};
Point cube_position = {.coordinates = {3.0f, 6.0f, 5.0f}};
const int cube_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6},
{6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}};
const float cube_speed[] = {0.03f, 0.0f, 0.0f};
const Object cube = {.name = "cube",
.points = cube_points,
.edges = cube_edges,
.number_of_points = 8,
.number_of_edges = 12,
.position = &cube_position,
.rotate_speed = &cube_speed};
Point pyramid_points[] = {{.coordinates = {-1.0f, 0.0f, -1.0f}},
{.coordinates = {1.0f, 0.0f, -1.0f}},
{.coordinates = {1.0f, 0.0f, 1.0f}},
{.coordinates = {-1.0f, 0.0f, 1.0f}},
{.coordinates = {0.0f, 1.5f, 0.0f}}};
Point pyramid_position = {.coordinates = {6.0f, 3.0f, 5.0f}};
const int pyramid_edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0},
{0, 4}, {1, 4}, {2, 4}, {3, 4}};
const float pyramid_speed[] = {0.0f, 0.0f, 0.03f};
const Object pyramid = {.name = "pyramid",
.points = pyramid_points,
.edges = pyramid_edges,
.number_of_points = 5,
.number_of_edges = 8,
.position = &pyramid_position,
.rotate_speed = &pyramid_speed};
Point prism_points[] = {
{.coordinates = {0.0f, 0.0f, 0.0f}}, {.coordinates = {1.0f, 0.0f, 0.0f}},
{.coordinates = {0.5f, 0.0f, 1.0f}}, {.coordinates = {0.0f, 1.0f, 0.0f}},
{.coordinates = {1.0f, 1.0f, 0.0f}}, {.coordinates = {0.5f, 1.0f, 1.0f}}};
Point prism_position = {.coordinates = {10.0f, 10.0f, 10.0f}};
const int prism_edges[][2] = {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5},
{5, 3}, {0, 3}, {1, 4}, {2, 5}};
const float prism_speed[] = {0.0f, 0.03f, 0.0f};
const Object prism = {.name = "prism",
.points = prism_points,
.edges = prism_edges,
.number_of_points = 6,
.number_of_edges = 9,
.position = &prism_position,
.rotate_speed = &prism_speed};
const int number_of_objects = 3;
Object objects[] = {cube, pyramid, prism};
Point camera_position = {.coordinates = {0.0f, 0.0f, 0.0f}};
Point camera_target = {.coordinates = {1.0f, 1.0f, 1.0f}};
const Camera camera = {.position = &camera_position,
.target = &camera_target,
.up = {-1.0f, -1.0f, 1.0f},
.fov = 120.0f,
.max_distance = 20.0f,
.min_distance = .0f,
.aspect_ratio = WINDOW_WIDTH / WINDOW_HEIGHT};
#endif

75
src/platforms/log.h Normal file
View File

@@ -0,0 +1,75 @@
#ifndef LOG_H
#define LOG_H
#if defined(WIN) || defined(UNIX)
#include <stdio.h>
// Определения цветов и стилей
#define RESET "\033[0m"
#define BOLD "\033[1m"
#define ITALIC "\033[3m"
#define UNDERLINE "\033[4m"
// Цвета текста
#define BLACK "\033[30m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define YELLOW "\033[33m"
#define BLUE "\033[34m"
#define MAGENTA "\033[35m"
#define CYAN "\033[36m"
#define WHITE "\033[37m"
// Фоновые цвета
#define BG_BLACK "\033[40m"
#define BG_RED "\033[41m"
#define BG_GREEN "\033[42m"
#define BG_YELLOW "\033[43m"
#define BG_BLUE "\033[44m"
#define BG_MAGENTA "\033[45m"
#define BG_CYAN "\033[46m"
#define BG_WHITE "\033[47m"
#define printff(format_codes, ...) \
do { \
printf("%s", format_codes); \
printf(__VA_ARGS__); \
printf("\033[0m\n"); \
} while (0)
#ifdef DEBUG_MODE
#define logf(fmt, ...) \
do { \
printf("[%s:%d] ", __FILE__, __LINE__); \
printf(fmt, ##__VA_ARGS__); \
printf("\n"); \
} while (0)
#define logff(format_codes, fmt, ...) \
do { \
printf("%s", format_codes); \
printf("[%s:%d] ", __FILE__, __LINE__); \
printf(fmt, ##__VA_ARGS__); \
printf("\n"); \
printf("\033[0m\n"); \
} while (0)
#define loge(fmt, ...) logff(RED UNDERLINE, fmt, ##__VA_ARGS__)
#else
#define logf(fmt, ...)
#define logff(format_codes, fmt, ...)
#define loge(fmt, ...)
#endif // DEBUG_MODE
#elif MICRO
#define printff(format_codes, ...) // TODO
#ifdef DEBUG_MODE
#define logf(fmt, ...) // TODO
#define logff(format_codes, fmt, ...) // TODO
#define loge(fmt, ...) logff(RED UNDERLINE, fmt, ##__VA_ARGS__) // TODO
#else
#define logf(fmt, ...)
#define logff(format_codes, fmt, ...)
#define loge(fmt, ...)
#endif // DEBUG_MODE
#endif // WIN || UNIX || MICRO
#endif // UTILS_STYLES_H

22
src/platforms/platform.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef PLATFORM_H
#define PLATFORM_H
#include "../utils/screen.h"
#include "log.h"
#ifdef WIN
#include "win.h"
#define FPS 10
#define WINDOW_WIDTH 800.0f
#define WINDOW_HEIGHT 600.0f
#define BG_COLOR BLACK_BRUSH
#define COLOR RGB(255, 255, 255)
#endif
void init_engine();
void render(Screen screen);
void tic();
void destroy();
#endif

88
src/platforms/win.c Normal file
View File

@@ -0,0 +1,88 @@
#include "platform.h"
#include "win.h"
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
SetTimer(hwnd, 1, 50, NULL);
init_engine();
return 0;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
FillRect(hdc, &rect, (HBRUSH)GetStockObject(BG_COLOR));
float width = rect.right - rect.left;
float height = rect.bottom - rect.top;
HPEN hPen = CreatePen(PS_SOLID, 1, COLOR);
HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
void draw_line(const ScreenPoint *sp1, const ScreenPoint *sp2) {
MoveToEx(hdc, sp1->coordinates[0], sp1->coordinates[1], NULL);
LineTo(hdc, sp2->coordinates[0], sp2->coordinates[1]);
};
Screen screen = {.width = width, .height = height, .draw_line = &draw_line};
render(screen);
SelectObject(hdc, hOldPen);
DeleteObject(hPen);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_TIMER:
tic();
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_KEYDOWN:
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
const char CLASS_NAME[] = "C3DGraphicEngine";
WNDCLASS wc = {0};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, CLASS_NAME, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}

6
src/platforms/win.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef WIN_H
#define WIN_H
#include <windows.h>
#endif

16
src/utils/screen.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef UTILS_H
#define UTILS_H
struct ScreenPoint {
int coordinates[2];
};
typedef struct ScreenPoint ScreenPoint;
struct Screen {
int width;
int height;
void (*draw_line)(const ScreenPoint *sp1, const ScreenPoint *sp2);
};
typedef struct Screen Screen;
#endif

View File

@@ -1,246 +0,0 @@
#include "utils.h"
#include <math.h>
#include <stdio.h>
#include <windows.h>
// === Vectors ===
void vector_substruct(int size, float v1[size], float v2[size], float r[size]) {
for (int i = 0; i < size; i++)
r[i] = v1[i] - v2[i];
}
void cross_product_vectors(float v1[3], float v2[3], float r[3]) {
r[0] = v1[1] * v2[2] - v1[2] * v2[1];
r[1] = v1[2] * v2[0] - v1[0] * v2[2];
r[2] = v1[0] * v2[1] - v1[1] * v2[0];
}
float dot_product_vectors(float v1[3], float v2[3]) {
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
void vector_normalize(int size, float coordinates[size]) {
float divider = 0;
for (int i = 0; i < size; i++)
divider += coordinates[i] * coordinates[i];
divider = sqrt(divider);
float new_coordinates[size];
for (int i = 0; i < size; i++)
coordinates[i] /= divider;
}
void print_vector(int size, float vector[size]) {
printf("(");
for (int i = 0; i < size; i++) {
printf("%f", vector[i]);
if (i != size - 1)
printf(",\t");
}
printf(")\n");
}
// === Matrices ===
void print_matrix(int size, float matrix[size][size]) {
for (int i = 0; i < size; i++) {
printf("(");
for (int j = 0; j < size; j++) {
printf("%f", matrix[i][j]);
if (j != size - 1)
printf(",\t");
}
printf(")\n");
}
}
void matrix_mult_matrix(int size, float matrix1[size][size],
float matrix2[size][size], float result[size][size]) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
result[i][j] = 0;
}
}
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
for (int k = 0; k < size; k++) {
result[i][j] += matrix1[i][k] * matrix2[k][j];
}
}
}
}
void matrix_mult_vector(int size, float matrix[size][size], float vector[size],
float result[size]) {
for (int i = 0; i < size; i++)
result[i] = 0;
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
result[i] += matrix[i][j] * vector[j];
}
}
}
void create_axis_rotate_matrix(int axis, float angle,
float rotate_matrix[3][3]) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
rotate_matrix[i][j] = 0;
}
}
float cos_angle = cosf(angle);
float sin_angle = sinf(angle);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == axis || j == axis) {
if (i == j)
rotate_matrix[i][j] = 1;
else
rotate_matrix[i][j] = 0;
} else {
if (i == j) {
rotate_matrix[i][j] = cos_angle;
} else {
if ((i < j && axis == 2) || (i > j && axis != 2)) {
rotate_matrix[i][j] = sin_angle;
} else {
rotate_matrix[i][j] = -sin_angle;
}
}
}
}
}
}
void create_rotate_matrix(float rotate_speed[3], float rotate_matrix[3][3]) {
float x_rotate[3][3];
create_axis_rotate_matrix(0, rotate_speed[0], x_rotate);
float y_rotate[3][3];
create_axis_rotate_matrix(1, rotate_speed[1], y_rotate);
float z_rotate[3][3];
create_axis_rotate_matrix(2, rotate_speed[2], z_rotate);
float xy_rotate[3][3];
matrix_mult_matrix(3, x_rotate, y_rotate, xy_rotate);
matrix_mult_matrix(3, xy_rotate, z_rotate, rotate_matrix);
}
// === Points in scene ===
void transform_point(Point3D *point, int size,
float translate_matrix[size][size]) {
float new_coordinates[3];
float old_coordinates[] = {point->coordinates[0], point->coordinates[1],
point->coordinates[2], 1.0f};
matrix_mult_vector(size, translate_matrix, old_coordinates, new_coordinates);
for (int i = 0; i < 3; i++)
point->coordinates[i] = new_coordinates[i];
}
void create_translate_matrix(Point3D *position, float translate_matrix[4][4],
int k) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
translate_matrix[i][j] = 0;
if (i == j)
translate_matrix[i][j] = 1;
else if (j == 3)
translate_matrix[i][j] = position->coordinates[i] * k;
}
}
}
// === Points on screen ===
ScreenPoint convert_to_screen_point(Point3D *point, Screen *screen) {
float tmp[] = {point->coordinates[0], point->coordinates[1],
point->coordinates[2], 1.0f};
float point_projection_view[4];
matrix_mult_vector(4, *(screen->render_matrix), tmp, point_projection_view);
float perspective_coordinates[3] = {
point_projection_view[0] / point_projection_view[3],
point_projection_view[1] / point_projection_view[3],
point_projection_view[2] / point_projection_view[3],
};
ScreenPoint spoint = {
.coordinates = {((perspective_coordinates[0] + 1.0f) / 2.0f) *
(float)screen->width,
((perspective_coordinates[1] + 1.0f) / 2.0f) *
(float)screen->height}};
return spoint;
}
void draw_line(Screen *screen, ScreenPoint *sp1, ScreenPoint *sp2) {
MoveToEx(*(screen->hdc), sp1->coordinates[0], sp1->coordinates[1], NULL);
LineTo(*(screen->hdc), sp2->coordinates[0], sp2->coordinates[1]);
}
// === Camera ===
void create_view_matrix(Camera *camera, float view_matrix[4][4]) {
float forward[3];
vector_substruct(3, camera->position.coordinates, camera->target.coordinates,
forward);
vector_normalize(3, forward);
float right[3];
cross_product_vectors(camera->up, forward, right);
vector_normalize(3, right);
float up[3];
cross_product_vectors(forward, right, up);
vector_normalize(3, up);
float *vectors[] = {right, up, forward};
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
view_matrix[i][j] = 0;
if (i == 3) {
if (j == 3)
view_matrix[i][j] = 1;
} else if (j == 3) {
view_matrix[i][j] =
-1 * dot_product_vectors(vectors[i], camera->position.coordinates);
} else {
view_matrix[i][j] = vectors[i][j];
}
}
}
}
void create_projection_matrix(Camera *camera, float projection_matrix[4][4]) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
projection_matrix[i][j] = 0;
}
}
float f = 1 / tan(camera->fov / 2);
projection_matrix[0][0] = f / camera->aspect_ratio;
projection_matrix[1][1] = f;
projection_matrix[2][2] = (camera->max_distance + camera->min_distance) /
(camera->min_distance - camera->max_distance);
projection_matrix[3][2] = -1;
projection_matrix[2][3] = (2 * camera->max_distance * camera->min_distance) /
(camera->min_distance - camera->max_distance);
}
// === Composite objects ===
void transform_object(const Object *object, int size,
float translate_matrix[size][size]) {
for (int i = 0; i < object->number_of_points; i++) {
transform_point(&(object->points[i]), size, translate_matrix);
}
}
void draw_object(const Object *object, Screen *screen) {
for (int i = 0; i < object->number_of_edges; i++) {
Point3D point1 = object->points[object->edges[i][0]];
ScreenPoint screen_point1 = convert_to_screen_point(&point1, screen);
Point3D point2 = object->points[object->edges[i][1]];
ScreenPoint screen_point2 = convert_to_screen_point(&point2, screen);
draw_line(screen, &screen_point1, &screen_point2);
}
}
Point3D calculate_centroid(const Object *object) {
Point3D centroid = {{0.0f, 0.0f, 0.0f}};
for (int i = 0; i < object->number_of_points; i++) {
centroid.coordinates[0] += object->points[i].coordinates[0];
centroid.coordinates[1] += object->points[i].coordinates[1];
centroid.coordinates[2] += object->points[i].coordinates[2];
}
float inv_n = 1.0f / (float)object->number_of_points;
centroid.coordinates[0] *= inv_n;
centroid.coordinates[1] *= inv_n;
centroid.coordinates[2] *= inv_n;
return centroid;
}

View File

@@ -1,80 +0,0 @@
#ifndef OBJECTS_H
#define OBJECTS_H
#include <windows.h>
// === Vectors ===
void vector_substruct(int size, float v1[size], float v2[size], float r[size]);
void cross_product_vectors(float v1[3], float v2[3], float r[3]);
float dot_product_vectors(float v1[3], float v2[3]);
void vector_normalize(int size, float coordinates[size]);
void print_vector(int size, float vector[size]);
// === Matrix ===
void print_matrix(int size, float matrix[size][size]);
void matrix_mult_matrix(int size, float matrix1[size][size],
float matrix2[size][size], float result[size][size]);
void matrix_mult_vector(int size, float matrix[size][size], float vector[size],
float result[size]);
void create_axis_rotate_matrix(int axis, float angle,
float rotate_matrix[3][3]);
void create_rotate_matrix(float rotate_speed[3], float rotate_matrix[3][3]);
// === Point in scene ===
struct Point3D {
float coordinates[3];
};
typedef struct Point3D Point3D;
void transform_point(Point3D *point, int size,
float translate_matrix[size][size]);
void create_translate_matrix(Point3D *position, float translate_matrix[4][4],
int k);
// === Screen summary ===
struct Screen {
int width;
int height;
HDC *hdc;
float (*render_matrix)[4][4];
};
typedef struct Screen Screen;
// === Point on screen ===
struct ScreenPoint {
int coordinates[2];
};
typedef struct ScreenPoint ScreenPoint;
ScreenPoint convert_to_screen_point(Point3D *point, Screen *screen);
void draw_line(Screen *screen, ScreenPoint *sp1, ScreenPoint *sp2);
// === Camera in scene ===
struct Camera {
Point3D position;
Point3D target;
float up[3];
float fov;
float max_distance;
float min_distance;
float aspect_ratio;
};
typedef struct Camera Camera;
void create_view_matrix(Camera *camera, float view_matrix[4][4]);
void create_projection_matrix(Camera *camera, float projection_matrix[4][4]);
// === Composite object in scene ===
struct Object {
Point3D *points;
int (*edges)[2];
int number_of_points;
int number_of_edges;
Point3D *position;
float (*rotate_speed)[3];
};
typedef struct Object Object;
void transform_object(const Object *object, int size,
float translate_matrix[size][size]);
void draw_object(const Object *object, Screen *screen);
Point3D calculate_centroid(const Object *object);
#endif