From 10a973e2f177848ab3a337f985b06e2ca5f4ef64 Mon Sep 17 00:00:00 2001 From: "Thomas \"Cakeisalie5\" Touhey" Date: Tue, 7 Mar 2017 01:17:53 +0100 Subject: [PATCH] Initial commit - that's out of the way. --- .gitignore | 4 + AUTHORS.md | 6 + CONTRIBUTING.md | 3 + LICENSE.md | 157 ++++++++++++++++++++++++++ Makefile | 120 ++++++++++++++++++++ Makefile.msg | 58 ++++++++++ Makefile.vars | 82 ++++++++++++++ README.md | 48 ++++++++ configure | 96 ++++++++++++++++ include/libwindmill.h | 156 ++++++++++++++++++++++++++ include/libwindmill/internals.h | 27 +++++ src/draw.c | 191 ++++++++++++++++++++++++++++++++ src/dynamic.c | 69 ++++++++++++ src/render.c | 121 ++++++++++++++++++++ src/update.c | 72 ++++++++++++ 15 files changed, 1210 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.md create mode 100755 Makefile create mode 100755 Makefile.msg create mode 100755 Makefile.vars create mode 100644 README.md create mode 100755 configure create mode 100644 include/libwindmill.h create mode 100644 include/libwindmill/internals.h create mode 100644 src/draw.c create mode 100644 src/dynamic.c create mode 100644 src/render.c create mode 100644 src/update.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..868e92c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/Makefile.cfg +/obj +/lib*.a +/lib*-*.tar.gz diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..d8e871e --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,6 @@ +# libwindmill authors +Copyright (C) 2017 Olivier "Ninestars" Lanneau +Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + +Ninestars was the one thinking and making the actual code, I (Cakeisalie5) +just came along and reorganized the project. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6e2b30e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to libwindmill +For now, external contributions aren't allowed, as the project is in +very alpha. Maybe later :) diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..408c98d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,157 @@ +### GNU LESSER GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the +terms and conditions of version 3 of the GNU General Public License, +supplemented by the additional permissions listed below. + +#### 0. Additional Definitions. + +As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the +GNU General Public License. + +"The Library" refers to a covered work governed by this License, other +than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + +The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + +#### 1. Exception to Section 3 of the GNU GPL. + +You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + +#### 2. Conveying Modified Versions. + +If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + +- a) under this License, provided that you make a good faith effort + to ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or +- b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + +#### 3. Object Code Incorporating Material from Library Header Files. + +The object code form of an Application may incorporate material from a +header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + +- a) Give prominent notice with each copy of the object code that + the Library is used in it and that the Library and its use are + covered by this License. +- b) Accompany the object code with a copy of the GNU GPL and this + license document. + +#### 4. Combined Works. + +You may convey a Combined Work under terms of your choice that, taken +together, effectively do not restrict modification of the portions of +the Library contained in the Combined Work and reverse engineering for +debugging such modifications, if you also do each of the following: + +- a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. +- b) Accompany the Combined Work with a copy of the GNU GPL and this + license document. +- c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. +- d) Do one of the following: + - 0) Convey the Minimal Corresponding Source under the terms of + this License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + - 1) Use a suitable shared library mechanism for linking with + the Library. A suitable mechanism is one that (a) uses at run + time a copy of the Library already present on the user's + computer system, and (b) will operate properly with a modified + version of the Library that is interface-compatible with the + Linked Version. +- e) Provide Installation Information, but only if you would + otherwise be required to provide such information under section 6 + of the GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the Application + with a modified version of the Linked Version. (If you use option + 4d0, the Installation Information must accompany the Minimal + Corresponding Source and Corresponding Application Code. If you + use option 4d1, you must provide the Installation Information in + the manner specified by section 6 of the GNU GPL for conveying + Corresponding Source.) + +#### 5. Combined Libraries. + +You may place library facilities that are a work based on the Library +side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + +- a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities, conveyed under the terms of this License. +- b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + +#### 6. Revised Versions of the GNU Lesser General Public License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +as you received it specifies that a certain numbered version of the +GNU Lesser General Public License "or any later version" applies to +it, you have the option of following the terms and conditions either +of that published version or of any later version published by the +Free Software Foundation. If the Library as you received it does not +specify a version number of the GNU Lesser General Public License, you +may choose any version of the GNU Lesser General Public License ever +published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..024a220 --- /dev/null +++ b/Makefile @@ -0,0 +1,120 @@ +#!/usr/bin/make -f +#******************************************************************************# +# Include variables and message subsystem # +#******************************************************************************# +include Makefile.vars Makefile.msg + +#******************************************************************************# +# General targets # +#******************************************************************************# +# Build everything. +all: all-lib + +# Mostly clean everything. (remove everything but the end results) +mostlyclean: mostlyclean-lib + mclean: mostlyclean + +# Clean everything. +clean fclean: clean-lib + $(call qcmd,$(RM) -r lib$(NAME)-*) + +# Clean everything, and configuration. +mrproper: clean + $(call rmsg,Removing configuration.) + $(call qcmd,$(RM) Makefile.cfg) + +# Make a distribution tarball +dist: mrproper + $(call bcmd,mkdir,lib$(NAME)-$(VERSION),\ + $(MD) .dist) + $(call bcmd,cp,* lib$(NAME)-$(VERSION),\ + $(CP) -R * .dist) + $(call qcmd,\ + $(MV) .dist lib$(NAME)-$(VERSION)) + $(call bcmd,tarball,lib$(NAME)-$(VERSION),\ + tar czf lib$(NAME)-$(VERSION).tar.gz \ + --exclude .git lib$(NAME)-$(VERSION)) + $(call qcmd,$(RM) -r lib$(NAME)-$(VERSION)) + +# Remake everything. (clean and build) +re: clean all + +.PHONY: all mostlyclean mclean clean fclean dist mrproper re +#******************************************************************************# +# Configuration (version) checking dependencies # +#******************************************************************************# +# Define the dependencies. + CHECKCFG := $(if $(shell test -f Makefile.cfg || echo y),check-config, \ + $(if $(shell [ "$(VERSION)" = "$(CONFIG_VERSION)" ] || echo y), \ + check-config-version)) + +# Define the rules. + check-config: + @echo -e "\033[1;31mNo configuration file found!" + @echo -e "You should configure before re-running this target.\033[0m" + @false + check-config-version: + @echo -e "\033[1;31mConfiguration version is incorrect!" + @echo -e "You should re-configure before re-running this target.\033[0m" + @false + +.PHONY: check-config check-config-version +#******************************************************************************# +# Information getting from the Makefile variables # +#******************************************************************************# +# Get the project name. + getname: + @echo lib$(NAME) + +# Get the maintainer + getmaintainer: + @echo "$(MAINTAINER_NAME) <$(MAINTAINER_MAIL)>" + +# Get the project version. + getversion: + @echo "$(VERSION)" + +.PHONY: getname getmaintainer getversion +#******************************************************************************# +# Library-specific targets # +#******************************************************************************# +# Make the library. + all-lib: $(CHECKCFG) lib$(NAME).a + +# Make an object directory. + $(OBJDIR)/ $(DIRS:%=$(OBJDIR)/%): + $(call bcmd,mkdir,$@,$(MD) $@) + +# Make an object out of a source file. +define make-obj-rules +ifeq ($(shell test -f $(SRCDIR)/$1.c && echo y),y) + $(OBJDIR)/$1.o: $(SRCDIR)/$1.c $(INC) | $(dir $(OBJDIR)/$1) + $(call bcmd,cc,$$@,$(CC) -c -o $$@ $$< $(CFLAGS)) +else + $(OBJDIR)/$1.o: $(SRCDIR)/$1.s | $(dir $(OBJDIR)/$1) + $(call bcmd,as,$$@,$(AS) -c -o $$@ $$< $(ASFLAGS)) +endif +endef +$(foreach src,$(SRC), \ +$(eval $(call make-obj-rules,$(src)))) + +# Make the library. + lib$(NAME).a: $(SRC:%=$(OBJDIR)/%.o) + $(call bcmd,ar rcs,$@,$(AR) rc $@ $^) + +# Remove the objects directory. + mostlyclean-lib: + $(call rmsg,Removing object directory.) + $(call qcmd,$(RM) -r $(OBJDIR)) + mclean-lib: mostlyclean-lib + +# Clean and remove the built library. + clean-lib: mclean-lib + $(call rmsg,Removing the library.) + $(call qcmd,$(RM) lib$(NAME).a) + +# Remake the library. + re-lib: clean-lib all-lib + +.PHONY: all-lib mostlyclean-lib mclean-lib clean-lib re-lib +# End of file. diff --git a/Makefile.msg b/Makefile.msg new file mode 100755 index 0000000..186d90a --- /dev/null +++ b/Makefile.msg @@ -0,0 +1,58 @@ +#!/usr/bin/make -f +# The Makefile message subsystem. +# For nice logs. 5 dollars per log only. +#******************************************************************************# +# Colors and misc # +#******************************************************************************# +# Used colors ANSI modifiers escape codes + color_green := 32 + color_red := 31 + color_yellow := 33 + +# Newline - comes handy in some situations +define \n + + +endef + +#******************************************************************************# +# General messages # +#******************************************************************************# +# Command message - display basic info about the command, and run it. +define cmd +@$(if $(MAKE_FULL_LOG),,printf "\033[1;""$4""m>\033[0m \033[1m%s\033[0m %s\n" "$1" "$2";) + $(if $(MAKE_FULL_LOG),,@)$3 +endef + +# Quiet command - make it non-quiet if full log is enabled. +define qcmd +$(if $(MAKE_FULL_LOG),,@)$1 +endef + +# Normal message - display it. +define msg +$(if $(MAKE_FULL_LOG),,@printf "\033[1;""$2""m>\033[0m \033[1m%s\033[0m\n" "$1") +endef + +#******************************************************************************# +# Commands # +#******************************************************************************# +# Build command +define bcmd +$(call cmd,$1,$2,$3,$(color_green)) +endef + +#******************************************************************************# +# Messages # +#******************************************************************************# +# Remove message +define rmsg +$(call msg,$1,$(color_red)) +endef + +# Install message +define imsg +$(call msg,$1,$(color_yellow)) +endef + +# End of file diff --git a/Makefile.vars b/Makefile.vars new file mode 100755 index 0000000..78ac0cf --- /dev/null +++ b/Makefile.vars @@ -0,0 +1,82 @@ +#!/usr/bin/make -f +#******************************************************************************# +# Include configuration # +#******************************************************************************# +-include Makefile.cfg + +#******************************************************************************# +# Project main information # +#******************************************************************************# +# Project name and supported targets + NAME := windmill + +# Maintainer information + MAINTAINER_NAME := Thomas \"Cakeisalie5\" Touhey + MAINTAINER_MAIL := thomas@touhey.fr + +# Project version + MAJOR := 0 + MINOR := 1 + INDEV := yes + +# Project version string + VERSION := $(MAJOR).$(MINOR)$(if $(INDEV),-indev) + +#******************************************************************************# +# Project directories # +#******************************************************************************# +# Headers directory - where all the headers are. + INCDIR := ./include + +# Sources directory - where all the sources are. + SRCDIR := ./src + +# Objects directory - where the objects will be put. + OBJDIR := ./obj + +#******************************************************************************# +# Binary utilities # +#******************************************************************************# +# Compiler + CC := sh3eb-elf-gcc +# - Check flags (warnings) + CWARN := -Wall -Wextra -Wno-attributes +# - For random manipulations (profiling, ...) +#CMOREFLAGS := +# - All C compiling flags + CFLAGS := $(CWARN) -I $(INCDIR) -std=gnu11 -O2 -ffreestanding + +# Assembler + AS := sh3eb-elf-as +# - All assembling flags + ASFLAGS := + +# Archiver + AR := sh3eb-elf-ar +# Directory maker + MD := mkdir -p +# Symbolic link maker + LN := ln -sf +# Copier + CP := cp +# Mover + MV := mv +# File remover + RM := rm -f +# Installer + INST := install +# GZipper + GZIP := gzip -f + +#******************************************************************************# +# Look for modules and modules sources # +#******************************************************************************# +# Look up the sources + SRC := $(basename $(shell find $(SRCDIR) -mindepth 1 -type f \ + -name "*.[cs]" -printf "%P\n")) + DIRS := $(sort $(dir $(SRC))) + +# Look up the includes + INC := $(shell find $(INCDIR) -mindepth 1 -name "*.h") + +# End of file. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f14fb77 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# libwindmill - a 3D engine for your CASIO fx-9860G +## Introduction +Windmill is a 3D engine project by Ninestars, originally developed in C++ and +built with the CASIO fx-9860G SDK. It was adapted to compile with GNU +utilities by Cakeisalie5. + +This 3D engine is made to be easy to use, and modular, in order to quickly +create a game, or to use it in an existing project. +It features the following: +* Calculates coordinates to display them on screen; +* Camera that can move in any direction and turn around using two axes; +* Management of display windows; +* Depth buffer so objects hidden behind others aren't displayed; +* Display of textures defined as images; +* Simplified management of meshes and textures; +* Display of fixed and dynamic objects that can turn following three axes; +* Simultaneous renders. + +Windmill is only a graphical engine, it only displays predefined triangles +in space on the screen, which means it doesn't manage collisions between +objects, or between an object and the camera. + +## Requirements/setup +This project is easier to build under GNU/Linux. It might be possible to +build it for Microsoft Windows, but this host platform is not officially +supported. + +You will need [GCC][gcc] and [GNU Binutils][binutils], both compiled for the +`sh3eb-elf` target, and the [G1A wrapper][wrapper]. A french tutorial of how +to make and install all of these can be found [on Planète Casio][pc-gcc-tuto] +(steps 1 to 6 included). + +## Build +Just type in the following: + + ./configure && make + +## Miscellaneous information +For the authors of the project, check `AUTHORS.md`. +For the license of the project, check `LICENSE.md`. + +If you want to contribute to this project, check the contribution guide +in the `CONTRIBUTING.md` file. + +[gcc]: https://gcc.gnu.org/ +[binutils]: https://www.gnu.org/software/binutils/ +[wrapper]: https://bitbucket.org/Lephenixnoir/add-in-wrapper.git +[pc-gcc-tuto]: http://www.planet-casio.com/Fr/programmation/tutoriels.php?id=61 diff --git a/configure b/configure new file mode 100755 index 0000000..5892fd1 --- /dev/null +++ b/configure @@ -0,0 +1,96 @@ +#!/bin/sh +cd "$(dirname $0)" +#******************************************************************************# +# Defaults # +#******************************************************************************# +# Project variables +name="$(make -s getname)" +version="$(make -s getversion)" + +# Maintainer info +maintainer="$(make -s getmaintainer)" + +# Make options +make_full_log= + +#******************************************************************************# +# Help message # +#******************************************************************************# +usage() { +cat </dev/null 2>/dev/null + +# Do it! +exec 3>&1 1>Makefile.cfg +cat <&3 3>&- +chmod +x Makefile.cfg + +# Put the lil' message. +echo "Configuration loaded, you can make now." + +# End of file. diff --git a/include/libwindmill.h b/include/libwindmill.h new file mode 100644 index 0000000..4e8ecb3 --- /dev/null +++ b/include/libwindmill.h @@ -0,0 +1,156 @@ +/* ***************************************************************************** + * libwindmill.h -- 3D rendering engine. + * Copyright (C) 2017 Olivier "Ninestars" Lanneau + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libwindmill. + * libwindmill is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libwindmill is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libwindmill; if not, see . + * ************************************************************************** */ +#ifndef LIBWINDMILL_H +# define LIBWINDMILL_H +# include +# include +# include +# define N 0 +# define X 1 +# define Y 2 +# define Z 3 +# define XC 4 +# define YC 5 +# define ZC 6 + +/* ************************************************************************** */ +/* Types */ +/* ************************************************************************** */ +/* a vertex */ +typedef struct Vertex { + int x, y, z; + char tx, ty; + int z_normalized; +} wml_vertex_t; + +/* a texture */ +typedef struct Texture { + const unsigned char *sprite; + char width, height, offset; +} wml_texture_t; + +/* a triangle */ +typedef struct Triangle { + /* coordinates */ + short x0, y0, z0; + short x1, y1, z1; + short x2, y2, z2; + + /* textures */ + struct Texture *texture_front; + struct Texture *texture_back; +} wml_triangle_t; + +/* a rectangle */ +typedef struct Rectangle { + /* coordinates */ + short x0, y0, z0; + short x1, y1, z1; + short x2, y2, z2; + + /* textures */ + struct Texture *texture_front; + struct Texture *texture_back; +} wml_rectangle_t; + +/* an object */ +typedef struct Object { + /* coordinates */ + int x, y, z; + char axe; float angle; + + /* rectangles */ + wml_rectangle_t *list_rect; + int list_rect_size; + + /* triangles */ + wml_triangle_t *list_tri; + int list_tri_size; +} wml_object_t; + +/* a map */ +typedef struct Map { + int id; + + /* objects */ + struct Object **list_object; + int list_object_size; +} wml_map_t; + +/* a scene */ +typedef struct Scene { + /* flow control */ + int stop_execution; + + /* camera */ + float camera_x, camera_y, camera_z; + float camera_yaw, camera_pitch; + int scale_coef, near, far; + float near_coef; + int far_coef; + float div_coef; + int viewport_x1, viewport_y1, viewport_x2, viewport_y2; + int shift_x, shift_y; + unsigned short *z_buffer; + int z_buffer_size, z_buffer_offset, z_buffer_width; + + /* texture, map, background */ + struct Texture *hidden, *black, *white; + struct Map map; + const unsigned char *background; + + /* i don't know what this is, yet */ + int a1, a2, a3, a4, a5, a6, a7, a8, a9; +} wml_scene_t; + +/* ************************************************************************** */ +/* Functions */ +/* ************************************************************************** */ +/* update things */ +void wml_update_camera(wml_scene_t *scene); +void wml_update_viewport(wml_scene_t *scene); + +/* render */ +void wml_render_draw(wml_scene_t *scene); + +void wml_dynamic(wml_vertex_t *vertex, + int axe, int x, int y, int z, + float cosinus, float sinus); +void wml_transform(wml_scene_t *scene, wml_vertex_t *vertex); + +void wml_render_triangle(wml_scene_t *scene, + wml_vertex_t *vertex1, wml_vertex_t *vertex2, wml_vertex_t *vertex3, + wml_texture_t *texture); +void wml_render_triangle_black(wml_scene_t *scene, + wml_vertex_t *vertex1, wml_vertex_t *vertex2, wml_vertex_t *vertex3, + wml_texture_t *texture); +void wml_render_triangle_white(wml_scene_t *scene, + wml_vertex_t *vertex1, wml_vertex_t *vertex2, wml_vertex_t *vertex3, + wml_texture_t *texture); + +void wml_edge(wml_vertex_t *a, wml_vertex_t *b, wml_vertex_t *c); +void wml_edge_start(wml_vertex_t *a, wml_vertex_t *b, int px, int py); +void wml_edge_step_x(wml_vertex_t *a, wml_vertex_t *b); +void wml_edge_step_y(wml_vertex_t *a, wml_vertex_t *b); + +void wml_move_camera_face(wml_scene_t *scene, float value); +void wml_move_camera_side(wml_scene_t *scene, float value); + +#endif /* LIBWINDMILL_H */ diff --git a/include/libwindmill/internals.h b/include/libwindmill/internals.h new file mode 100644 index 0000000..0d53d44 --- /dev/null +++ b/include/libwindmill/internals.h @@ -0,0 +1,27 @@ +/* ***************************************************************************** + * libwindmill/internals.h -- libwindmill internal utilities. + * Copyright (C) 2017 Olivier "Ninestars" Lanneau + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libwindmill. + * libwindmill is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libwindmill is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libwindmill; if not, see . + * ************************************************************************** */ +#ifndef LIBWINDMILL_INTERNALS_H +# define LIBWINDMILL_INTERNALS_H +# include + +void wml_set_vertex_xyz(wml_vertex_t *vertex, int x, int y, int z); +void wml_set_vertex_txy(wml_vertex_t *vertex, char tx, char ty); + +#endif /* LIBWINDMILL_INTERNALS_H */ diff --git a/src/draw.c b/src/draw.c new file mode 100644 index 0000000..f09d2d7 --- /dev/null +++ b/src/draw.c @@ -0,0 +1,191 @@ +/* ***************************************************************************** + * draw.c -- draw the scene. + * Copyright (C) 2017 Olivier "Ninestars" Lanneau + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libwindmill. + * libwindmill is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libwindmill is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libwindmill; if not, see . + * ************************************************************************** */ +#include + +/* ************************************************************************** */ +/* Rectangle */ +/* ************************************************************************** */ +static void render_rectangle_part(wml_scene_t *scene, + wml_texture_t *texture, wml_vertex_t *vertex0, + wml_vertex_t *vertex1, wml_vertex_t *vertex2, + wml_vertex_t *vertex3) +{ + if (texture == hidden) { + /* do nothing */ + } else if (texture == scene->black) { + wml_render_triangle_black(scene, vertex0, vertex1, vertex2); + wml_render_triangle_black(scene, vertex1, vertex3, vertex2); + } else if (texture == scene->white) { + wml_render_triangle_white(scene, vertex0, vertex1, vertex2); + wml_render_triangle_white(scene, vertex1, vertex3, vertex2); + } else { + wml_set_vertex_txy(vertex0, 0, texture->height); + wml_set_vertex_txy(vertex1, texture->width, texture->height); + wml_set_vertex_txy(vertex2, 0, 0); + wml_set_vertex_txy(vertex3, texture->width, 0); + + wml_render_triangle(scene, vertex0, vertex1, vertex2, texture); + wml_render_triangle(scene, vertex1, vertex3, vertex2, texture); + } +} + +static void render_rectangle(wml_scene_t *scene, wml_polygon_t *polygon, + float cosinus, float sinus) +{ + wml_vertex_t vertex0, vertex1, vertex2, vertex3; + + int x0 = polygon->x0, y0 = polygon->y0, z0 = polygon->z0; + int x1 = polygon->x1, y1 = polygon->y1, z1 = polygon->z1; + int x2 = polygon->x2, y2 = polygon->y2, z2 = polygon->z2; + int x3 = x1 + x2 - x0, y3 = y1 + y2 - y0, z3 = z1 + z2 - z0; + + wml_set_vertex_xyz(&vertex0, x0, y0, z0); + wml_set_vertex_xyz(&vertex1, x1, y1, z1); + wml_set_vertex_xyz(&vertex2, x2, y2, z2); + wml_set_vertex_xyz(&vertex3, x3, y3, z3); + + /* object dynamique */ + wml_dynamic(&vertex0, polygon->axe, + current_object->x, polygon->y, cosinus, sinus); + wml_dynamic(&vertex1, polygon->axe, + current_object->x, polygon->y, cosinus, sinus); + wml_dynamic(&vertex2, polygon->axe, + current_object->x, polygon->y, cosinus, sinus); + wml_dynamic(&vertex3, polygon->axe, + current_object->x, polygon->y, cosinus, sinus); + + /* calcul des coordonnées après rotation de la caméra et projection + * des coordonnées 3D sur l'écran */ + wml_transform(&vertex0); + wml_transform(&vertex1); + wml_transform(&vertex2); + wml_transform(&vertex3); + + /* si devant la caméra */ + if (vertex0.z < 0 && vertex1.z < 0 && vertex2.z < 0 + && vertex3.z < 0) { + int area = wml_edge(&vertex0, &vertex1, &vertex2); + if (area > 0) render_rectangle_part(scene, + current_rect->texture_front, + &vertex0, &vertex1, &vertex2, &vertex3); + if (area < 0) render_rectangle_part(scene, + current_rect->texture_back, + &vertex0, &vertex2, &vertex1, &vertex3); + } +} + +/* ************************************************************************** */ +/* Triangle */ +/* ************************************************************************** */ +static void render_triangle_part(wml_scene_t *scene, + wml_texture_t *texture, wml_vertex_t *vertex0, + wml_vertex_t *vertex1, wml_vertex_t *vertex2) +{ + if (texture == hidden) { + /* do nothing */ + } else if (texture == scene->black) { + wml_render_triangle_black(scene, vertex0, vertex1, vertex2); + } else if (texture == scene->white) { + wml_render_triangle_white(scene, vertex0, vertex1, vertex2); + } else { + wml_set_vertex_txy(vertex0, 0, texture->height); + wml_set_vertex_txy(vertex1, texture->width, texture->height); + wml_set_vertex_txy(vertex2, texture->width, 0); + + wml_render_triangle(scene, vertex0, vertex1, vertex2, texture); + } +} + +static void render_triangle(wml_scene_t *scene, + wml_object_t *object, wml_triangle_t *triangle, + float cosinus, float sinus) +{ + wml_vertex_t vertex0, vertex1, vertex2; + + int x0 = triangle->x0, y0 = triangle->y0, z0 = triangle->z0; + int x1 = triangle->x1, y1 = triangle->y1, z1 = triangle->z1; + int x2 = triangle->x2, y2 = triangle->y2, z2 = triangle->z2; + + wml_set_vertex_xyz(&vertex0, x0, y0, z0); + wml_set_vertex_xyz(&vertex1, x1, y1, z1); + wml_set_vertex_xyz(&vertex2, x2, y2, z2); + + /* object dynamique */ + wml_dynamic(&vertex0, triangle->axe, + object->x, object->y, cosinus, sinus); + wml_dynamic(&vertex1, polygon->axe, + object->x, object->y, cosinus, sinus); + wml_dynamic(&vertex2, polygon->axe, + object->x, object->y, cosinus, sinus); + + /* calcul des coordonnées après rotation de la caméra et projection + * des coordonnées 3D sur l'écran */ + wml_transform(&vertex0); + wml_transform(&vertex1); + wml_transform(&vertex2); + + /* si devant la caméra */ + if (vertex0.z < 0 && vertex1.z < 0 && vertex2.z < 0) { + int area = wml_edge(&vertex0, &vertex1, &vertex2); + if (area > 4) render_triangle_part(scene, + current_rect->texture_front, + &vertex0, &vertex1, &vertex2, &vertex3); + if (area < 4) render_triangle_part(scene, + current_rect->texture_back, + &vertex0, &vertex2, &vertex1, &vertex3); + } +} + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/** + * wml_render_draw: + * Render. + * + * @arg scene the scene to render. + */ + +void wml_render_draw(wml_scene_t *scene) +{ + wml_update_camera(scene); + memset(scene->z_buffer, 0xFF, scene->z_buffer_size * 2); + + for (int i = 0; i < scene->map.list_object_size; i++) { + wml_object_t *current_object = scene->map.list_object[i]; + + /* object dynamique */ + float cosinus, sinus; + if (current_object->axe == N) { + cosinus = 0; + sinus = 1; + } else { + cosinus = cosf(current_object->angle); + sinus = sinf(current_object->angle); + } + + for (int j = 0; j < current_object->list_rect_size; j++) + render_rectangle(scene, ¤t_object->list_rect[j], + cosinus, sinus); + for (int j = 0; j < current_object->list_tri_size; j++) + render_triangle(scene, ¤t_object->list_tri[j], + cosinus, sinus); + } +} diff --git a/src/dynamic.c b/src/dynamic.c new file mode 100644 index 0000000..39d5b50 --- /dev/null +++ b/src/dynamic.c @@ -0,0 +1,69 @@ +/* ***************************************************************************** + * dynamic.c -- dynamic thingies. + * Copyright (C) 2017 Olivier "Ninestars" Lanneau + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libwindmill. + * libwindmill is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libwindmill is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libwindmill; if not, see . + * ************************************************************************** */ +#include + +void wml_dynamic(wml_vertex_t *vertex, int axe, int x, int y, int z, + float cosinus, float sinus) +{ + int vertex_x = vertex->x, + vertex_y = vertex->y, + vertex_z = vertex->z; + if (axe == N) { + vertex->x = vertex_x + x; + vertex->y = vertex_y + y; + vertex->z = vertex_z + z; + } else if (axe == X) { + vertex->x = vertex_x + x; + vertex->y = vertex_y * cosinus - vertex_z * sinus + y; + vertex->z = vertex_z * sinus + vertex_z * cosinus + z; + } else if (axe == Y) { + vertex->x = vertex_x * cosinus - vertex_z * sinus + x; + vertex->y = vertex_y + y; + vertex->z = vertex_x * sinus + vertex_z * cosinus + z; + } else if (axe == Z) { + vertex->x = vertex_x * cosinus - vertex_y * sinus + x; + vertex->y = vertex_x * sinus + vertex_y * cosinus + y; + vertex->z = vertex_z + z; + } +} + +void wml_transform(wml_scene_t *scene, wml_vertex_t *vertex) +{ + int vertex_x = vertex->x - (int)scene->camera_x, + vertex_y = vertex->y - (int)scene->camera_y, + vertex_z = vertex->z - (int)scene->camera_z; + + /* produit matriciel */ + int x = scene->a1 * vertex_x + scene->a2 * vertex_y; + int y = scene->a4 * vertex_x + scene->a5 * vertex_y + scene->a6 * vertex_z; + int z = scene->a7 * vertex_x + scene->a8 * vertex_y + scene->a9 * vertex_z; + + /* perspective */ + if (z < 0) { /* z < near * 128 * 128 */ + vertex->x = -(x * scene->scale_coef + 8192) / z + shift_x; + vertex->y = -(y * scene->scale_coef + 8192) / z + shift_y; + vertex->z = 8192 / z; + + /* calcul de z normalise entre les deux plans de clipping */ + vertex->z_normalized = -(0xFFFF * + (scene->near_coef * z + scene->far_coef) + 32767) / z; + } else + vertex->z = 1; +} diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..b50f62d --- /dev/null +++ b/src/render.c @@ -0,0 +1,121 @@ +/* ***************************************************************************** + * render.c -- draw the triangles. + * Copyright (C) 2017 Olivier "Ninestars" Lanneau + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libwindmill. + * libwindmill is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libwindmill is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libwindmill; if not, see . + * ************************************************************************** */ +#include + +void wml_render_triangle(wml_scene_t *scene, + wml_vertex_t *vertex1, wml_vertex_t *vertex2, wml_vertex_t *vertex3, + wml_texture_t *texture) +{ + /* calcul du rectangle circonscrit au triangle */ + int min_x = max(scene->viewport_x1 - 64, + min(vertex0->x, min(vertex1->x, vertex2->x))); + int max_x = min(scene->viewport_x2 - 64, + max(vertex0->x, max(vertex1->x, vertex2->x))); + int min_y = max(scene->viewport_y1 - 32, + min(vertex0->y, min(vertex1->y, vertex2->y))); + int max_y = min(scene->viewport_y2 - 32, + max(vertex0->y, max(vertex1->y, vertex2->y))); + + /* pré-calcul des coordonnées de la texture */ + int tx0 = vertex0->tx * vertex0->z; + int ty0 = vertex0->ty * vertex0->z; + int tx1 = vertex1->tx * vertex1->z; + int ty1 = vertex1->ty * vertex1->z; + int tx2 = vertex2->tx * vertex2->z; + int tx2 = vertex2->ty * vertex2->z; + + /* calcul des produits vectoriels */ + int px = min_x, py = min_y; + int w0_start = wml_edge_start(vertex1, vertex2, px, py); + int w0_step_x = wml_edge_step_x(vertex1, vertex2); + int w0_step_y = wml_edge_step_y(vertex1, vertex2); + int w1_start = wml_edge_start(vertex2, vertex0, px, py); + int w1_step_x = wml_edge_step_x(vertex2, vertex0); + int w1_step_y = wml_edge_step_y(vertex2, vertex0); + int w2_start = wml_edge_start(vertex0, vertex1, px, py); + int w2_step_x = wml_edge_step_x(vertex0, vertex1); + int w2_step_y = wml_edge_step_y(vertex0, vertex1); + + /* calcul de l'aire du triangle */ + int area = wml_edge(vertex0, vertex1, vertex2); + + /* ? */ + int z_num_start = (w0_start * vertex0->z_normalized + + w1_start * vertex1->z_normalized + + w2_start * vertex2->z_normalized) / area; + int z_num_step_x = (w0_step_x * vertex0->z_normalized + + w1_step_x * vertex1->z_normalized + + w2_step_x * vertex2->z_normalized) / area; + int z_num_step_y = (w0_step_y * vertex0->z_normalized + + w1_step_y * vertex1->z_normalized + + w2_step_y * vertex2->z_normalized) / area; + + /* ? */ + int z_div_start = w0_start * vertex0->z + + w1_start * vertex1->z + + w2_start * vertex2->z; + int z_div_step_x = w0_step_x * vertex0->z + + w1_step_x * vertex1->z + + w2_step_x * vertex2->z; + int z_div_step_y = w0_step_y * vertex0->z + + w1_step_y * vertex1->z + + w2_step_y * vertex2->z; + + /* pré-calcul largeur en octet des tableaux */ + int nbw_tex = (texture->width - 1) / 8 + 1; + for (int x = min_x; x <= max_x; x++) { + int w0 = w0_start, w1 = w1_start, w2 = w2_start; + int z_num = z_num_start, z_div = z_div_start; + int offset_vram = (x >> 3) + 520; + char mask_vram = 128 >> (x & 7); + + /* parcours en colonne */ + for (int y = min_y; y <= max_y; y++) { + if (w0 >= 0 && w1 >= 0 && w2 >= 0 && z_num > 0) { + int address = x + y * z_buffer_width + z_buffer_offset; + if (z_num <= z_buffer[address]) { + z_buffer[address] = z_num; + /* calcul des coordonnées pour la texture */ + int f_tx = (w0 * tx0 + w1 * tx1 + w2 * tx2) / z_div; + int f_ty = (w0 * ty0 + w1 * ty1 + w2 * ty2) / z_div; + + /* calcul du masque pour l'octet */ + char mask_tx = 128 >> (f_tx & 7); + int black = texture->sprite[(f_tx >> 3) + (f_ty * nbw_tex)] + & mask_tx; + + /* affichage du pixel */ + if (black) vram[(y << 4) + offset_vram] |= mask_vram; + else vram[(y << 4) + offset_vram] &= ~mask_vram; + } + } + w0 += w0_step_y; + w1 += w1_step_y; + w2 += w2_step_y; + z_num += z_num_step_y; + z_div += z_div_step_y; + } + w0_start += w0_step_x; + w1_start += w1_step_x; + w2_start += w2_step_x; + z_num_start += z_num_step_x; + z_div_start += z_div_step_x; + } +} diff --git a/src/update.c b/src/update.c new file mode 100644 index 0000000..a1a2383 --- /dev/null +++ b/src/update.c @@ -0,0 +1,72 @@ +/* ***************************************************************************** + * update.c -- update the scene. + * Copyright (C) 2017 Olivier "Ninestars" Lanneau + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libwindmill. + * libwindmill is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libwindmill is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libwindmill; if not, see . + * ************************************************************************** */ +#include + +/** + * wml_update_camera: + * Update the camera. + * + * @arg scene the scene to update. + */ + +void wml_update_camera(wml_scene_t *scene) +{ + scene->camera_yaw = (int)(scene->camera_yaw + 360) % 360; + float camera_yaw_rad = M_PI * scene->camera_yaw / 180.0; + + /* calculate intermediate value */ + float camera_pitch_rad = M_PI * scene->camera_pitch / 180.0; + float cos_yaw = cosf(camera_yaw_rad), sin_yaw = sinf(camera_yaw_rad); + float cos_pitch = cosf(camera_pitch_rad); + float sin_pitch = sinf(camera_pitch_rad); + + /* set final values */ + scene->a1 = 128.0 * cos_yaw; + scene->a2 = 128.0 * -sin_yaw; + scene->a3 = 0.0; + scene->a4 = 128.0 * cos_pitch * sin_yaw; + scene->a5 = 128.0 * cos_pitch * cos_yaw; + scene->a6 = 128.0 * -sin_pitch; + scene->a7 = 128.0 * sin_pitch * sin_yaw; + scene->a8 = 128.0 * sin_pitch * cos_yaw; + scene->a9 = 128.0 * cos_pitch; +} + +/** + * wml_update_viewport: + * Update the viewport. + * + * @arg scene the scene to update. + */ + +void wml_update_viewport(wml_scene_t *scene) +{ + free(scene->z_buffer); + scene->shift_x = (scene->viewport_x1 + scene->viewport_x2 - 128) / 2; + scene->shift_y = (scene->viewport_y1 + scene->viewport_y2 - 64) / 2; + + scene->z_buffer_size = (scene->viewport_x2 - scene->viewport_x1) + * (scene->viewport_y2 -scene->viewport_y2); + scene->z_buffer = (unsigned short*)calloc(scene->z_buffer_size, 2); + scene->z_buffer_width = scene->viewport_x2 - scene->viewport_x1; + scene->z_buffer_offset = 64 - scene->viewport_x1 + + scene->z_buffer_width * (32 - scene->viewport_y1); + memset(scene->z_buffer, 0xFF, scene->z_buffer_size * 2); +}