commit 10a973e2f177848ab3a337f985b06e2ca5f4ef64 Author: Thomas "Cakeisalie5" Touhey Date: Tue Mar 7 01:17:53 2017 +0100 Initial commit - that's out of the way. 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); +}