refactoring Part 1 done

This commit is contained in:
fatmeat 2025-05-31 00:27:25 +02:00
parent 39b998d62b
commit c4de88e53e
76 changed files with 43532 additions and 12 deletions

12
.gitignore vendored
View File

@ -1,6 +1,10 @@
build/ build/*
obj/
*.o *.o
*.out .vscode/*
*.exe *.exe
*.a
*.out
.cache/*
*.cignore
assets/not_free
zhidden/*

360
LICENSE.md Normal file
View File

@ -0,0 +1,360 @@
# GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
<https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
## Preamble
The licenses for most software are designed to take away your freedom
to share and change it. By contrast, the GNU General Public License is
intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on,
we want its recipients to know that what they have is not the
original, so that any problems introduced by others will not reflect
on the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at
all.
The precise terms and conditions for copying, distribution and
modification follow.
## TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
**0.** This License applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License. The
"Program", below, refers to any such program or work, and a "work
based on the Program" means either the Program or any derivative work
under copyright law: that is to say, a work containing the Program or
a portion of it, either verbatim or with modifications and/or
translated into another language. (Hereinafter, translation is
included without limitation in the term "modification".) Each licensee
is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the Program
(independent of having been made by running the Program). Whether that
is true depends on what the Program does.
**1.** You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a
fee.
**2.** You may modify your copy or copies of the Program or any
portion of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
**a)** You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
**b)** You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any part
thereof, to be licensed as a whole at no charge to all third parties
under the terms of this License.
**c)** If the modified program normally reads commands interactively
when run, you must cause it, when started running for such interactive
use in the most ordinary way, to print or display an announcement
including an appropriate copyright notice and a notice that there is
no warranty (or else, saying that you provide a warranty) and that
users may redistribute the program under these conditions, and telling
the user how to view a copy of this License. (Exception: if the
Program itself is interactive but does not normally print such an
announcement, your work based on the Program is not required to print
an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
**3.** You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
**a)** Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
**b)** Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your cost of
physically performing source distribution, a complete machine-readable
copy of the corresponding source code, to be distributed under the
terms of Sections 1 and 2 above on a medium customarily used for
software interchange; or,
**c)** Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is allowed
only for noncommercial distribution and only if you received the
program in object code or executable form with such an offer, in
accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
**4.** You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt otherwise
to copy, modify, sublicense or distribute the Program is void, and
will automatically terminate your rights under this License. However,
parties who have received copies, or rights, from you under this
License will not have their licenses terminated so long as such
parties remain in full compliance.
**5.** You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
**6.** Each time you redistribute the Program (or any work based on
the Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
**7.** If, as a consequence of a court judgment or allegation of
patent infringement or for any other reason (not limited to patent
issues), conditions are imposed on you (whether by court order,
agreement or otherwise) that contradict the conditions of this
License, they do not excuse you from the conditions of this License.
If you cannot distribute so as to satisfy simultaneously your
obligations under this License and any other pertinent obligations,
then as a consequence you may not distribute the Program at all. For
example, if a patent license would not permit royalty-free
redistribution of the Program by all those who receive copies directly
or indirectly through you, then the only way you could satisfy both it
and this License would be to refrain entirely from distribution of the
Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
**8.** If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
**9.** The Free Software Foundation may publish revised and/or new
versions of the 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 Program
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Program does not specify a
version number of this License, you may choose any version ever
published by the Free Software Foundation.
**10.** If you wish to incorporate parts of the Program into other
free programs whose distribution conditions are different, write to
the author to ask for permission. For software which is copyrighted by
the Free Software Foundation, write to the Free Software Foundation;
we sometimes make exceptions for this. Our decision will be guided by
the two goals of preserving the free status of all derivatives of our
free software and of promoting the sharing and reuse of software
generally.
**NO WARRANTY**
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
## How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and an idea of what it does.
Copyright (C) yyyy name of author
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper
mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
type `show w'. This is free software, and you are welcome
to redistribute it under certain conditions; type `show c'
for details.
The hypothetical commands \`show w' and \`show c' should show the
appropriate parts of the General Public License. Of course, the
commands you use may be called something other than \`show w' and
\`show c'; they could even be mouse-clicks or menu items--whatever
suits your program.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the program,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright
interest in the program `Gnomovision'
(which makes passes at compilers) written
by James Hacker.
signature of Moe Ghoul, 1 April 1989
Moe Ghoul, President of Vice
This General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library,
you may consider it more useful to permit linking proprietary
applications with the library. If this is what you want to do, use the
[GNU Lesser General Public
License](https://www.gnu.org/licenses/lgpl.html) instead of this
License.

99
Makefile_Old Normal file
View File

@ -0,0 +1,99 @@
# Compiler and flags
CC = gcc
CFLAGS = -g -Wall
CFLAGS += -std=c11
LDFLAGS =
# Directories
BUILDDIR = build/
OBJ_DIR = obj/
SRC_DIR = source/
# Library
LIB = HavenLib.a
# Sources and objects for the library
SRC += $(wildcard $(SRC_DIR)engine/**.c)
OBJ = $(SRC:$(SRC_DIR)%.c=$(OBJ_DIR)%.o)
# Executables
EXEC = Game
EDITOR = HavenEditor
EXEC_SRC = $(wildcard $(SRC_DIR)game/*.c)
EDITOR_SRC = $(wildcard $(SRC_DIR)tools/*.c)
EXEC_OBJ = $(EXEC_SRC:$(SRC_DIR)%.c=$(OBJ_DIR)%.o)
EDITOR_OBJ = $(EDITOR_SRC:$(SRC_DIR)%.c=$(OBJ_DIR)%.o)
# Includes and libraries
INCLUDE = -Iinclude
# Platform-specific configurations
ifeq ($(OS), Windows_NT)
INCLUDE += -IC:/mingw64/include
LIBS = -lraylib -lopengl32 -lgdi32 -lwinmm
else ifeq ($(shell uname -s), Linux)
CFLAGS += -fsanitize=address
LDFLAGS += -fsanitize=address
LIBS = -lglfw -lraylib -lGl -lm -lpthread -ldl -lrt -lX11
endif
# Default target
all: lib $(EXEC) $(EDITOR)
# Pattern rule for object files
$(OBJ_DIR)%.o: $(SRC_DIR)%.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
# Static library
$(BUILDDIR)$(LIB): $(OBJ)
@mkdir -p $(BUILDDIR)
ar rcs $@ $^
# Game executable
$(EXEC): $(EXEC_OBJ) $(BUILDDIR)$(LIB)
@mkdir -p $(BUILDDIR)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $(BUILDDIR)$@
# Editor executable
$(EDITOR): $(EDITOR_OBJ) $(BUILDDIR)$(LIB)
@mkdir -p $(BUILDDIR)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $(BUILDDIR)$@
# Dynamic library target
dynamic: CFLAGS += -fPIC
dynamic: LDFLAGS += -shared
ifeq ($(OS), Windows_NT)
dynamic: $(BUILDDIR)$(LIB:.a=.dll)
else
dynamic: $(BUILDDIR)$(LIB:.a=.so)
endif
# Windows DLL
$(BUILDDIR)$(LIB:.a=.dll): $(OBJ)
@mkdir -p $(BUILDDIR)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
# Unix shared object
$(BUILDDIR)$(LIB:.a=.so): $(OBJ)
@mkdir -p $(BUILDDIR)
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
# Cleanup
clean:
rm -rf $(OBJ_DIR)
fclean: clean
rm -rf $(BUILDDIR)
re: fclean all
debug:
gdb ./build/Game.exe
.PHONY: all lib $(EXEC) $(EDITOR) clean fclean re dynamic

175
RAMBLING.md Normal file
View File

@ -0,0 +1,175 @@
# GAME ENGINE
## World of Haven : Chaos dungeons
Je suis en train de développer un moteur de jeu écrit en c99, ayant pour but de créer un jeu rogue-like dans un univers Voxel.
## networking
## input
## thread
in c99 how does a struct is handled when accessing it's member from multiple thread, do i need a mutex for all of the struct or only for it's member ?
like i have this struct :
```c
static struct {
queue_t async_tasks;
pthread_mutex_t async_mutex;
queue_t sync_tasks;
pthread_mutex_t sync_mutex;
queue_t completed_tasks;
bool running;
pthread_mutex_t running;
int next_task_id;
pthread_mutex_t status_mtx;
pthread_t workers[32];
} thread_mgr;
```
but should i use a mutex for the struct instead ? is it defined behavior or not ?
answer:
each member is a piece of memory. a 'struct' is not a 'real' thing.. it's just a conveneint way for us as programmers to group these related things.
mutex is required for modifying any piece of memory .. so accessing each item would require the use of a mutex. but it's not very practical to have 10 mutexes. so you have 1 and that serves the purpose of accessing any of them
then there are exceptions for this on platforms like x86.. where WORD sized items can to read/wrote atomically.
think about changing or reading things as a single transaction.. if the transaction requires more than 1 member.. you need a mutex.
yes okay thanks i had a misconception about how struct work (i thinked of them as memory address with different data type contiguous in memory), but okay thanks, so i can access different member at the same time, thanks for your great answer
yes.. they do appear in memory next to each other.. but there is nothing in code or memory that tells the cpu something is a struct
NOUVEAU
[4:47 pm]
it stops being a struct once it's in binary/asm.
it's up to you to decide which things you want to associate to a mutex.
it's just very typical programmer/human that decides the things will be 'this struct' or 'this namespace' or these 3 int's. or a complete subsystem.
on x86? you'll find anything on the same 64byte span of memory is going to have the most need for mutex.
this discussion is thanks to AJ the goat.
## Graphic
## Sound
## AI
## game loop
## physic
## animation
## voxel items
## terrain gen ::
/*
first generate terrain
then change block type
then generate caves
then add minerals
then generate hydrometrie
then add erosion and water material displacement
then add structures
*/
### Idea
discret non euclidian voxel element;
make the player discover a little bit of the world by himself
Procedural Dungeon with mob spawn
Wave mod in certain handcrafted dungeon
sql >> nosql;
mongodb; atlas;
elastic search
collection pour reduire base de donnee;
Optimisation des index;g
Commerce de dofus, et le temps passer sur un craft item /fm augmente ca valeur focus et valeur des auugmentes en fonction des build
i could add a system with different faction and aggro level
## CREDIT
raylib
raygui
librg
fastNoiseLight
enet.h
## TODO
setup and update of shader
engine setup
create voxel mesh
render voxel
render one voxel
render one chunk
render all chunk
## Naration Idea
```
Day 0 is when you start the game.
Tutorial phase
Day 12, the force of the shadow advance and start to build a bridge
First game loop (gather ressources and combat)
Day 20 The bridge is finished
mid game, dungeon and exploring the mainland
Day 50 the force advance through the middle island
Start introducing the full picture of the story
player need to reenforce is base
Day 75 The second bridge start building
same gameloop as before
Day 100 the scond bridge is finished and the shadow advance through the mainland
Start to defend Your City (free build) (biger and biger wave).
if player start to explore, he enter the third gameloop
where
when the player advance to the evil domain (south island), go to the late game where you start your journey to conquer the forgotten land
you can go back but explored and conquered land will be lost
the point of the late game is to use what you aquired trhough the early and mid game to go as far as you can, and when you finish you unlock the good ending,
bad ending when you lose your base,
neutral / Unending when cannot go to terme on the 999 days (hidden success).
Pendant plus de 10000 ans, les manikins on prospère sous la montagne. Leurs royaumes s'étend à perte de vue sous terre, les gemmes et les minéraux brillent telle des étoiles donnant le sentiment de ne pas etre enterre, sur le plafond, soutenue par d'imposantes statues.
Les murs sont recouverts d'habitations avec des mécanismes de toutes sortes.
```

202
README.md Normal file
View File

@ -0,0 +1,202 @@
# HAVEN GAME ENGINE
___
currently in very early stage.
written in c99
intended for voxel games.
___
## Betdrorlim : Dungeon of Chaos
You start in a city, you end up getting swarmed by monster attack, you need to create a settlement deep into the wilderness and find what is the reason of the swarm
___
## Design
every function should be like this:
[module]_[submodule]_[use]();
ex:
alloc_pool_init();
thread_mgr_init();
everything under is a ia slope template for now:
```
██████████████████████████████████████████
█ Hybrid Client-Server Flow █
██████████████████████████████████████████
┌───────────────────┐
│ Game Client │
│ ┌───────────────┐ │
│ │ Input │ │ ┌───────┐
│ │ Processing ├─┼──►│ Local │
│ └───────┬───────┘ │ │ Server│
│ │ │ └───┬───┘
│ ┌───────▼───────┐ │ │
│ │ Prediction │ │ │
│ │ & Rendering │ │ │
│ └───────┬───────┘ │ │
└─────┬───┼───┬─────┘ │
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────┐
│ Shared Simulation Core │
│ ┌──────────────────┐ ┌───────────────┐ │
│ │ Command Queue │ │ World State │ │
│ │ - Input buffer │ │ - Chunk data │ │
│ │ - Priority sorted│ │ - Entities │ │
│ └────────┬─────────┘ └───────┬───────┘ │
│ │ │ │
│ ┌────────▼────────────────────▼───────┐ │
│ │ Simulation Loop │ │
│ │ 1. Process commands │ │
│ │ 2. Update physics │ │
│ │ 3. Generate snapshots │ │
│ └─────────────────────────────────────┘ │
└───────────────────┬─────────────────────┘
┌─────────▼──────────┐
│ Snapshot Delivery │
│ - Local: Memcpy │
│ - Remote: Network │
└─────────┬──────────┘
┌─────────▼──────────┐
│ Client State │
│ Reconciliation │
└────────────────────┘
```
### Communication Flow
```
Client Side Server Side Shared
─────────────────────────────────────────────────────────────
[Input Polling] [Command Queue]
│ │
├──User Input────────►│
│ │
[Prediction] [Simulation Tick]
│ │
│◄───World Snapshot───┤
│ │
[Render Interp.] [State Broadcast]
│ │
├──Correction Req.───►│
│ │
[State Apply] [Delta Update]
```
### Sequence Diagram
```
Local Mode Remote Mode
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Client │ │ Server │ │ Client │ │ Server │
└───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘
│ Input │ │ Input │
│────────────►│ │───via Net──►│
│ │ Simulate │ │
│ Prediction │───┐ │ Prediction │
│◄────────────│◄──┘ │◄───via Net──│
│ Render │ │ Render │
│ │ │ │
```
### Data Model Relationships
```
WorldSnapshot ◄───┐ ┌───► InputCommand
▲ │ │ ▲
│ ├─has a───┤ │
│ │ │ │
│ ┌────┴─────┐ │ ┌────┴─────┐
└─────┤ World │ └─────┤ Player │
│ State │ │ State │
└──────────┘ └──────────┘
```
### Memory Layout Visualization
```
┌───────────────────────────┐
│ Client Memory │
├─────────────┬─────────────┤
│ Predicted │ Command │
│ World State │ Buffer │
│ (Write) │ (Ring) │
└──────┬──────┴──────┬──────┘
│ │
▼ ▼
┌───────────────────────────┐
│ Shared Memory │
├─────────────┬─────────────┤
│ Server │ Snapshot │
│ World State │ Buffer │
│ (Read-Only) │ (Double) │
└─────────────┴─────────────┘
```
### Performance Metrics
```
┌──────────────────────┬──────────────────────┐
│ Client │ Server │
├──────────────────────┼──────────────────────┤
│ FPS: 60 │ TPS: 20 │
│ Pred. Error: 0.2m │ Cmd Lag: 12ms │
│ Chunks Rendered: 142 │ Chunks Loaded: 256 │
│ VRAM: 1.4GB │ RAM: 2.1GB │
└──────────────────────┴──────────────────────┘
```
### Debug Visualization
```
┌─────────────────────────────────────────┐
│ Debug Overlay (F1) │
├─────────────────────────────────────────┤
│ Local Server: ACTIVE [] │
│ Snapshot Delay: 42ms [] │
│ Unacknowledged Commands: 3 [] │
│ Predicted vs Actual: 0.4m [] │
└─────────────────────────────────────────┘
```
### Testing Strategy
```
┌───────────────────┬───────────────────────┐
│ Test Case │ Visual Indicator │
├───────────────────┼───────────────────────┤
│ Normal Operation │ Green network icon │
│ High Latency │ Yellow ping counter │
│ Prediction Error │ Red position marker │
│ Server Desync │ Purple warning flash │
└───────────────────┴───────────────────────┘
```
___
## CREDIT
```
┌────────────────┐
│ raylib │
│ librg │
│ fastNoiseLight │
│ enet │
└────────────────┘
```
___

163
ROADMAP.md Normal file
View File

@ -0,0 +1,163 @@
# 🚀 Voxel Engine Roadmap (ADHD-Friendly)
## 🔥 Immediate Wins
- [ ] Add chunk bounding box visualization
- [ ] Implement 5-line frustum culling
- [ ] Create debug chunk counter
## 🎯 Today's Goals
1. **Memory**
- [ ] Chunk pool allocator
- [ ] LRU cache tracker
2. **Rendering**
- [ ] Instanced batches
- [ ] Material atlas
3. **Chunks**
- [ ] Neighbor awareness
- [ ] Dirty flag system
## 🛠️ Tomorrow's Tasks
1. **Procedural Gen**
- [ ] Add FastNoiseLite
- [ ] Basic heightmap
2. **Interaction**
- [ ] Voxel raycasting
- [ ] Place/break system
3. **Performance**
- [ ] Frame timers
- [ ] 2-level LOD
## 📈 Day 3: Polish
1. **Optimization**
- [ ] Compute shader meshes
- [ ] Buffer recycling
2. **Saving**
- [ ] Chunk diffing
- [ ] .chunkdiff format
3. **UI**
- [ ] Debug panel
- [ ] Heatmap vis
## ⚡ Quick Boosters
- [ ] Tweak camera
- [ ] Rainbow texture
- [ ] Wireframe toggle
- [ ] Unload particles
## 🧠 ADHD Tips
1. Use 25min timers
2. Commit often
3. Physical notepad
4. Compile frequently
```
██████████████████████████████████████████
█ Core Architecture █
██████████████████████████████████████████
▲ ▲
│ │
┌───────┴───────┐ ┌─┴──────────┐
▼ ▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ World │ │ Chunk │ │ Rendering │
│ - Chunk Grid│◄─►│ Manager │◄─►│ Pipeline │
│ - Entities │ │ - Loading │ │ - Instancing│
└──────┬──────┘ │ - Unloading │ │ - Culling │
│ └──────▲──────┘ └──────▲──────┘
│ │ │
▼ │ │
┌─────────────┐ │ │
│ Event │ │ │
│ System │──────────┘ │
│ - Tile │ │
│ Triggers │────────────────────────────┘
└─────────────┘
text
Copy
███ Chunk Lifecycle ███████████████████████
Unloaded → Loading → Active ↔ Modified
▲ │ ▲
└──────────┴─────◄────┘
(Unloading)
Components:
- Loading: Mesh gen, neighbor detection
- Active: In memory, rendered
- Modified: Queued for saving
- Unloading: Serialize & release
████ Rendering Pipeline ███████████████████
Chunk Data → Frustum Culling → Batch Prep
│ │
▼ ▼
LOD Selection Material Sorting
│ │
▼ ▼
GPU Upload ◄─────────────── Instancing
Draw Calls → Post Processing → Screen
████ Data Flow ████████████████████████████
Player → Event System → Chunk Manager
World State ←──────────────┤
Rendering Pipeline → GPU
Frame Presentation
███ Event System Flow █████████████████████
Input → Event Queue → Spatial Partitioning
Chunk Processing Queue
Game Logic & World Mods
Render Updates ◄─┐
│ │
└─────────┘
████ Voxel Memory Layout (16³) ███████████
┌───────────────────────────────┐
│ Chunk Header │
│ - Position │
│ - Neighbor ptrs [6] │
│ - LOD level │
├───────────────────────────────┤
│ Voxel Data (4096) │
│ - 4 bytes per voxel │
│ (RGBA + matID) │
├───────────────────────────────┤
│ Metadata │
│ - Light levels │
│ - Modified timestamp │
└───────────────────────────────┘
████ Debug HUD ████████████████████████████
┌─────────────────────────────────────────┐
│ Chunks: 142/1024 │ FPS: 72 │
│ VRAM: 1.2GB/2.0GB │ Draws: 86 │
│ CPU: 14ms │ GPU: 9ms │
│ LODs: 3.2k L0 │ 1.1k L1 │
└─────────────────────────────────────────┘
```

BIN
assets/Monster_hunter.ttf Normal file

Binary file not shown.

BIN
assets/skybox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

BIN
assets/skybox1/negx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
assets/skybox1/negy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
assets/skybox1/negz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
assets/skybox1/posx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
assets/skybox1/posy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
assets/skybox1/posz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/style_lavanda.rgs Normal file

Binary file not shown.

BIN
assets/style_terminal.rgs Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

68
source/client/game.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef GAME_H
# define GAME_H
#include "../engine/allocator/allocator.h"
#include "../engine/thread/thread.h"
#include "../engine/render/render.h"
#include "../engine/audio/audio.h"
#include <raylib.h>
#include <rlgl.h>
#include <raymath.h>
#include <rcamera.h>
// #define RLIGHTS_IMPLEMENTATION
// // #include <rlights.h>
// Deferred mode passes
typedef enum {
DEFERRED_POSITION,
DEFERRED_NORMAL,
DEFERRED_ALBEDO,
DEFERRED_ZBUFFER,
DEFERRED_SHADING
} deferred_mode_enum;
typedef enum {
SHADER_GBUFFER,
SHADER_DEFERRED,
SHADER_SKYBOX,
SHADER_VOXEL,
SHADER_SOBEL,
SHADER_PBR
} shader_type_enum;
typedef enum {
TEXTURE_GUI_BUTTON,
} texture_name_enum;
// typedef enum {
// ,
// } music_name_enum;
typedef enum {
SOUND_DUMMY1,
SOUND_ITEM_FOUND,
SOUND_DUMMY2,
SOUND_MENU_CLICK1,
SOUND_MENU_CLICK2,
SOUND_MENU_CLICK3
} sound_name_enum;
#define MAX_SHADER 6
Shader *game_shader_load(void);
void game_shader_unload(Shader *shader);
void scene_render(const Model skybox, Camera3D *camera, const gbuffer_t gbuffer, const Shader shader[MAX_SHADER], int deferred_mode);
void game_loading_screen(void);
/*
game menu
*/
void game_menu_start_init(void);
int game_menu_start_update();
void game_menu_start_render(Texture2D *texture, Font font);
void game_menu_start_clear(void);
#endif

View File

@ -0,0 +1,105 @@
#include "game.h"
void game_loading_screen(void) {
int logoPositionX = GetScreenWidth() * 0.5 - 128;
int logoPositionY = GetScreenHeight() * 0.5 - 128;
int framesCounter = 0;
int lettersCount = 0;
int topSideRecWidth = 16;
int leftSideRecHeight = 16;
int bottomSideRecWidth = 16;
int rightSideRecHeight = 16;
int state = 0; // Tracking animation states (State Machine)
float alpha = 1.0f; // Useful for fading
SetTargetFPS(60);
while (state != 4) {
// Update
//----------------------------------------------------------------------------------
if (state == 0) // State 0: Small box blinking
{
framesCounter++;
if (framesCounter == 120)
{
state = 1;
framesCounter = 0; // Reset counter... will be used later...
}
}
else if (state == 1) // State 1: Top and left bars growing
{
topSideRecWidth += 4;
leftSideRecHeight += 4;
if (topSideRecWidth == 256) state = 2;
}
else if (state == 2) // State 2: Bottom and right bars growing
{
bottomSideRecWidth += 4;
rightSideRecHeight += 4;
if (bottomSideRecWidth == 256) state = 3;
}
else if (state == 3) // State 3: Letters appearing (one by one)
{
framesCounter++;
if (framesCounter/12) // Every 12 frames, one more letter!
{
lettersCount++;
framesCounter = 0;
}
if (lettersCount >= 10) // When all letters have appeared, just fade out everything
{
alpha -= 0.02f;
if (alpha <= 0.0f)
{
alpha = 0.0f;
state = 4;
}
}
}
BeginDrawing(); {
ClearBackground(RAYWHITE);
if (state == 0)
{
if ((framesCounter/15)%2) DrawRectangle(logoPositionX, logoPositionY, 16, 16, BLACK);
}
else if (state == 1)
{
DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, BLACK);
DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, BLACK);
}
else if (state == 2)
{
DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, BLACK);
DrawRectangle(logoPositionX, logoPositionY, 16, leftSideRecHeight, BLACK);
DrawRectangle(logoPositionX + 240, logoPositionY, 16, rightSideRecHeight, BLACK);
DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, BLACK);
}
else if (state == 3)
{
DrawRectangle(logoPositionX, logoPositionY, topSideRecWidth, 16, Fade(BLACK, alpha));
DrawRectangle(logoPositionX, logoPositionY + 16, 16, leftSideRecHeight - 32, Fade(BLACK, alpha));
DrawRectangle(logoPositionX + 240, logoPositionY + 16, 16, rightSideRecHeight - 32, Fade(BLACK, alpha));
DrawRectangle(logoPositionX, logoPositionY + 240, bottomSideRecWidth, 16, Fade(BLACK, alpha));
DrawRectangle(GetScreenWidth()/2 - 112, GetScreenHeight()/2 - 112, 224, 224, Fade(RAYWHITE, alpha));
DrawText(TextSubtext("raylib", 0, lettersCount), GetScreenWidth()/2 - 44, GetScreenHeight()/2 + 48, 50, Fade(BLACK, alpha));
}
} EndDrawing();
}
SetTargetFPS(165);
}

247
source/client/main.c Normal file
View File

@ -0,0 +1,247 @@
#include "game.h"
typedef enum {
ENGINE_STATE_GAME,
ENGINE_STATE_MENU_START,
ENGINE_STATE_LOADING_SCREEN,
ENGINE_STATE_MENU_SETTING,
ENGINE_STATE_MENU4,
ENGINE_STATE_MENU5,
ENGINE_STATE_MENU6,
ENGINE_STATE_MENU7
} engine_state_enum;
static struct {
bool exit_window;
bool exit_request;
int engine_state;
int deferred_mode;
unsigned int width;
unsigned int height;
Shader *shader;
gbuffer_t gbuffer;
Font font;
Camera3D camera;
Texture2D *textures;
unsigned int textures_size;
} ctx;
void scene_render(const Model skybox, Camera3D *camera, const gbuffer_t gbuffer, const Shader shader[MAX_SHADER], int deferred_mode) {
UpdateCamera(camera, CAMERA_FREE);
float camerapos[3] = {camera->position.x, camera->position.y, camera->position.z};
SetShaderValue(shader[SHADER_DEFERRED], shader[SHADER_DEFERRED].locs[SHADER_LOC_VECTOR_VIEW], camerapos, SHADER_UNIFORM_VEC3);
SetMousePosition(GetScreenWidth() * 0.5, GetScreenHeight() * 0.5);
//update all light;
BeginDrawing(); {
haven_gbuffer_start_draw(gbuffer, *camera, shader[SHADER_GBUFFER]); {
//drawDeferred scene here
} haven_gbuffer_end_draw();
switch (deferred_mode){
case (DEFERRED_SHADING) : {
haven_gbuffer_rendering(gbuffer, *camera, shader[SHADER_DEFERRED]);
BeginMode3D(*camera); {
rlDisableBackfaceCulling();
rlDisableDepthMask();
DrawModel(skybox, Vector3Zero(), 1.0f, WHITE);
rlEnableBackfaceCulling();
rlEnableDepthMask();
rlEnableShader(rlGetShaderIdDefault()); {
DrawCube((Vector3){10, 10, 10}, 10, 10, 10, RED);
} rlDisableShader();
}EndMode3D();
break;
}
case (DEFERRED_POSITION) : {
haven_gbuffer_texture_render(gbuffer.positionTexture, gbuffer.width, gbuffer.height);
break;
}
case (DEFERRED_NORMAL) : {
haven_gbuffer_texture_render(gbuffer.normalTexture, gbuffer.width, gbuffer.height);
break;
}
case (DEFERRED_ALBEDO) : {
haven_gbuffer_texture_render(gbuffer.albedoSpecTexture, gbuffer.width, gbuffer.height);
break;
}
case (DEFERRED_ZBUFFER) : {
haven_gbuffer_texture_render(gbuffer.zTexture, gbuffer.width, gbuffer.height);
break;
}
default:{
break;
}
}
DrawFPS(10, 10);
DrawText(TextFormat("%f", GetFrameTime() * 1000), 30, 30, 10, GREEN);
render_debug_draw();
} EndDrawing();
}
int main(const int ac, char *av[]) {
haven_memory_system_init();
haven_thread_mgr_init();
haven_time_init();
ctx.height = 1080;
ctx.width = 1920;
ctx.deferred_mode = DEFERRED_SHADING;
ctx.engine_state = ENGINE_STATE_LOADING_SCREEN;
ctx.camera = (Camera3D){0};
// ctx.camera.position = (Vector3){ 2.0f, 3.0f, 2.0f };// Camera position
// ctx.camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point
// ctx.camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
// ctx.camera.fovy = 45.0f; // Camera field-of-view Y
// ctx.camera.projection = CAMERA_PERSPECTIVE; // Camera projection type(Camera3D) {
ctx.camera.fovy = 45.0f;
ctx.camera.projection = CAMERA_PERSPECTIVE;
ctx.camera.position = (Vector3){40, 40, 40};
ctx.camera.up = (Vector3){0, 1, 0};
ctx.camera.target = (Vector3){0, 0, 0};
haven_memory_system_print();
InitAudioDevice();
assert(IsAudioDeviceReady());
SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_UNDECORATED | FLAG_WINDOW_RESIZABLE);//need to see if it work
InitWindow(ctx.width, ctx.height, "World of Haven : Chaos dungeons");
assert(IsWindowReady());
rlEnableSmoothLines();//need to see if it work
ctx.shader = game_shader_load();
ctx.font = LoadFont("assets/Monster_hunter.ttf");
SetTargetFPS(ac == 2 ? atoi(av[1]) : 165);
ctx.gbuffer = haven_gbuffer_init(ctx.width, ctx.height);
haven_deferred_set_loc(ctx.shader[SHADER_DEFERRED]);
Model cube = haven_skybox_create(ctx.shader[SHADER_SKYBOX]);
FilePathList sounds_files = {0};
sounds_files = LoadDirectoryFiles("assets/sound");
haven_sound_init((const char**)sounds_files.paths, sounds_files.count);
UnloadDirectoryFiles(sounds_files);
// FilePathList music_files = {0};
// music_files = LoadDirectoryFiles("assets/not_free/music");
// haven_music_init((const char**)music_files.paths, music_files.count);
// UnloadDirectoryFiles(music_files);
FilePathList texture_files = {0};
texture_files = LoadDirectoryFiles("assets/textures");
ctx.textures = malloc(sizeof(Texture2D) * texture_files.count);
for (int i = 0; i < texture_files.count; i++) {
ctx.textures[i] = LoadTexture(texture_files.paths[i]);
}
ctx.textures_size = texture_files.count;
UnloadDirectoryFiles(texture_files);
game_menu_start_init();
rlEnableDepthTest();
rlEnableBackfaceCulling();
while (!WindowShouldClose()) {
switch (ctx.engine_state) {
case (ENGINE_STATE_GAME): {
scene_render(cube, &ctx.camera, ctx.gbuffer, ctx.shader, ctx.deferred_mode);
break;
}
case (ENGINE_STATE_MENU_START): {
BeginDrawing();
ClearBackground(BLACK);
switch (game_menu_start_update()) {
case (1): {
ctx.engine_state = ENGINE_STATE_GAME;
HideCursor();
break;
}
case (2): {
ctx.engine_state = ENGINE_STATE_GAME;
HideCursor();
break;
}
case (3): {
ctx.engine_state = ENGINE_STATE_MENU_SETTING;
break;
}
case (4): {
goto close;
break;
}
default:break;
}
game_menu_start_render(ctx.textures, ctx.font);
EndDrawing();
break;
}
case (ENGINE_STATE_LOADING_SCREEN): {
game_loading_screen();
ctx.engine_state = ENGINE_STATE_MENU_START;
break;
}
case (ENGINE_STATE_MENU_SETTING): {
BeginDrawing();
ClearBackground(BLACK);
//setting
EndDrawing();
break;
}
case (ENGINE_STATE_MENU4): {
BeginDrawing();
ClearBackground(BLACK);
EndDrawing();
break;
}
case (ENGINE_STATE_MENU5): {
BeginDrawing();
ClearBackground(BLACK);
EndDrawing();
break;
}
case (ENGINE_STATE_MENU6): {
BeginDrawing();
ClearBackground(BLACK);
EndDrawing();
break;
}
case (ENGINE_STATE_MENU7): {
BeginDrawing();
ClearBackground(BLACK);
EndDrawing();
break;
}
default:{
break;
}
}
// haven_music_update();
haven_time_update();
haven_stack_reset();
}
close:
game_menu_start_clear();
for (int i = 0; i < ctx.textures_size; i++) {
UnloadTexture(ctx.textures[i]);
}
haven_music_close();
haven_sound_close();
CloseAudioDevice();
game_shader_unload(ctx.shader);
ShowCursor();
haven_thread_mgr_close();
haven_memory_system_shutdown();
CloseWindow();
return(0);
}

22
source/client/shader.c Normal file
View File

@ -0,0 +1,22 @@
#include "game.h"
Shader *game_shader_load(void) {
Shader *shader = malloc(sizeof(Shader) * MAX_SHADER);
assert(shader);
shader[SHADER_GBUFFER] = LoadShader("shader/gbuffer.vs", "shader/gbuffer.fs");
shader[SHADER_DEFERRED] = LoadShader("shader/deferred.vs", "shader/deferred.fs");
shader[SHADER_DEFERRED].locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader[SHADER_DEFERRED], "viewPosition");
shader[SHADER_SKYBOX] = LoadShader("shader/skybox.vs", "shader/skybox.fs");
shader[SHADER_VOXEL] = LoadShader("shader/vox_solid.vs", "shader/vox_solid.fs");
shader[SHADER_SOBEL] = LoadShader(0x00, "shader/sobel.fs");
shader[SHADER_PBR] = LoadShader("shader/pbr.vs", "shader/pbr.fs");
return (shader);
}
void game_shader_unload(Shader *shader) {
for (int i = 0; i < MAX_SHADER; i++) {
UnloadShader(shader[i]);
}
free (shader);
}

View File

@ -0,0 +1,36 @@
#include "game.h"
#include "../engine/render/gui.h"
#define BUTTON_NUMBER 4
static haven_gui_button *button[BUTTON_NUMBER];
void game_menu_start_init(void) {
button[0] = haven_gui_button_create("Offline", (Rectangle){20, 100, 200, 50}, TEXTURE_GUI_BUTTON);
button[1] = haven_gui_button_create("Online", (Rectangle){20, 200, 200, 50}, TEXTURE_GUI_BUTTON);
button[2] = haven_gui_button_create("Setting", (Rectangle){20, 300, 200, 50}, TEXTURE_GUI_BUTTON);
button[3] = haven_gui_button_create("Leave", (Rectangle){20, 400, 200, 50}, TEXTURE_GUI_BUTTON);
}
int game_menu_start_update() {
Vector2 mouse_pos = GetMousePosition();
for (int i = 0; i < BUTTON_NUMBER; i++) {
if (haven_gui_button_logic(button[i], mouse_pos, haven_sound_play, SOUND_MENU_CLICK1)) {
haven_sound_play(SOUND_MENU_CLICK2);
return (i + 1);
}
}
return (0);
}
void game_menu_start_render(Texture2D *texture, Font font) {
for (int i = 0; i < BUTTON_NUMBER; i++) {
haven_gui_button_draw(button[i], texture, font);
}
}
void game_menu_start_clear(void) {
for (int i = 0; i < BUTTON_NUMBER; i++) {
haven_gui_button_delete(button[i]);
}
}

138
source/engine/allocator.c Normal file
View File

@ -0,0 +1,138 @@
#include "allocator_impl.h"
static memory_stats stats;
void memory_system_init() {
stats = (memory_stats){0};
}
memory_page* memory_system_alloc(size_t nmemb, size_t size) {
memory_page* memory;
memory = malloc(sizeof(memory_page));
assert(memory);
memory->size = 1;
memory->ptr = aligigned_alloc(size, nmemb * size);
stats.current_allocated += size;
stats.total_allocated += size;
stats.total_allocation ++;
return (memory);
}
void memory_system_free(memory_page* page) {
}
void memory_system_shutdown() {
if (stats.current_allocated != 0) {
printf("WARNING: %.2lld Mb got leaked\n", stats.current_allocated / 1048576);
}
}
void memory_system_print() {
printf("INFO: allocation_count: %i , total_allocated: %.2lld Mb, total_freed: %.2lld Mb, current_allocated: %2lld Mb\n",
stats.total_allocation, stats.total_allocated / 1048576, stats.total_freed / 1048576, stats.current_allocated / 1048576);
}
#include "allocator_impl.h"
// typedef struct pool_block_s {
// struct pool_block_s *next;
// } pool_block_t;
// typedef struct {
// pool_block_t *free_list;
// void *memory;
// size_t block_size;
// size_t pool_size;
// size_t alignment;
// } pool_allocator_t;
// pool_allocator_t *haven_pool_create(size_t block_size, size_t count, size_t alignment) {
// pool_allocator_t *pool = malloc(sizeof(pool_allocator_t));
// // Adjust block size for alignment and ensure it can hold a pointer
// size_t aligned_size = ((block_size + sizeof(pool_block_t) - 1) / alignment + 1) * alignment;
// pool->block_size = aligned_size;
// pool->pool_size = aligned_size * count;
// pool->alignment = alignment;
// // Allocate aligned memory for the pool
// pool->memory = _aligned_malloc(alignment, pool->pool_size);
// pool->free_list = NULL;
// // Initialize the free list
// char *block = (char*)pool->memory;
// for (size_t i = 0; i < count; i++) {
// pool_block_t *node = (pool_block_t*)block;
// node->next = pool->free_list;
// pool->free_list = node;
// block += aligned_size;
// }
// return pool;
// }
// void *haven_pool_alloc(pool_allocator_t *pool) {
// if (!pool->free_list) return NULL;
// pool_block_t *block = pool->free_list;
// pool->free_list = block->next;
// return block;
// }
// void haven_pool_free(pool_allocator_t *pool, void *ptr) {
// if (!ptr || ptr < pool->memory || ptr >= (char*)pool->memory + pool->pool_size) return;
// pool_block_t *block = (pool_block_t*)ptr;
// block->next = pool->free_list;
// pool->free_list = block;
// }
// void haven_pool_destroy(pool_allocator_t *pool) {
// if (pool) {
// free(pool->memory);
// free(pool);
// }
// }
#include "allocator_impl.h"
#define STACK_SIZE 67108864 //64Mb
static allocator_stack stacks[2];
static uint8_t current;
void *allocator_stack_alloc(size_t size, size_t alignment) {
uintptr_t raw_addr = 0x00;
void* aligned_ptr = 0x00;
size_t padding = 0;
raw_addr = (uintptr_t)(stacks[current].base + stacks[current].offset);
assert(raw_addr);
padding = (alignment - (raw_addr % alignment)) % alignment;
if (stacks[current].offset + size + padding > stacks[current].size) {
return NULL;
}
aligned_ptr = (void*)(raw_addr + padding);
stacks[current].offset += size + padding;
return aligned_ptr;
}
void allocator_stack_swap(void) {
stacks[current].offset = 0;
current ^= 1;
}
void allocator_stack_init(void) {
}
void allocator_stack_delete(void) {
}

102
source/engine/allocator.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef HAVEN_ALLOC_H
# define HAVEN_ALLOC_H
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
// ================================================================
// Memory System Core
// ================================================================
/**
* @brief
*
*/
void memory_system_init();
/**
* @brief
*
*/
void memory_system_shutdown();
/**
* @brief
*
*/
void memory_system_print();
// ================================================================
// Stack Allocator (Temporary/Fast)
// ================================================================
/**
* @brief return a pointer to position x in stack buffer
*
* @param size
* @param alignment
* @return void*
*/
void *allocator_stack_alloc(size_t size, size_t alignment);
/**
* @brief call this function to reset memory offset
* and swap stack memory buffer (in this order)
*
*/
void allocator_stack_swap(void);
/**
* @brief
*
*/
void allocator_stack_init(void);
/**
* @brief
*
*/
void allocator_stack_delete(void);
// ================================================================
// Pool Allocator (Fixed-Size Objects) WIP
// ================================================================
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
typedef struct {
uint32_t total_allocation;
size_t total_allocated;
size_t total_freed;
size_t current_allocated;
} memory_stats;
typedef struct {
bool used;
size_t size;
void* ptr;
} memory_page;
#define BASE_STACK_SIZE 67108864 //64Mb
typedef struct {
size_t size;
size_t offset;
uint8_t* base;
} allocator_stack;
#endif

212
source/engine/async.c Normal file
View File

@ -0,0 +1,212 @@
/*
Fiber
Fiber is a way to manage concurrency at a more granular level than threads.
While threads represent a sequence of instructions that can run concurrently
with other threads,
a fiber is a unit of execution which only runs on its initiating thread.
Fibers are scheduled by the application, not the operating system.
They are great tools for implementing co-operative multitasking
where you have many tasks that you want to have run concurrently,
but you want to have explicit control on when they are switched in and out.
For server-side game development, fibres can be particularly useful in dealing
with multiple user requests, where each request might have its own fiber
*/
#include <pthread.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "../type/queue.h"
#include <time.h>
#ifdef _WIN64
#include <windows.h>
void time_usleep(uint64_t usec) {
HANDLE timer;
LARGE_INTEGER ft;
ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time
timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
}
#else
#include <unistd.h>
void time_usleep(uint64_t usec) {
usleep(usec);
}
#endif
typedef struct s_task {
void *(*func)(void *);
void *arg;
int id;
bool completed;
void *result;
} task_t;
static struct {
static_queue *async_tasks;
pthread_mutex_t async_mutex;
static_queue *sync_tasks;
pthread_mutex_t sync_mutex;
bool running;
pthread_mutex_t running_mtx;
int next_task_id;
static_queue *completed_tasks;
pthread_mutex_t status_mtx;
pthread_t workers[32];
} thread_mgr;
/*
worker function
need to be careful with waiting usleep could be too long or short
*/
static void *worker_function(void *arg) {
(void)arg;
bool run = true;
while (run) {
task_t task;
bool found_task = false;
pthread_mutex_lock(&thread_mgr.sync_mutex);
if (static_queue_pop(&thread_mgr.sync_tasks, &task) == 0) {
found_task = true;
}
pthread_mutex_unlock(&thread_mgr.sync_mutex);
if (!found_task) {
pthread_mutex_lock(&thread_mgr.async_mutex);
if (static_queue_pop(&thread_mgr.async_tasks, &task) == 0) {
found_task = true;
}
pthread_mutex_unlock(&thread_mgr.async_mutex);
}
if (found_task) {
task.result = task.func(task.arg);
pthread_mutex_lock(&thread_mgr.status_mtx);
static_queue_push(&thread_mgr.completed_tasks, &task.id);
pthread_mutex_unlock(&thread_mgr.status_mtx);
} else {
time_usleep(1000);
}
pthread_mutex_lock(&thread_mgr.running_mtx);
run = thread_mgr.running;
pthread_mutex_unlock(&thread_mgr.running_mtx);
}
return (NULL);
}
/*
add task to pool, data inside arg should be used as input and output
*/
int thread_task_add(void *(*func)(void *), void *arg, bool is_synced) {
task_t task = {
.func = func,
.arg = arg,
.completed = false,
.result = NULL
};
pthread_mutex_lock(&thread_mgr.status_mtx);
task.id = thread_mgr.next_task_id++;
pthread_mutex_unlock(&thread_mgr.status_mtx);
if (is_synced) {
pthread_mutex_lock(&thread_mgr.sync_mutex);
static_queue_push(&thread_mgr.sync_tasks, &task);
pthread_mutex_unlock(&thread_mgr.sync_mutex);
} else {
pthread_mutex_lock(&thread_mgr.async_mutex);
static_queue_push(&thread_mgr.async_tasks, &task);
pthread_mutex_unlock(&thread_mgr.async_mutex);
}
return (task.id);
}
//not to rework as i just need to check if first data == id, then pop and return true else return false
bool thread_task_status(int task_id) {
pthread_mutex_lock(&thread_mgr.status_mtx);
int *completed_ids = thread_mgr.completed_tasks->data;
for (unsigned i = 0; i < thread_mgr.completed_tasks->size; i++) {
if (completed_ids[i] == task_id) {
memmove(&completed_ids[i], &completed_ids[i+1],
(thread_mgr.completed_tasks->size - i - 1) * sizeof(int));
thread_mgr.completed_tasks->size--;
// static_queue_pop(thread_mgr.completed_tasks, task_id);
pthread_mutex_unlock(&thread_mgr.status_mtx);
return (true);
}
}
pthread_mutex_unlock(&thread_mgr.status_mtx);
return (false);
}
/*
init thread_mgr and worker
*/
void thread_mgr_init() {
thread_mgr.async_tasks = static_queue_init(sizeof(task_t));
thread_mgr.sync_tasks = static_queue_init(sizeof(task_t));
thread_mgr.completed_tasks = static_queue_init(sizeof(int));
pthread_mutex_init(&thread_mgr.async_mutex, NULL);
pthread_mutex_init(&thread_mgr.sync_mutex, NULL);
pthread_mutex_init(&thread_mgr.status_mtx, NULL);
pthread_mutex_init(&thread_mgr.running_mtx, NULL);
thread_mgr.running = true;
thread_mgr.next_task_id = 1;
for (int i = 0; i < 32; i++) {
pthread_create(&thread_mgr.workers[i], NULL, worker_function, NULL);
}
}
/*
raise stopping flag to all thread then wait for them to close
*/
void thread_mgr_close() {
pthread_mutex_lock(&thread_mgr.running_mtx);
thread_mgr.running = false;
pthread_mutex_unlock(&thread_mgr.running_mtx);
for (int i = 0; i < 32; i++) {
pthread_join(thread_mgr.workers[i], NULL);
}
static_queue_destroy(&thread_mgr.async_tasks);
static_queue_destroy(&thread_mgr.sync_tasks);
static_queue_destroy(&thread_mgr.completed_tasks);
pthread_mutex_destroy(&thread_mgr.async_mutex);
pthread_mutex_destroy(&thread_mgr.sync_mutex);
pthread_mutex_destroy(&thread_mgr.status_mtx);
pthread_mutex_destroy(&thread_mgr.running_mtx);
}

47
source/engine/async.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef HAVEN_THREAD_H
# define HAVEN_THREAD_H
#include <stdbool.h>
#include <stdint.h>
/**
* @brief The usleep() function suspends execution of the calling thread for
(at least) usec microseconds. The sleep may be lengthened
slightly by any system activity or by the time spent processing
the call or by the granularity of system timers.
*
* @param usec
*/
void time_usleep(uint64_t usec);
/**
* @brief Initialize thread manager structure and start worker threads
*
*/
void thread_mgr_init();
/**
* @brief Send stop signale to evey worker threads and clear task queue
*
*/
void thread_mgr_close();
/**
* @brief Check task status by sending task id
*
* @param task_id
* @return int
*/
bool thread_task_status(int task_id);
/**
* @brief Add a task to the task pool synced or not and return task id as int
*
* @param func
* @param arg
* @param is_synced
* @return int
*/
int thread_task_add(void *(*func)(void *), void *arg, bool is_synced);
#endif

120
source/engine/audio.c Normal file
View File

@ -0,0 +1,120 @@
#include <stdlib.h>
#include <assert.h>
#include <raylib.h>
static struct {
unsigned int size;
Music *music;
} atlas = {0};
static Music current = {0};
#define MAX_TIME_WITHOUT_MUSIC 10
unsigned int haven_music_init(const char **music_files, unsigned int n_music) {
atlas.music = malloc(sizeof(Music) * n_music);
assert(atlas.music);
for (int i = 0; i < n_music; i++) {
Music span = LoadMusicStream(music_files[i]);
if (IsMusicValid(span)) {
atlas.size++;
atlas.music[i] = span;
}
}
return (atlas.size);
}
void haven_music_play(unsigned int idx) {
if (IsMusicStreamPlaying(current)) {
StopMusicStream(current);
}
current = atlas.music[idx];
PlayMusicStream(current);
}
void haven_music_update(void) {
static double value = 0.0;
if (!IsMusicStreamPlaying(current)) {
value += GetFrameTime();
if (value >= MAX_TIME_WITHOUT_MUSIC) {
int rand = GetRandomValue(0, atlas.size - 1);
current = atlas.music[rand];
PlayMusicStream(current);
value = 0.0;
}
}
UpdateMusicStream(current);
}
void haven_music_close(void) {
for (int i = 0; i < atlas.size; i++) {
UnloadMusicStream(atlas.music[i]);
}
}
#include <stdlib.h>
#include <assert.h>
#include <raylib.h>
static struct {
unsigned int size;
Sound *sound;
} atlas;
/*
sound queue
*/
#define MAX_CONCURENT_SOUND 10
struct {
unsigned int idx;
Sound data[MAX_CONCURENT_SOUND];
} queue;
static void sound_queue_add(Sound sound) {
Sound toplay = LoadSoundAlias(sound);
if (IsSoundValid(queue.data[queue.idx])) {
UnloadSoundAlias(queue.data[queue.idx]);
}
PlaySound(toplay);
queue.data[queue.idx] = toplay;
queue.idx ++;
queue.idx %= 10;
}
/*
actual implementation
*/
bool haven_sound_play(unsigned int idx) {
if (!IsSoundValid(atlas.sound[idx])) {
return (false);
}
sound_queue_add(atlas.sound[idx]);
return (true);
}
unsigned int haven_sound_init(const char **sound_files, unsigned int n_sound) {
atlas.sound = malloc(sizeof(Sound) * n_sound);
assert(atlas.sound);
for (int i = 0; i < n_sound; i++) {
Sound tmp_sound = LoadSound(sound_files[i]);
if (IsSoundValid(tmp_sound)) {
atlas.size++;
atlas.sound[i] = tmp_sound;
}
}
queue.idx = 0;
return (atlas.size);
}
void haven_sound_close(void) {
for (int i = 0; i < atlas.size; i++) {
UnloadSound(atlas.sound[i]);
}
}

68
source/engine/audio.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef HAVEN_SOUND_H
# define HAVEN_SOUND_H
#include <stdbool.h>
/*
SOUND
*/
/**
* @brief add sound to play
*
* @param idx
* @return true
* @return false
*/
bool haven_sound_play(unsigned int idx);
/**
* @brief initialize audio device and load sound atlas
*
* @param sound
* @param n_sound
* @return unsigned int
*/
unsigned int haven_sound_init(const char **sound, unsigned int n_sound);
/**
* @brief close audio device and unload sound atlas;
*
*/
void haven_sound_close(void);
/*
MUSIC
*/
/**
* @brief
*
* @param idx
*/
void haven_music_play(unsigned int idx);
/**
* @brief
*
*/
void haven_music_update(void);
/**
* @brief
*
* @param music_files
* @param n_music
* @return unsigned int
*/
unsigned int haven_music_init(const char **music_files, unsigned int n_music);
/**
* @brief
*
*/
void haven_music_close(void);
#endif

56
source/engine/entity.c Normal file
View File

@ -0,0 +1,56 @@
#include "entity.h"
entity_pool pool;
void entity_pool_init(void) {
// entity_stats
pool.health = calloc(MAX_ENTITY, sizeof(int32_t));
pool.max_health = calloc(MAX_ENTITY, sizeof(uint32_t));
pool.regeneration = calloc(MAX_ENTITY, sizeof(uint16_t));
pool.damage = calloc(MAX_ENTITY, sizeof(uint16_t));
// entity_info
pool.position = calloc(MAX_ENTITY, sizeof(Vector3));
pool.min = calloc(MAX_ENTITY, sizeof(Vector3));
pool.max = calloc(MAX_ENTITY, sizeof(Vector3));
pool.direction = calloc(MAX_ENTITY, sizeof(Vector3));
pool.velocity = calloc(MAX_ENTITY, sizeof(Vector3));
assert(pool.health);
assert(pool.max_health);
assert(pool.regeneration);
assert(pool.damage);
assert(pool.position);
assert(pool.min);
assert(pool.max);
assert(pool.direction);
assert(pool.velocity);
pool.capacity = MAX_ENTITY;
pool.size = 0;
memset(pool.controle_bit, 0, sizeof(pool.controle_bit));
}
void entity_pool_destroy(void) {
// entity_stats
free(pool.health);
free(pool.max_health);
free(pool.regeneration);
free(pool.damage);
// entity_info
free(pool.position);
free(pool.min);
free(pool.max);
free(pool.direction);
free(pool.velocity);
}
void entity_pool_update() {
}
void entity_spawn(entity nwaps) {
}
void entity_kill() {
}

46
source/engine/entity.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef ENTITY_H
# define ENTITY_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <raylib.h>
#include <assert.h>
#define MAX_ENTITY 100000000 //100 000 000
typedef struct entity_pool {
// entity_stats
int32_t *health;
uint32_t *max_health;
uint16_t *regeneration;
uint16_t *damage;
// entity_info
Vector3 *position;
Vector3 *min;
Vector3 *max;
Vector3 *direction;
Vector3 *velocity;
// data control
uint64_t controle_bit[MAX_ENTITY / sizeof(uint64_t)];
uint32_t size;
uint32_t capacity;
} entity_pool;
typedef struct entity {
// entity_stats
int32_t health;
uint32_t max_health;
uint16_t regeneration;
uint16_t damage;
// entity_info
Vector3 position;
Vector3 min;
Vector3 max;
Vector3 direction;
Vector3 velocity;
} entity;
#endif

32
source/engine/input.c Normal file
View File

@ -0,0 +1,32 @@
#include "input.h"
input_binding load_default_binds(void) {
InputBindings bindings = {
.forward = {.is_mouse = false, .repeat = true, .bind = KEY_W},
.backward = {.is_mouse = false, .repeat = true, .bind = KEY_S},
.left = {.is_mouse = false, .repeat = true, .bind = KEY_A},
.right = {.is_mouse = false, .repeat = true, .bind = KEY_D},
.jump = {.is_mouse = false, .repeat = false, .bind = KEY_SPACE},
.crouch = {.is_mouse = false, .repeat = true, .bind = KEY_LEFT_CONTROL},
.sprint = {.is_mouse = false, .repeat = true, .bind = KEY_LEFT_SHIFT},
};
return (bindings);
}
static bool key_down(keybind_t *keybind) {
if (keybind->is_mouse) {
return IsMouseButtonDown(keybind->bind);
}
return (keybind->repeat ?
IsKeyDown(keybind->bind) : IsKeyPressed(keybind->bind));
}
void update_input_state(InputBindings *bindings, InputState *state) {
state->forward = key_down(&bindings->forward);
state->backward = key_down(&bindings->backward);
state->left = key_down(&bindings->left);
state->right = key_down(&bindings->right);
state->jump = key_down(&bindings->jump);
state->crouch = key_down(&bindings->crouch);
state->sprint = key_down(&bindings->sprint);
}

32
source/engine/input.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef ENGINE_INPUT_H
# define ENGINE_INPUT_H
typedef struct {
bool forward;
bool backward;
bool left;
bool right;
bool jump;
bool crouch;
bool sprint;
} input_state;
typedef struct {
bool is_mouse;
bool repeat;
int bind;
} keybind_t;
typedef struct {
keybind_t forward;
keybind_t backward;
keybind_t left;
keybind_t right;
keybind_t jump;
keybind_t crouch;
keybind_t sprint;
} input_binding;
void update_inpute_state(input_binding* bindings, input_state* state);
#endif

1
source/engine/item.c Normal file
View File

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

38
source/engine/item.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef HAVEN_ITEM_H
# define HAVEN_ITEM_H
#define INVENTORY_SIZE 64
#define TOOL_BAR_SIZE 8
typedef struct item_stats {
int material;
int damage;
int durability;
int effect;
int rarity;
} item_stats;
typedef struct item_data {
int type;
int texture_id;
int max_stack;
int size;
} item_data;
typedef struct item {
item_data data;
item_stats stats;
} item;
typedef struct tool_bar{
int current_item;
item item[TOOL_BAR_SIZE];
} tool_bar;
typedef struct inventory {
int money;
tool_bar tool;
item item[INVENTORY_SIZE];
} inventory;
#endif

95
source/engine/lexer.c Normal file
View File

@ -0,0 +1,95 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
/*
* LEXER
*/
typedef struct {
uint32_t position;
const char *input;
} lexer;
void lexer_init(lexer *l, const char *input) {
assert(l);
l->input = input;
l->position = 0;
}
void lexer_advance(lexer *l) {
assert(l);
l->position++;
}
char lexer_peek(lexer *l) {
assert(l);
return (l->input[l->position]);
}
char lexer_peek_next(lexer *l) {
assert(l);
return (l->input[l->position + 1]);
}
int lexer_match(lexer *l, char expected) {
assert(l);
return (l->input[l->position] - expected);
}
void lexer_skip_whitespace(lexer *l) {
assert(l);
while (isspace(l->input[l->position])) {
l->position++;
}
}
int lexer_read_int(lexer *l) {
assert(l);
int x = 0;
while (isdigit(l->input[l->position])) {
x *= 10;
x += l->input[l->position] - '0';
}
return (x);
}
char* lexer_read_string(lexer *l) {
assert(l);
const int max_size = 100;
char str[max_size];
int k = 0;
for (;isalnum(l->input[l->position + k] && k < max_size); k++) {
str[k] = l->input[l->position + k];
}
str[k] = 0x00;
return(strdup(str));
}
typedef struct {
uint32_t id;
char* data;
} token;
typedef struct {
char** data;
uint16_t size;
} dictionnary;
void token_getid(const char *data, const dictionnary *context) {
assert(context);
assert(data);
for(uint32_t i = 0; i < context->size; i++) {
if (strcmp(data, context->data[i]) == 0x00) {
break;
}
}
}

75
source/engine/memory.c Normal file
View File

@ -0,0 +1,75 @@
#include <immintrin.h>
void* memcpy_simd(void *dest, const void *src, size_t n) {
unsigned char *d = dest;
const unsigned char *s = src;
size_t i = 0;
for (; i + 16 <= n; i += 16) {
__m128i chunk = _mm_loadu_si128((const __m128i *)(s + i));
_mm_storeu_si128((__m128i *)(d + i), chunk);
}
for (; i < n; i++) {
d[i] = s[i];
}
return dest;
}
void* memset_simd(void *dest, int c, size_t n) {
unsigned char *d = dest;
size_t i = 0;
__m128i val = _mm_set1_epi8((char)c);
for (; i + 16 <= n; i += 16) {
_mm_storeu_si128((__m128i *)(d + i), val);
}
for (; i < n; i++) {
d[i] = (unsigned char)c;
}
return dest;
}
int memcmp_simd(const void *s1, const void *s2, size_t n) {
const unsigned char *a = s1;
const unsigned char *b = s2;
size_t i = 0;
for (; i + 16 <= n; i += 16) {
__m128i va = _mm_loadu_si128((const __m128i *)(a + i));
__m128i vb = _mm_loadu_si128((const __m128i *)(b + i));
__m128i cmp = _mm_cmpeq_epi8(va, vb);
int mask = _mm_movemask_epi8(cmp);
if (mask != 0xFFFF) {
// not equal somewhere, fallback to scalar check
for (int j = 0; j < 16; j++) {
if (a[i + j] != b[i + j])
return a[i + j] - b[i + j];
}
}
}
for (; i < n; i++) {
if (a[i] != b[i])
return a[i] - b[i];
}
return 0;
}
void* memmove_simd(void *dest, const void *src, size_t n) {
unsigned char *d = dest;
const unsigned char *s = src;
if (d < s) {
return memcpy_simd(dest, src, n);
} else {
size_t i = n;
while (i >= 16) {
i -= 16;
__m128i chunk = _mm_loadu_si128((const __m128i *)(s + i));
_mm_storeu_si128((__m128i *)(d + i), chunk);
}
while (i-- > 0) {
d[i] = s[i];
}
return dest;
}
}

120
source/engine/old/camera.c Normal file
View File

@ -0,0 +1,120 @@
#include "render_impl.h"
#include <rcamera.h>
void extract_frustum_from_matrix(const Matrix vp, Frustum *frustum, bool normalize) {
// Left Plane
frustum->planes[0].normal.x = vp.m8 + vp.m1;
frustum->planes[0].normal.y = vp.m9 + vp.m2;
frustum->planes[0].normal.z = vp.m10 + vp.m3;
frustum->planes[0].distance = vp.m11 + vp.m4;
// Right Plane
frustum->planes[1].normal.x = vp.m8 - vp.m1;
frustum->planes[1].normal.y = vp.m9 - vp.m2;
frustum->planes[1].normal.z = vp.m10 - vp.m3;
frustum->planes[1].distance = vp.m11 - vp.m4;
// Bottom Plane
frustum->planes[2].normal.x = vp.m8 + vp.m5;
frustum->planes[2].normal.y = vp.m9 + vp.m6;
frustum->planes[2].normal.z = vp.m10 + vp.m7;
frustum->planes[2].distance = vp.m11 + vp.m8;
// Top Plane
frustum->planes[3].normal.x = vp.m8 - vp.m5;
frustum->planes[3].normal.y = vp.m9 - vp.m6;
frustum->planes[3].normal.z = vp.m10 - vp.m7;
frustum->planes[3].distance = vp.m11 - vp.m8;
// Near Plane
frustum->planes[4].normal.x = vp.m8 + vp.m9;
frustum->planes[4].normal.y = vp.m9 + vp.m10;
frustum->planes[4].normal.z = vp.m10 + vp.m11;
frustum->planes[4].distance = vp.m11 + vp.m12;
// Far Plane
frustum->planes[5].normal.x = vp.m8 - vp.m9;
frustum->planes[5].normal.y = vp.m9 - vp.m10;
frustum->planes[5].normal.z = vp.m10 - vp.m11;
frustum->planes[5].distance = vp.m11 - vp.m12;
// Normalize all the planes if requested
if (normalize) {
for (int i = 0; i < NUMBER_FACE; i++) {
float length = sqrtf(
frustum->planes[i].normal.x * frustum->planes[i].normal.x +
frustum->planes[i].normal.y * frustum->planes[i].normal.y +
frustum->planes[i].normal.z * frustum->planes[i].normal.z
);
if (length != 0.0f) {
frustum->planes[i].normal.x /= length;
frustum->planes[i].normal.y /= length;
frustum->planes[i].normal.z /= length;
frustum->planes[i].distance /= length;
}
}
}
}
Frustum frustum_from_camera(Camera camera) {
Frustum frustum = {0};
Matrix view = GetCameraViewMatrix(&camera);
Matrix proj = GetCameraProjectionMatrix(&camera, GetScreenHeight() / GetScreenWidth());
Matrix view_proj = MatrixMultiply(view, proj);
extract_frustum_from_matrix(view_proj, &frustum, false);
return (frustum);
}
float plane_point_distance(const Plane plan, const Vector3 pos) {
return (Vector3DotProduct(plan.normal, pos) + plan.distance);
}
bool frustum_bounding_culling(BoundingBox box, Frustum frustum) {
const int plan = 6;
const int corner = 8;
Vector3 corners[8] = {
(Vector3){ box.min.x, box.min.y, box.min.z},
(Vector3){ box.max.x, box.min.y, box.min.z},
(Vector3){ box.min.x, box.max.y, box.min.z},
(Vector3){ box.max.x, box.max.y, box.min.z},
(Vector3){ box.min.x, box.min.y, box.max.z},
(Vector3){ box.max.x, box.min.y, box.max.z},
(Vector3){ box.min.x, box.max.y, box.max.z},
(Vector3){ box.max.x, box.max.y, box.max.z}
};
int out = 0;
for (int i = 0; i < plan; i++) {
for (int j = 0; j < corner; j++) {
if (plane_point_distance(frustum.planes[i], corners[j]) < 0) {
out++;
}
}
}
if (out == corner) {
return(false);
}
return (true);
}
Camera3D camera_iso_init(void) {
Camera3D camera;
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
camera.position = (Vector3){40, 40, 40};
camera.up = (Vector3){0, 1, 0};
camera.target = (Vector3){0, 0, 0};
return (camera);
}
void camera_iso_update(Camera3D *camera, const Vector3 target) {
camera->target = target;
camera->position = Vector3AddValue(target, 40);
}

36
source/engine/old/debug.c Normal file
View File

@ -0,0 +1,36 @@
#include "render_impl.h"
#define MAX_HISTORY 1000
static struct {
uint8_t chunks_buffer;//current states of chunk buffer
uint32_t chunks_draw;//number of drawn chunks
uint32_t chunks_load;//number of loaded chunks
} debug_struct;
void render_debug_draw(void) {
static float frame_history[MAX_HISTORY] = {0};
static uint8_t offset = 0;
frame_history[offset] = GetFrameTime() * 1000;
offset = (offset + 1) % MAX_HISTORY;
float max_time = 0.01f;
for (int i = 0; i < MAX_HISTORY; i++) {
if (frame_history[i] > max_time) {
max_time = frame_history[i];
}
}
for (int i = 0; i < MAX_HISTORY; i++) {
const float val = frame_history[i] / max_time;
if (val > 16.7f) {
DrawLine(10 + 2 * i, 110, 10 + 2 * i, 110 - 50 * val, RED);
} else if (val > 4.8f) {
DrawLine(10 + 2 * i, 110, 10 + 2 * i, 110 - 50 * val, WHITE);
} else {
DrawLine(10 + 2 * i, 110, 10 + 2 * i, 110 - 50 * val, GREEN);
}
}
}

View File

@ -0,0 +1,121 @@
#include "render_impl.h"
typedef struct s_gbuffer{
//framebuffer
unsigned int framebuffer;
unsigned int depthRenderbuffer;
//texture
unsigned int positionTexture;
unsigned int normalTexture;
unsigned int albedoSpecTexture;
unsigned int zTexture;
//format
unsigned int width;
unsigned int height;
} gbuffer_t;
gbuffer_t haven_gbuffer_init(int width, int height) {
gbuffer_t buffer = {0};
buffer.height = height;
buffer.width = width;
buffer.framebuffer = rlLoadFramebuffer();
if (!buffer.framebuffer) {
TraceLog(LOG_WARNING, "failed to create framebuffer");
exit(1);
}
rlEnableFramebuffer(buffer.framebuffer);
buffer.positionTexture = rlLoadTexture(NULL, width,height, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1);
buffer.normalTexture = rlLoadTexture(NULL, width,height, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1);
buffer.albedoSpecTexture = rlLoadTexture(NULL, width,height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
buffer.zTexture = rlLoadTexture(NULL, width,height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
buffer.depthRenderbuffer = rlLoadTextureDepth(width, height, true);
rlActiveDrawBuffers(4);
rlFramebufferAttach(buffer.framebuffer, buffer.positionTexture, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
rlFramebufferAttach(buffer.framebuffer, buffer.normalTexture, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0);
rlFramebufferAttach(buffer.framebuffer, buffer.albedoSpecTexture, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0);
rlFramebufferAttach(buffer.framebuffer, buffer.zTexture, RL_ATTACHMENT_COLOR_CHANNEL3, RL_ATTACHMENT_TEXTURE2D, 0);
rlFramebufferAttach(buffer.framebuffer, buffer.depthRenderbuffer, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
if (!rlFramebufferComplete(buffer.framebuffer)) {
TraceLog(LOG_WARNING, "Framebuffer is not complete");
exit(1);
}
return (buffer);
}
void haven_deferred_set_loc(Shader shader) {
rlEnableShader(shader.id);
int tex_unit_posistion = 0;
int tex_unit_normal = 1;
int tex_unit_albedospec = 2;
int tex_unit_z = 3;
SetShaderValue(shader, rlGetLocationUniform(shader.id, "gPosition"), &tex_unit_posistion, RL_SHADER_UNIFORM_SAMPLER2D);
SetShaderValue(shader, rlGetLocationUniform(shader.id, "gNormal"), &tex_unit_normal, RL_SHADER_UNIFORM_SAMPLER2D);
SetShaderValue(shader, rlGetLocationUniform(shader.id, "gAlbedoSpec"), &tex_unit_albedospec, RL_SHADER_UNIFORM_SAMPLER2D);
SetShaderValue(shader, rlGetLocationUniform(shader.id, "gZ"), &tex_unit_z, RL_SHADER_UNIFORM_SAMPLER2D);
rlDisableShader();
}
void haven_gbuffer_start_draw(const gbuffer_t gbuffer, const Camera3D camera, const Shader gbuffershader) {
rlEnableFramebuffer(gbuffer.framebuffer);
rlClearColor(0, 0, 0, 0);
rlClearScreenBuffers();
rlDisableColorBlend();
BeginMode3D(camera);
rlEnableShader(gbuffershader.id);
}
void haven_gbuffer_end_draw() {
rlDisableShader();
EndMode3D();
rlEnableColorBlend();
rlDisableFramebuffer();
rlClearScreenBuffers();
}
void haven_gbuffer_rendering(const gbuffer_t gbuffer, const Camera3D camera, const Shader deferredshader) {
BeginMode3D(camera); {
rlDisableColorBlend();
rlEnableShader(deferredshader.id); {
// Bind our g-buffer textures
// We are binding them to locations that we earlier set in sampler2D uniforms `gPosition`, `gNormal`,
// `gAlbedoSpec` and gZ
rlActiveTextureSlot(0);
rlEnableTexture(gbuffer.positionTexture);
rlActiveTextureSlot(1);
rlEnableTexture(gbuffer.normalTexture);
rlActiveTextureSlot(2);
rlEnableTexture(gbuffer.albedoSpecTexture);
rlActiveTextureSlot(3);
rlEnableTexture(gbuffer.zTexture);
// Finally, we draw a fullscreen quad to our default framebuffer
// This will now be shaded using our deferred shader
rlLoadDrawQuad();
}
rlDisableShader();
rlEnableColorBlend();
} EndMode3D();
// As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer.
rlBindFramebuffer(RL_READ_FRAMEBUFFER, gbuffer.framebuffer);
rlBindFramebuffer(RL_DRAW_FRAMEBUFFER, 0);
rlBlitFramebuffer(0, 0, gbuffer.width, gbuffer.height, 0, 0, gbuffer.width, gbuffer.height, 0x00000100);//0x00000100 -> GL_DEPTH_BUFFER_BIT
rlDisableFramebuffer();
}
void haven_gbuffer_texture_render(const unsigned int texture_id, const unsigned int width, const unsigned int height) {
DrawTextureRec((Texture2D){
.id = texture_id,
.width = width,
.height = -height,
}, (Rectangle) {0, 0, width, -height}, Vector2Zero(), RAYWHITE);
}

80
source/engine/old/gui.c Normal file
View File

@ -0,0 +1,80 @@
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include "render_impl.h"
#define BUTTON_TEX_WIDTH 64
#define BUTTON_TEX_HEIGHT 32
typedef enum {
BUTTON_DEFAULT = 0,
BUTTON_DOWN = 1,
BUTTON_RELEASE = 2,
BUTTON_HOVER = 3,
} button_state;
typedef struct {
button_state state;
bool collision;
char *str;
Rectangle bound;
int texture_id;
} haven_gui_button;
haven_gui_button *haven_gui_button_create(const char *text, Rectangle bound, unsigned int texture_id) {
haven_gui_button *button = 0x00;
button = malloc(sizeof(haven_gui_button));
assert(button);
button->str = strdup(text);
assert(button->str);
button->texture_id = texture_id;
button->bound = bound;
button->state = 0;
button->collision = false;
return (button);
}
void haven_gui_button_delete(haven_gui_button *button) {
free(button->str);
free(button);
}
bool haven_gui_button_logic(haven_gui_button *button, Vector2 mouse_pos, bool (*hover_action)(unsigned int), unsigned int arg) {
if (CheckCollisionPointRec(mouse_pos, button->bound)) {
button->collision = true;
} else {
button->collision = false;
}
if (BUTTON_DOWN != button->state) {
if (button->collision) {
if (BUTTON_HOVER != button->state) {
button->state = BUTTON_HOVER;
hover_action(arg);
}
} else {
button->state = BUTTON_DEFAULT;
}
}
if (true == button->collision && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
button->state = BUTTON_DOWN;
}
if (BUTTON_DOWN == button->state && IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) {
button->state = BUTTON_RELEASE;
}
if (BUTTON_RELEASE == button->state && true == button->collision) {
button->state = BUTTON_DEFAULT;
return (true);
}
return (false);
}
void haven_gui_button_draw(haven_gui_button *button, const Texture2D *texture, const Font font) {
DrawTexturePro(texture[button->texture_id],
(Rectangle){0, (BUTTON_TEX_HEIGHT - 1) * button->state, BUTTON_TEX_WIDTH, BUTTON_TEX_HEIGHT},
button->bound, (Vector2){0, 0}, 0, WHITE);
DrawTextEx(font, button->str,
Vector2AddValue((Vector2){button->bound.x, button->bound.y}, 5),
button->bound.height - 10, 0, WHITE);
}

58
source/engine/old/gui.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef GUI_H
#define GUI_H
#include <stdbool.h>
#include <raylib.h>
typedef enum {
BUTTON_DEFAULT = 0,
BUTTON_DOWN = 1,
BUTTON_RELEASE = 2,
BUTTON_HOVER = 3,
} button_state;
typedef struct {
button_state state;
bool collision;
char *str;
Rectangle bound;
int texture_id;
} haven_gui_button;
/**
* @brief create a new button
*
* @param text
* @param bound
* @param texture_id
* @return haven_gui_button*
*/
haven_gui_button *haven_gui_button_create(const char *text, Rectangle bound, unsigned int texture_id);
/**
* @brief delete button
*
* @param button
*/
void haven_gui_button_delete(haven_gui_button *button);
/**
* @brief button_logic
*
* @param button
* @param mouse_pos
* @return true
* @return false
*/
bool haven_gui_button_logic(haven_gui_button *button, Vector2 mouse_pos, bool (*hover_action)(unsigned int), unsigned int arg);
/**
* @brief button_drawing
*
* @param button
* @param texture
* @param font
*/
void haven_gui_button_draw(haven_gui_button *button, const Texture2D *texture, const Font font);
#endif

View File

@ -0,0 +1,80 @@
#ifndef HAVEN_RENDER_H
# define HAVEN_RENDER_H
#include <raylib.h>
typedef struct s_gbuffer{
unsigned int framebuffer;
unsigned int positionTexture;
unsigned int normalTexture;
unsigned int albedoSpecTexture;
unsigned int depthRenderbuffer;
unsigned int zTexture;
unsigned int width;
unsigned int height;
} gbuffer_t;
/**
* @brief
*
* @param texture_id
* @param width
* @param height
*/
void haven_gbuffer_texture_render(const unsigned int texture_id, const unsigned int width, const unsigned int height);
/**
* @brief
*
* @param gbuffer
* @param camera
* @param deferredshader
*/
void haven_gbuffer_rendering(const gbuffer_t gbuffer, const Camera3D camera, const Shader deferredshader);
/**
* @brief
*
*/
void haven_gbuffer_end_draw(void);
/**
* @brief
*
* @param gbuffer
* @param camera
* @param gbuffershader
*/
void haven_gbuffer_start_draw(const gbuffer_t gbuffer, const Camera3D camera, const Shader gbuffershader);
/**
* @brief
*
* @param width
* @param height
* @param shader_id
* @return gbuffer_t
*/
gbuffer_t haven_gbuffer_init(const int width, const int height);
/**
* @brief
*
* @param shader
*/
void haven_deferred_set_loc(Shader shader);
/**
* @brief
*
* @return Model
*/
Model haven_skybox_create(const Shader skybox_shader);
/**
* @brief
*
*/
void render_debug_draw(void);
#endif

View File

@ -0,0 +1,58 @@
#ifndef RENDER_IMPL_H
# define RENDER_IMPL_H
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <raylib.h>
#include <rlgl.h>
#include <raymath.h>
# define GRAY_VALUE(c) ((float)(c.r + c.g + c.b)/3.0f)
# define NUMBER_FACE 6
typedef struct Plane {
Vector3 normal;
float distance;
} Plane;
typedef struct Frustum {
Plane planes[NUMBER_FACE];
} Frustum;
typedef struct {
uint32_t count;
uint32_t instanceCount;
uint32_t firstIndex;
uint32_t baseVertex;
uint32_t baseInstance;
} deic_t;
typedef enum {
XN,
XP,
YN,
YP,
ZN,
ZP,
} direction_enum;
typedef struct quad_data {
bool render;
uint16_t element;
int face_data;
} quad_data;
typedef struct voxel_mesh {
uint32_t vao;
uint32_t vbo;
uint32_t ebo;
uint32_t ibo;
quad_data faces[NUMBER_FACE][4096];
unsigned int faces_count[NUMBER_FACE];
Texture2D face_texture;
} voxel_mesh;
#endif

View File

@ -0,0 +1,15 @@
#include <raylib.h>
Model haven_skybox_create(const Shader skybox_shader) {
Mesh cube = GenMeshCube(1, 1, 1);
Model skybox = LoadModelFromMesh(cube);
skybox.materials[0].shader = skybox_shader;
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MATERIAL_MAP_CUBEMAP }, SHADER_UNIFORM_INT);
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "doGamma"), (int[1]){ 0 }, SHADER_UNIFORM_INT);
SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "vflipped"), (int[1]){ 0 }, SHADER_UNIFORM_INT);
Image img = LoadImage("assets/skybox.png");
skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT);
UnloadImage(img);
return (skybox);
}

View File

@ -0,0 +1,166 @@
#include "voxel.h"
static const float quad_vertices[] = {
0.0f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
};
static const uint32_t quad_indices[] = {
0, 1, 2, 3,
};
uint32_t voxel_mesh_pack_face(uint8_t face, uint8_t x, uint8_t y, uint8_t z, uint8_t height, uint8_t width) {
return (((face & 0x7F) << 25) | // 7 bits for face (0-127) for marching cube
((width & 0x1F) << 20) | // 5 bits for width (0-31)
((height & 0x1F) << 15) | // 5 bits for height (0-31)
((z & 0x1F) << 10) | // 5 bits for Z (0-31)
((y & 0x1F) << 5) | // 5 bits for Y (0-31)
(x & 0x1F)); // 5 bits for X (0-31)
}
uint64_t voxel_mesh_pack_tex(uint16_t tex_id, uint16_t material, uint32_t data) {
return ((tex_id << 48) | (material << 32) | data);
}
void voxel_update_render_data(voxel_chunk_render_queue *rqueue) {
if (rqueue->draw_cmd) {
free(rqueue->draw_cmd);
}
rqueue->draw_cmd = calloc(rqueue->chunk_count, sizeof(DrawElementsIndirectCommand));
assert(rqueue->draw_cmd);
if (rqueue->global_data) {
free(rqueue->global_data);
}
rqueue->global_data = calloc(rqueue->face_count, sizeof(uint64_t));
assert(rqueue->global_data);
uint32_t offset = 0;
for (int i = 0; i < rqueue->chunk_count; i++) {
rqueue->draw_cmd[i] = (DrawElementsIndirectCommand){
.count = 4,
.instanceCount = rqueue->meshes[i].face_count,
.firstIndex = 0,
.baseVertex = 0,
.baseInstance = rqueue->face_count,
};
memcpy(rqueue->global_data[offset], rqueue->meshes[i].faces_buffer, sizeof(uint64_t) * rqueue->meshes[i].face_count);
offset += rqueue->meshes[i].face_count;
rqueue->face_count = offset;
}
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, rqueue->ibo);
glBufferData(GL_DRAW_INDIRECT_BUFFER, rqueue->chunk_count * sizeof(DrawElementsIndirectCommand), rqueue->draw_cmd, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, rqueue->global_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(uint64_t) * rqueue->face_count, rqueue->global_data, GL_DYNAMIC_DRAW);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
//need to sort chunk for transparency,
//also need to not upload face that are oposite to player pos from chunk relative pos (unless player is in chunk)
voxel_chunk_render_queue* voxel_render_queue_create() {
voxel_chunk_render_queue* rqueue = malloc(sizeof(voxel_chunk_render_queue));
assert(rqueue);
rqueue->ssbo_data = 0x00;
rqueue->meshes = 0x00;
rqueue->global_data = 0x00;
rqueue->face_count = 0;
rqueue->chunk_count = 0;
rqueue->draw_cmd = 0;
rqueue->vao = rlLoadVertexArray();
rlEnableVertexArray(rqueue->vao);
rqueue->vbo = rlLoadVertexBuffer(quad_vertices, sizeof(quad_vertices), false);
rlSetVertexAttribute(0, 3, RL_FLOAT, false, 3 *sizeof(float), 0);
rlEnableVertexAttribute(0);
rqueue->ebo = rlLoadVertexBufferElement(quad_indices, sizeof(quad_indices), false);
rqueue->ibo = rlLoadVertexBuffer(rqueue->draw_cmd, rqueue->chunk_count, true);
rqueue->global_vbo = rlLoadVertexBuffer(rqueue->global_data, rqueue->face_count, true);
rlSetVertexAttribute(1, 1, RL_FLOAT, false, sizeof(int64_t), 0);
rlEnableVertexAttribute(1);
rlSetVertexAttributeDivisor(1, 1);
rlDisableVertexArray();
// rlEnableShader();
glGenBuffers(1, &rqueue->ssbo);
assert(rqueue->ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, rqueue->ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, rqueue->chunk_count, rqueue->ssbo_data, RL_STREAM_COPY);
glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);// Clear buffer data to 0
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
return (rqueue);
}
bool voxel_render_queue_update(voxel_chunk_render_queue* rqueue) {
//load all face if update into rqueue->ibo_data
rlEnableVertexArray(rqueue->vao);
rlUpdateVertexBuffer(rqueue->global_vbo, rqueue->global_data, sizeof(uint64_t) * rqueue->face_count, 0);
rlDisableVertexArray();
glBindBuffer(GL_SHADER_STORAGE_BUFFER, rqueue->ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, rqueue->chunk_count, rqueue->ssbo_data, RL_STREAM_COPY);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
voxel_mesh* voxel_mesh_create(void) {
voxel_mesh* mesh = malloc(sizeof(voxel_mesh));
assert(mesh);
mesh->face_count = 0;
mesh->faces_buffer = 0;
return (mesh);
}
void voxel_render_chunks(const voxel_chunk_render_queue rqueue, Shader shader, Matrix transform) {
Matrix matModel = MatrixIdentity();
Matrix matView = rlGetMatrixModelview();
Matrix matProjection = rlGetMatrixProjection();
rlEnableShader(shader.id ); {
matModel = MatrixMultiply(transform, rlGetMatrixTransform());
if (shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) {
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_MODEL], matModel);
}
if (shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) {
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
}
if (shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) {
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
}
if (shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) {
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
}
rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_MVP], MatrixMultiply(MatrixMultiply(matModel, matView), matProjection));
// rlBindShaderBuffer();
// rlBindShaderBuffer();
glBindBuffer(GL_SHADER_STORAGE_BUFFER, rqueue.ssbo);
glMultiDrawElementsIndirect(GL_TRIANGLE_STRIP, GL_UNSIGNED_INT, rqueue.draw_cmd, rqueue.chunk_count, 0);
// glMultiDrawElements(,);
// for (int i = 0; i < 0; i++) {
// rlEnableVertexArray(rqueue.meshes[i].vao);
// glDrawElementsInstanced(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, 0, rqueue.meshes[i].face_count);
// }
// rlDisableVertexArray();
} rlDisableShader();
rlSetMatrixModelview(matView);
rlSetMatrixProjection(matProjection);
}

View File

@ -0,0 +1,121 @@
#include "voxel.h"
#define FNL_IMPL
#include <FastNoiseLite.h>
#include <stdio.h>
voxel_data *get_chunk_voxels(voxel_world *world, int x, int y, int z) {
return world->voxel_arena + ((x * CHUNK_STRIDE_X + y * CHUNK_STRIDE_Y + z) * CHUNK_SIZE);
}
void island_mask(Image *input) {
Image mask = GenImageGradientRadial(1024, 1024, 0.5, BLACK, WHITE);
Color *mask_colors = LoadImageColors(mask);
// UnloadImage(mask);
float gray = 0;
Color *input_color = LoadImageColors(*input);
for (int i = 0; i < 1024 * 1024; i++) {
gray = GRAY_VALUE(input_color[i]) - GRAY_VALUE(mask_colors[i]);
gray = Clamp(gray, 0, 254);
input_color[i] = (Color){.a = 255, .b = gray, .g = gray, .r = gray};
ImageDrawPixel(input, i % 1024, i / 1024, input_color[i]);
}
UnloadImageColors(mask_colors);
UnloadImageColors(input_color);
}
void rain_mask(Image *input, fnl_state *noise) {
float mask;
Color *input_color = LoadImageColors(*input);
for (int i = 0; i < 1024 * 1024; i++) {
mask = fnlGetNoise2D(noise, i / 1024, - (i % 1024)) + 1;
if (mask + GRAY_VALUE(input_color[i]) >= 2) {
input_color[i].b = 255;
}
ImageDrawPixel(input, i % 1024, i / 1024, input_color[i]);
};
UnloadImageColors(input_color);
}
Texture2D gen_texture_noise(fnl_state *noise) {
Image test = GenImageColor(1024, 1024, BLACK);
for (int x = 0; x < 1024; x++) {
for (int z = 0; z < 1024; z++) {
float noise_data = fnlGetNoise2D(noise, x, z) + 1;
char color_data = floorf(noise_data * 255 / 2);
Color tmp = {
.a = 255,
.r = color_data,
.g = color_data,
.b = color_data,
};
ImageDrawPixel(&test, x, z, tmp);
}
}
// ImageColorInvert(&test);
island_mask(&test);
rain_mask(&test, noise);
Texture2D text = LoadTextureFromImage(test);
UnloadImage(test);
return (text);
}
void chunk_gen_height(int x_off, int z_off, fnl_state *noise) {
float value[64][64] = {0};
float max = 0;
for (int x = 0; x < 64; x++) {
for (int z = 0; z < 64; z++) {
value[x][z] = (fnlGetNoise2D(noise, x_off + x, z_off + z) + 1) * 32 + 64;
if (value[x][z] > max) max = value[x][z];
}
}
}
voxel_world *gen_world(uint32_t seed) {
printf("The Mad God has Begun Creation\n");
voxel_world *world = malloc(sizeof(voxel_world));
assert(world);
// Allocate a single arena for all voxel data
world->voxel_arena = malloc(sizeof(voxel_data) * CHUNK_SIZE * 32 * 8 * 32);
assert(world->voxel_arena);
fnl_state noise = fnlCreateState();
noise.seed = seed;
noise.noise_type = FNL_NOISE_OPENSIMPLEX2;
noise.frequency = 0.02f;
noise.octaves = 5;
// Initialize all chunks
for (int x = 0; x < 32; x++) {
for (int y = 0; y < 8; y++) {
for (int z = 0; z < 32; z++) {
world->chunk[x][y][z] = malloc(sizeof(voxel_chunk));
assert(world->chunk[x][y][z]);
// Initialize chunk position
world->chunk[x][y][z]->positon = (Vector3){
x * CHUNK_RESOLUTION,
y * CHUNK_RESOLUTION,
z * CHUNK_RESOLUTION
};
// Generate terrain for this chunk
chunk_gen_height(x * CHUNK_RESOLUTION, z * CHUNK_RESOLUTION, &noise);
// Get this chunk's voxel data
voxel_data *chunk_voxels = get_chunk_voxels(world, x, y, z);
}
}
}
printf("World Generation Complete\n");
return (world);
}

View File

@ -0,0 +1,79 @@
#ifndef VOXEL_H
# define VOXEL_H
#include <raylib.h>
#include <stdint.h>
#include "../type/octree.h"
#include <rlgl.h>
#include <raymath.h>
#include <glad/glad.h>
#define CHUNK_RESOLUTION 32
#define CHUNK_SIZE (CHUNK_RESOLUTION * CHUNK_RESOLUTION * CHUNK_RESOLUTION)
#define FACES 6
#define WORLD_SIZE_X 32
#define WORLD_SIZE_Y 8
#define WORLD_SIZE_Z 32
#define CHUNK_STRIDE_Y (WORLD_SIZE_Z)
#define CHUNK_STRIDE_X (WORLD_SIZE_Y * WORLD_SIZE_Z)
typedef struct chunk_texture {
Texture2D texture[CHUNK_SIZE];
} chunk_texture;
typedef struct voxel_data {
uint16_t block_id;
uint8_t material;
} voxel_data;
typedef struct voxel_chunk {
octree voxel_tree;
Vector3 positon;
uint32_t bit[CHUNK_RESOLUTION * CHUNK_RESOLUTION];//used to send data to gpu
} voxel_chunk;
typedef struct voxel_world {
voxel_chunk *chunk[WORLD_SIZE_X][WORLD_SIZE_Y][WORLD_SIZE_Z];
voxel_data *voxel_arena;
} voxel_world;
typedef struct voxel_mesh {
uint64_t *faces_buffer;
uint32_t face_count;
// uint8_t face_mask;//to filter face to push in render queue
} voxel_mesh;
typedef struct {
uint32_t count;
uint32_t instanceCount;
uint32_t firstIndex;
uint32_t baseVertex;
uint32_t baseInstance;
} DrawElementsIndirectCommand;
typedef struct voxel_chunk_render_queue {
uint32_t vao;
uint32_t vbo;
uint32_t ebo;
//only buffer under will be updated above should be static
uint32_t ibo;//draw Cmd indirect buffer
uint32_t global_vbo;//collection of all face to be rendered
uint32_t ssbo;
uint32_t face_count;
uint32_t chunk_count;
DrawElementsIndirectCommand* draw_cmd;//one draw cmd per chunk mesh
void* ssbo_data;// should be set at the same time of mesh sorting and updated every frame(should need to change if camera not updated)
uint64_t* global_data;//should countain all face to be rendered
voxel_mesh* meshes;//sort from farthest to nearest using player camera forward(could be easier said than done)
} voxel_chunk_render_queue;
#endif

332
source/engine/type.c Normal file
View File

@ -0,0 +1,332 @@
#include "type.h"
static_queue* static_queue_init(unsigned int data_size) {
static_queue *q;
q = (static_queue *)malloc(sizeof(static_queue));
assert(q);
q->front = 0;
q->rear = 0;
q->size = 0;
q->data_size = data_size;
q->capacity = MAX_QUEUE_SIZE;
q->data = (char *)malloc(q->capacity * q->data_size);
assert(q->data);
if (!q->data) {
q->capacity = 0;
}
}
int static_queue_push(static_queue *q, const void *value) {
assert(q);
if (q->size == q->capacity) {
return -1; // Queue full
}
char *target = q->data + (q->rear * q->data_size);
memcpy(target, value, q->data_size);
q->rear = (q->rear + 1) % q->capacity;
q->size++;
return 0;
}
int static_queue_pop(static_queue *q, void *value) {
assert(q);
if (q->size == 0) {
return -1; // Queue empty
}
const char *source = q->data + (q->front * q->data_size);
memcpy(value, source, q->data_size);
q->front = (q->front + 1) % q->capacity;
q->size--;
return 0;
}
void static_queue_destroy(static_queue *q) {
assert(q);
free(q->data);
q->data = NULL;
q->capacity = 0;
q->size = 0;
}
/*
DYNAMIC QUEUE
*/
void dynamic_queue_init(dynamic_queue *q, unsigned int data_size) {
assert(q);
q->front = 0;
q->rear = 0;
q->size = 0;
q->data_size = data_size;
q->capacity = MAX_QUEUE_SIZE;
q->data = (char *)malloc(q->capacity * q->data_size);
assert(q->data);
if (!q->data) {
// Handle allocation failure (e.g., set capacity to 0)
q->capacity = 0;
}
}
int dynamic_queue_push(dynamic_queue *q, const void *value) {
assert(q);
if (q->size == q->capacity) {
return -1; // Queue full
}
char *target = q->data + (q->rear * q->data_size);
memcpy(target, value, q->data_size);
q->rear = (q->rear + 1) % q->capacity;
q->size++;
return 0;
}
int dynamic_queue_pop(dynamic_queue *q, void *value) {
assert(q);
if (q->size == 0) {
return -1; // Queue empty
}
const char *source = q->data + (q->front * q->data_size);
memcpy(value, source, q->data_size);
q->front = (q->front + 1) % q->capacity;
q->size--;
return 0;
}
void dynamic_queue_destroy(dynamic_queue *q) {
assert(q);
free(q->data);
q->data = NULL;
q->capacity = 0;
q->size = 0;
}
/* OCTREE */
octree_node* octree_node_create(float center[3], float extent) {
octree_node* node = (octree_node*)malloc(sizeof(octree_node));
assert(node);
memcpy(node->center, center, sizeof(float) * 3);
node->extent = extent;
node->child_mask = 0;
node->data = 0x00;
memset(node->children, 0, sizeof(octree_node *) * 8);
return (node);
}
int octree_node_child_index(octree_node* node, float point[3]) {
assert(node);
int index = 0;
if (point[0] >= node->center[0]) {
index |= 1;
}
if (point[1] >= node->center[1]) {
index |= 2;
}
if (point[2] >= node->center[2]) {
index |= 4;
}
return (index);
}
bool octree_node_isleaf(octree_node* node, int index) {
assert(node);
return ((node->child_mask & (1 << index)) != 0);
}
octree_node* octree_node_get_or_create(octree_node* parent, int index) {
assert(parent);
if (parent->children[index]) {
return (parent->children[index]);
}
float half = parent->extent * 0.5f;
float child_center[3] = {
parent->center[0] + ((index & 1) ? half : -half),
parent->center[1] + ((index & 2) ? half : -half),
parent->center[2] + ((index & 4) ? half : -half),
};
octree_node* child = octree_node_create(child_center, half);
assert(child);
parent->children[index] = child;
parent->child_mask |= (1 << index);
return (child);
}
octree_node* octree_node_lookup(octree_node* root, float point[3]) {
assert(root);
octree_node* current = root;
while(current) {
int idx = octree_node_child_index(current, point);
if (!octree_node_isleaf(current, idx)) {
break;
}
current = current->children[idx];
}
return (current);
}
void* octree_node_insert(octree_node* root, float point[3], void* data, float min_extent) {
assert(root);
octree_node* current = root;
while(current->extent > min_extent) {
int idx = octree_node_child_index(current, point);
if (!octree_node_isleaf(current, idx) && !octree_node_get_or_create(current, idx)) {
return (NULL);
}
current = current->children[idx];
}
void* old_data = current->data;
current->data = data;
return (old_data);
}
void octree_node_free(octree_node *node) {
if (!node) {
return;
}
for (int i = 0; i < 8; i++) {
if (node->children[i]) {
node_free(node->children[i]);
}
}
free(node);
}
octree_node* octree_traversal() {
octree_node* node = 0x00;
return (node);
}
bool octree_insert() {
return (false);
}
void* octree_extract() {
return (0x00);
}
octree* octree_init(const uint8_t max_depth) {
octree* tree = 0x00;
return (tree);
}
/* dynamic array */
void dynamic_array_add(dynamic_array *array, const void *element) {
assert(array);
if ((array->size + 1) * array->data_size >= array->capacity) {
const uint32_t new_capacity = (unsigned int)(array->capacity * ARRAY_ALLOC_STEP);
void *tmp = _aligned_malloc(new_capacity, array->alignement);
assert(tmp);
memcpy(tmp, array->data, array->size * array->data_size);
_aligned_free(array->data);
array->data = tmp;
array->capacity = new_capacity;
}
memcpy((unsigned char *)array->data + array->size * array->data_size, element, array->data_size);
array->size++;
}
void dynamic_array_del(dynamic_array *array, const unsigned int idx) {
assert(array);
if (idx >= array->size) return;
memmove((unsigned char *)array->data + idx * array->data_size,
(unsigned char *)array->data + (idx + 1) * array->data_size,
(array->size - idx - 1) * array->data_size);
array->size--;
if (array->size * array->data_size < array->capacity * 0.5 && array->capacity > 8 * array->data_size) {
const uint32_t new_capacity = (unsigned int)(array->capacity * 0.5);
void *tmp = _aligned_malloc(new_capacity, array->alignement);
assert(tmp);
memcpy(tmp, array->data, array->size * array->data_size);
_aligned_free(array->data);
array->data = tmp;
array->capacity = new_capacity;
}
}
void dynamic_array_clear(dynamic_array *array) {
assert(array);
memset(array->data, 0, array->capacity);
array->size = 0;
}
dynamic_array* dynamic_array_init(const char data_size, const size_t alignement) {
dynamic_array* array = (dynamic_array *)malloc(sizeof(dynamic_array));
assert(array);
array->data = _aligned_malloc(data_size * 8, array->alignement);
assert(array->data);
array->capacity = data_size * 8;
array->data_size = data_size;
array->size = 0;
haven_darray_clear(array);
return array;
}
void dynamic_array_destroy(dynamic_array* array) {
assert(array);
_aligned_free(array->data);
free(array);
}
void dynamic_array_range_remove(dynamic_array* array, const uint32_t start, const uint32_t end) {
assert(array);
if (start >= array->size || end >= array->size || start > end) {
return;
}
const uint32_t num_elements_to_remove = end - start + 1;
memmove(
(unsigned char *)array->data + start * array->data_size,
(unsigned char *)array->data + (end + 1) * array->data_size,
(array->size - end - 1) * array->data_size
);
array->size -= num_elements_to_remove;
if (array->size * array->data_size < array->capacity * 0.5 && array->capacity > 8 * array->data_size) {
const uint32_t new_capacity = (unsigned int)(array->capacity * 0.5);
void *tmp = _aligned_malloc(new_capacity, array->alignement);
assert(tmp);
memcpy(tmp, array->data, array->size * array->data_size);
_aligned_free(array->data);
array->data = tmp;
array->capacity = new_capacity;
}
}
void dynamic_array_sort(const dynamic_array *array, int (*comparator)(const void *, const void *)) {
assert(array);
qsort(array->data, array->size, array->data_size, comparator);
}
//return a pointer to data idx;
void* dynamic_array_get(const dynamic_array *array, const uint32_t idx) {
assert(array);
void *ret = (unsigned char *)array->data + (idx * array->data_size);
return (ret);
}
void dynamic_array_foreach(dynamic_array *array, void (*callback)(void *)) {
assert(array);
for (uint32_t i = 0; i < array->size; i++) {
callback(haven_darray_get(array, i));
}
}

66
source/engine/type.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef ENGINE_TYPE_H
# define ENGINE_TYPE_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
# include <stdint.h>
# include <stdlib.h>
# include <string.h>
# include <assert.h>
#ifndef ARRAY_ALLOC_STEP
# define ARRAY_ALLOC_STEP 1.25f
#endif
#define MAX_QUEUE_SIZE 100
/**
* @brief
*
*/
typedef struct static_queue {
void *data; // Dynamic byte buffer
unsigned int front;
unsigned int rear;
unsigned int size;
unsigned int data_size; // Use larger type (not limited to 255 bytes)
unsigned int capacity;
} static_queue;
typedef struct dynamic_queue {
void *data; // Dynamic byte buffer
unsigned int front;
unsigned int rear;
unsigned int size;
unsigned int data_size; // Use larger type (not limited to 255 bytes)
unsigned int capacity;
} dynamic_queue;
typedef struct octree_node {
uint8_t child_mask;
float center[3];
float extent;
void* data;
octree_node* children[8];
} octree_node;
typedef struct octree {
uint8_t depth;
uint32_t size;
octree_node root;
} octree;
typedef struct dynamic_array {
char data_size;// Size of each element (in bytes)
void* data;
size_t size;// Number of elements in the array
size_t capacity;// Total capacity of the array (in bytes)
size_t alignement;//Alignment requirement (must be a power of two)
} dynamic_array;
#endif

View File

@ -1,15 +1,8 @@
#ifndef ENGINE_UI_H #ifndef ENGINE_UI_H
# define ENGINE_UI_H # define ENGINE_UI_H
/* input */ #include <raylib.h>
typedef struct {
bool is_mouse;
bool repeat;
int bind;
} keybind_t;
/* gui */
typedef struct { typedef struct {
Texture2D tex; Texture2D tex;
Rectangle rec; Rectangle rec;

0
source/engine/world.h Normal file
View File

2433
source/extern/FastNoiseLite.h vendored Normal file

File diff suppressed because it is too large Load Diff

311
source/extern/KHR/khrplatform.h vendored Normal file
View File

@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

6170
source/extern/enet.h vendored Normal file

File diff suppressed because it is too large Load Diff

2532
source/extern/glad.c vendored Normal file

File diff suppressed because it is too large Load Diff

5169
source/extern/glad/glad.h vendored Normal file

File diff suppressed because it is too large Load Diff

22120
source/extern/librg.h vendored Normal file

File diff suppressed because it is too large Load Diff

172
source/extern/rlights.h vendored Normal file
View File

@ -0,0 +1,172 @@
/**********************************************************************************************
*
* raylib.lights - Some useful functions to deal with lights data
*
* CONFIGURATION:
*
* #define RLIGHTS_IMPLEMENTATION
* Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation.
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2017-2024 Victor Fisac (@victorfisac) and Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
#ifndef RLIGHTS_H
#define RLIGHTS_H
#include <raylib.h>
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#define MAX_LIGHTS 4 // Max dynamic lights supported by shader
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Light data
typedef struct {
int type;
bool enabled;
Vector3 position;
Vector3 target;
Color color;
float attenuation;
// Shader locations
int enabledLoc;
int typeLoc;
int positionLoc;
int targetLoc;
int colorLoc;
int attenuationLoc;
} Light;
// Light type
typedef enum {
LIGHT_DIRECTIONAL = 0,
LIGHT_POINT
} LightType;
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader); // Create a light and get shader locations
void UpdateLightValues(Shader shader, Light light); // Send light properties to shader
#ifdef __cplusplus
}
#endif
#endif // RLIGHTS_H
/***********************************************************************************
*
* RLIGHTS IMPLEMENTATION
*
************************************************************************************/
#if defined(RLIGHTS_IMPLEMENTATION)
#include "raylib.h"
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
static int lightsCount = 0; // Current amount of created lights
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Create a light and get shader locations
Light CreateLight(int type, Vector3 position, Vector3 target, Color color, Shader shader)
{
Light light = { 0 };
if (lightsCount < MAX_LIGHTS)
{
light.enabled = true;
light.type = type;
light.position = position;
light.target = target;
light.color = color;
// NOTE: Lighting shader naming must be the provided ones
light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightsCount));
light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightsCount));
light.positionLoc = GetShaderLocation(shader, TextFormat("lights[%i].position", lightsCount));
light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightsCount));
light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightsCount));
UpdateLightValues(shader, light);
lightsCount++;
}
return light;
}
// Send light properties to shader
// NOTE: Light shader locations should be available
void UpdateLightValues(Shader shader, Light light)
{
// Send to shader light enabled state and type
SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT);
SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT);
// Send to shader light position values
float position[3] = { light.position.x, light.position.y, light.position.z };
SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3);
// Send to shader light target position values
float target[3] = { light.target.x, light.target.y, light.target.z };
SetShaderValue(shader, light.targetLoc, target, SHADER_UNIFORM_VEC3);
// Send to shader light color values
float color[4] = { (float)light.color.r/(float)255, (float)light.color.g/(float)255,
(float)light.color.b/(float)255, (float)light.color.a/(float)255 };
SetShaderValue(shader, light.colorLoc, color, SHADER_UNIFORM_VEC4);
}
#endif // RLIGHTS_IMPLEMENTATION

54
source/shader/deferred.fs Normal file
View File

@ -0,0 +1,54 @@
#version 330 core
out vec4 finalColor;
in vec2 texCoord;
in vec2 texCoord2;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
uniform sampler2D gZ;
struct Light {
int enabled;
int type; // Unused in this demo.
vec3 position;
vec3 target; // Unused in this demo.
vec4 color;
};
const int NR_LIGHTS = 4;
uniform Light lights[NR_LIGHTS];
uniform vec3 viewPosition;
const float QUADRATIC = 0.032;
const float LINEAR = 0.09;
void main() {
vec3 fragPosition = texture(gPosition, texCoord).rgb;
vec3 normal = texture(gNormal, texCoord).rgb;
vec3 albedo = texture(gAlbedoSpec, texCoord).rgb;
float specular = texture(gAlbedoSpec, texCoord).a;
//vec3 depth = texture(gZ, texCoord).rgb;
vec3 ambient = albedo * vec3(0.1f);
vec3 viewDirection = normalize(viewPosition - fragPosition);
for(int i = 0; i < NR_LIGHTS; ++i) {
if(lights[i].enabled == 0) continue;
vec3 lightDirection = lights[i].position - fragPosition;
vec3 diffuse = max(dot(normal, lightDirection), 0.0) * albedo * lights[i].color.xyz;
vec3 halfwayDirection = normalize(lightDirection + viewDirection);
float spec = pow(max(dot(normal, halfwayDirection), 0.0), 32.0);
vec3 specular = specular * spec * lights[i].color.xyz;
// Attenuation
float distance = length(lights[i].position - fragPosition);
float attenuation = 1.0 / (1.0 + LINEAR * distance + QUADRATIC * distance * distance);
diffuse *= attenuation;
specular *= attenuation;
ambient += diffuse + specular;
}
finalColor = vec4(ambient, 1.0);
}

11
source/shader/deferred.vs Normal file
View File

@ -0,0 +1,11 @@
#version 330 core
layout (location = 0) in vec3 vertexPosition;
layout (location = 1) in vec2 vertexTexCoord;
out vec2 texCoord;
void main() {
gl_Position = vec4(vertexPosition, 1.0);
texCoord = vertexTexCoord;
}

32
source/shader/gbuffer.fs Normal file
View File

@ -0,0 +1,32 @@
#version 330 core
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
layout (location = 3) out vec4 gZ;
in vec3 fragPosition;
in vec2 fragTexCoord;
in vec3 fragNormal;
uniform sampler2D diffuseTexture;
uniform sampler2D specularTexture;
float near = 0.1;
float far = 100.0;
float LinearizeDepth(float depth) {
float z = depth * 2.0 - 1.0;
return (2.0 * near * far) / (far + near - z * (far - near));
}
void main() {
float depth = LinearizeDepth(gl_FragCoord.z) / far;
gZ = vec4(vec3(depth), 1.0);
gPosition = fragPosition;
gNormal = normalize(fragNormal);
gAlbedoSpec.rgb = texture(diffuseTexture, fragTexCoord).rgb;
gAlbedoSpec.a = texture(specularTexture, fragTexCoord).r;
}

24
source/shader/gbuffer.vs Normal file
View File

@ -0,0 +1,24 @@
#version 330 core
layout (location = 0) in vec3 vertexPosition;
layout (location = 1) in vec2 vertexTexCoord;
layout (location = 2) in vec3 vertexNormal;
out vec3 fragPosition;
out vec2 fragTexCoord;
out vec3 fragNormal;
uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
void main()
{
vec4 worldPos = matModel * vec4(vertexPosition, 1.0);
fragPosition = worldPos.xyz;
fragTexCoord = vertexTexCoord;
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
fragNormal = normalMatrix * vertexNormal;
gl_Position = matProjection * matView * worldPos;
}

162
source/shader/pbr.fs Normal file
View File

@ -0,0 +1,162 @@
#version 330
#define MAX_LIGHTS 4
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT 1
#define PI 3.14159265358979323846
struct Light {
int enabled;
int type;
vec3 position;
vec3 target;
vec4 color;
float intensity;
};
// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
in vec2 fragTexCoord;
in vec4 fragColor;
in vec3 fragNormal;
in vec4 shadowPos;
in mat3 TBN;
// Output fragment color
out vec4 finalColor;
// Input uniform values
uniform int numOfLights;
uniform sampler2D albedoMap;
uniform sampler2D mraMap;
uniform sampler2D normalMap;
uniform sampler2D emissiveMap; // r: Hight g:emissive
uniform vec2 tiling;
uniform vec2 offset;
uniform int useTexAlbedo;
uniform int useTexNormal;
uniform int useTexMRA;
uniform int useTexEmissive;
uniform vec4 albedoColor;
uniform vec4 emissiveColor;
uniform float normalValue;
uniform float metallicValue;
uniform float roughnessValue;
uniform float aoValue;
uniform float emissivePower;
// Input lighting values
uniform Light lights[MAX_LIGHTS];
uniform vec3 viewPos;
uniform vec3 ambientColor;
uniform float ambient;
// Reflectivity in range 0.0 to 1.0
// NOTE: Reflectivity is increased when surface view at larger angle
vec3 SchlickFresnel(float hDotV,vec3 refl)
{
return refl + (1.0 - refl)*pow(1.0 - hDotV, 5.0);
}
float GgxDistribution(float nDotH,float roughness)
{
float a = roughness * roughness * roughness * roughness;
float d = nDotH * nDotH * (a - 1.0) + 1.0;
d = PI * d * d;
return a / max(d,0.0000001);
}
float GeomSmith(float nDotV,float nDotL,float roughness)
{
float r = roughness + 1.0;
float k = r*r / 8.0;
float ik = 1.0 - k;
float ggx1 = nDotV/(nDotV*ik + k);
float ggx2 = nDotL/(nDotL*ik + k);
return ggx1*ggx2;
}
vec3 ComputePBR()
{
vec3 albedo = texture(albedoMap,vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb;
albedo = vec3(albedoColor.x*albedo.x, albedoColor.y*albedo.y, albedoColor.z*albedo.z);
float metallic = clamp(metallicValue, 0.0, 1.0);
float roughness = clamp(roughnessValue, 0.0, 1.0);
float ao = clamp(aoValue, 0.0, 1.0);
if (useTexMRA == 1)
{
vec4 mra = texture(mraMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y))*useTexMRA;
metallic = clamp(mra.r + metallicValue, 0.04, 1.0);
roughness = clamp(mra.g + roughnessValue, 0.04, 1.0);
ao = (mra.b + aoValue)*0.5;
}
vec3 N = normalize(fragNormal);
if (useTexNormal == 1)
{
N = texture(normalMap, vec2(fragTexCoord.x*tiling.x + offset.y, fragTexCoord.y*tiling.y + offset.y)).rgb;
N = normalize(N*2.0 - 1.0);
N = normalize(N*TBN);
}
vec3 V = normalize(viewPos - fragPosition);
vec3 emissive = vec3(0);
emissive = (texture(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * useTexEmissive;
// return N;//vec3(metallic,metallic,metallic);
// if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
vec3 baseRefl = mix(vec3(0.04), albedo.rgb, metallic);
vec3 lightAccum = vec3(0.0); // Acumulate lighting lum
for (int i = 0; i < numOfLights; i++)
{
vec3 L = normalize(lights[i].position - fragPosition); // Compute light vector
vec3 H = normalize(V + L); // Compute halfway bisecting vector
float dist = length(lights[i].position - fragPosition); // Compute distance to light
float attenuation = 1.0/(dist*dist*0.23); // Compute attenuation
vec3 radiance = lights[i].color.rgb*lights[i].intensity*attenuation; // Compute input radiance, light energy comming in
// Cook-Torrance BRDF distribution function
float nDotV = max(dot(N,V), 0.0000001);
float nDotL = max(dot(N,L), 0.0000001);
float hDotV = max(dot(H,V), 0.0);
float nDotH = max(dot(N,H), 0.0);
float D = GgxDistribution(nDotH, roughness); // Larger the more micro-facets aligned to H
float G = GeomSmith(nDotV, nDotL, roughness); // Smaller the more micro-facets shadow
vec3 F = SchlickFresnel(hDotV, baseRefl); // Fresnel proportion of specular reflectance
vec3 spec = (D*G*F)/(4.0*nDotV*nDotL);
// Difuse and spec light can't be above 1.0
// kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
vec3 kD = vec3(1.0) - F;
// Mult kD by the inverse of metallnes, only non-metals should have diffuse light
kD *= 1.0 - metallic;
lightAccum += ((kD*albedo.rgb/PI + spec)*radiance*nDotL)*lights[i].enabled; // Angle of light has impact on result
}
vec3 ambientFinal = (ambientColor + albedo)*ambient*0.5;
return ambientFinal + lightAccum*ao + emissive;
}
void main()
{
vec3 color = ComputePBR();
// HDR tonemapping
color = pow(color, color + vec3(1.0));
// Gamma correction
color = pow(color, vec3(1.0/2.2));
finalColor = vec4(color, 1.0);
}

48
source/shader/pbr.vs Normal file
View File

@ -0,0 +1,48 @@
#version 330
// Input vertex attributes
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec3 vertexTangent;
in vec4 vertexColor;
// Input uniform values
uniform mat4 mvp;
uniform mat4 matModel;
uniform mat4 matNormal;
uniform vec3 lightPos;
uniform vec4 difColor;
// Output vertex attributes (to fragment shader)
out vec3 fragPosition;
out vec2 fragTexCoord;
out vec4 fragColor;
out vec3 fragNormal;
out mat3 TBN;
const float normalOffset = 0.1;
void main()
{
// Compute binormal from vertex normal and tangent
vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
// Compute fragment normal based on normal transformations
mat3 normalMatrix = transpose(inverse(mat3(matModel)));
// Compute fragment position based on model transformations
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0f));
fragTexCoord = vertexTexCoord*2.0;
fragNormal = normalize(normalMatrix*vertexNormal);
vec3 fragTangent = normalize(normalMatrix*vertexTangent);
fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
fragBinormal = cross(fragNormal, fragTangent);
TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
// Calculate final vertex position
gl_Position = mvp*vec4(vertexPosition, 1.0);
}

30
source/shader/skybox.fs Normal file
View File

@ -0,0 +1,30 @@
#version 430
// Input vertex attributes (from vertex shader)
in vec3 fragPosition;
// Input uniform values
uniform samplerCube environmentMap;
uniform bool vflipped;
uniform bool doGamma;
// Output fragment color
out vec4 finalColor;
void main()
{
// Fetch color from texture map
vec3 color = vec3(0.0);
if (vflipped) color = texture(environmentMap, vec3(fragPosition.x, -fragPosition.y, fragPosition.z)).rgb;
else color = texture(environmentMap, fragPosition).rgb;
if (doGamma)// Apply gamma correction
{
color = color/(color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));
}
// Calculate final fragment color
finalColor = vec4(color, 1.0);
}

24
source/shader/skybox.vs Normal file
View File

@ -0,0 +1,24 @@
#version 430
// Input vertex attributes
in vec3 vertexPosition;
// Input uniform values
uniform mat4 matProjection;
uniform mat4 matView;
// Output vertex attributes (to fragment shader)
out vec3 fragPosition;
void main()
{
// Calculate fragment position based on model transformations
fragPosition = vertexPosition;
// Remove translation from the view matrix
mat4 rotView = mat4(mat3(matView));
vec4 clipPos = matProjection*rotView*vec4(vertexPosition, 1.0);
// Calculate final vertex position
gl_Position = clipPos;
}

40
source/shader/sobel.fs Normal file
View File

@ -0,0 +1,40 @@
#version 430
in vec2 fragTexCoord;
in vec4 fragColor;
uniform sampler2D texture0;
uniform vec4 colDiffuse;
out vec4 finalColor;
uniform vec2 resolution = vec2(800, 450);
vec4 sobel_filter(void) {
float x = 1.0/resolution.x;
float y = 1.0/resolution.y;
vec4 horizEdge = vec4(0.0);
horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))* 1.0;
horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y ))* 2.0;
horizEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))* 1.0;
horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))* 1.0;
horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y ))* 2.0;
horizEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))* 1.0;
vec4 vertEdge = vec4(0.0);
vertEdge -= texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y - y))* 1.0;
vertEdge -= texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y - y))* 2.0;
vertEdge -= texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y - y))* 1.0;
vertEdge += texture2D(texture0, vec2(fragTexCoord.x - x, fragTexCoord.y + y))* 1.0;
vertEdge += texture2D(texture0, vec2(fragTexCoord.x , fragTexCoord.y + y))* 2.0;
vertEdge += texture2D(texture0, vec2(fragTexCoord.x + x, fragTexCoord.y + y))* 1.0;
vec3 edge = sqrt((horizEdge.rgb*horizEdge.rgb) + (vertEdge.rgb*vertEdge.rgb));
return (vec4(edge, texture2D(texture0, fragTexCoord).a));
}
void main(void) {
finalColor = fragColor - sobel_filter();
}

View File

@ -0,0 +1,27 @@
#version 460 core
flat in vec3 frag_pos;
flat in vec3 face_normal;
flat in int block_id_out;
flat in int face;
flat in int extra;
out vec4 frag_color;
const vec3 color[6] = vec3[](
vec3(0.5, 0.0, 0.0),
vec3(0.0, 0.5, 0.0),
vec3(0.2, 0.2, 0.5),
vec3(0.5, 0.5, 0.0),
vec3(0.5, 0.0, 0.5),
vec3(0.0, 0.5, 0.5)
);
void main() {
if (block_id_out == 1) {
frag_color = vec4(color[face], 1.0);
}
if (block_id_out == 2) {
frag_color = vec4(0.0, 0.0, 0.5, 0.5);
}
}

View File

@ -0,0 +1,77 @@
#version 460 core
#define FACE_YP 0
#define FACE_Y 1
#define FACE_XP 2
#define FACE_X 3
#define FACE_ZP 4
#define FACE_Z 5
layout(location = 0) in vec3 aPos;
layout(location = 1) in int packed_data;
layout(location = 2) in int block_id;
layout(std430, binding = 3) buffer ssbo {
int ssbo_data[];
};
uniform mat4 mvp;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matModel;
out vec3 frag_pos;
out vec3 face_normal;
flat out int block_id_out;
flat out int face;
const vec3 NORMALS[6] = vec3[](
vec3( 0.0, 1.0, 0.0),
vec3( 0.0, -1.0, 0.0),
vec3( 1.0, 0.0, 0.0),
vec3(-1.0, 0.0, 0.0),
vec3( 0.0, 0.0, 1.0),
vec3( 0.0, 0.0, -1.0)
);
const mat3 ROTATIONS[6] = mat3[](
mat3(1,0,0, 0,0,-1, 0,1,0),
mat3(1,0,0, 0,0,1, 0,-1,0),
mat3(0,0,-1, 0,1,0, 1,0,0),
mat3(0,0,1, 0,1,0, -1,0,0),
mat3(1,0,0, 0,1,0, 0,0,1),
mat3(-1,0,0, 0,1,0, 0,0,-1)
);
const vec3 FACE_OFFSET[6] = vec3[](
vec3(0.0, 0.5, 0.5),
vec3(0.0, 0.0, 0.0),
vec3(0.5, 0.0, 0.5),
vec3(0.0, 0.0, 0.0),
vec3(0.0, 0.0, 0.5),
vec3(0.5, 0.0, 0.0)
);
void main() {
int x = (packed_data >> 0) & 0x3F;
int y = (packed_data >> 6) & 0x3F;
int z = (packed_data >> 12) & 0x3F;
int width = (packed_data >> 18) & 0x3F;
int height = (packed_data >> 24) & 0x3F;
face = block_id & 0x07;
block_id_out = (block_id >> 3) & 0x1FFF;
vec3 vox_pos = vec3(x, y, z) * 0.5;
vec3 vPos = vec3(aPos.x * width, aPos.y * height, aPos.z);
vec3 rotated_aPos = ROTATIONS[face] * vPos;
int packed_pos = ssbo_data[gl_DrawID];//need to be called via multi draw command ???
vec3 chunk_pos = vec3((packed_pos & 0x1f), ((packed_pos >> 5) & 0x1f), ((packed_pos >> 10) & 0x1f));
vec3 world_pos = vox_pos + FACE_OFFSET[face] + chunk_pos + rotated_aPos;
gl_Position = matProjection * matView * matModel * vec4(world_pos, 1.0);
frag_pos = world_pos;
face_normal = NORMALS[face];
}

216
source/tools/main.c Normal file
View File

@ -0,0 +1,216 @@
#include <raylib.h>
#define RAYGUI_IMPLEMENTATION
#include <raygui.h>
#include <engine/core/utils.h>
#include <engine/core/thread.h>
typedef enum {
map_edit_mod,
ui_edit_mod,
obj_edit_mod,
item_edit_mod
} edm_enum;
int tool = 0;
bool saved = false;
bool mode_enable = false;
int error = 0;
Rectangle error_bound = (Rectangle){};
int editor_mode = 0;
void startscreen() {
BeginDrawing();
EndDrawing();
}
// ui
void top_bar() {
GuiStatusBar((Rectangle){0, 0, (float)GetScreenWidth(), 30}, "");
if (GuiDropdownBox((Rectangle){0, 0, 120, 30}, "map;item;obj;ui", &editor_mode, mode_enable))
mode_enable = !mode_enable;
}
void left_bar() {
GuiDummyRec((Rectangle){0, 30, 200, (float)GetScreenHeight() - 30}, "Debug");
}
void right_bar() {
static bool fract = false;
static bool noise_type = false;
static Vector2 scroll;
static Rectangle view;
GuiScrollPanel((Rectangle){(float)GetScreenWidth() - 200, 30, 200, (float)GetScreenHeight() - 30}, "Setting", (Rectangle){0, 0, 180, (float)GetScreenHeight() - 60}, &scroll, &view);
BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height);
EndScissorMode();
}
//editor mode
void map_editor() {
// static Camera2D cam = (Camera2D){
// .offset = {0, 0},
// .target = {0, 0},
// .rotation = 0,
// .zoom = 0.8,
// };
// BeginMode2D(cam);
// EndMode2D();
}
void item_editor() {
// Name
// type
// max_stack
// description
}
void obj_editor() {
}
void ui_editor() {
}
void drop_down_terminal(bool show_term) {
Rectangle rec = {200, -200, GetScreenWidth() - 400, 200};
static int height = 0;
static Vector2 scroll;
static Rectangle view;
static bool enter_pressed;
static char text_buffer[100];
if (show_term) {
if (height < 230) {
height += 10;
}
} else if (height > 0) {
height -= 10;
}
if (height > 0) {
GuiScrollPanel((Rectangle){rec.x, rec.y + height, rec.width, rec.height},
"", (Rectangle){rec.x, rec.y + height, rec.width - 20, rec.height}, &scroll, &view);
BeginScissorMode(rec.x, rec.y + height, rec.width - 10, rec.height);
EndScissorMode();
if (GuiTextBox((Rectangle){200, rec.y + 200 + height, rec.width, 20}, &text_buffer[0], 99, show_term && height == 230))
enter_pressed = true;
}
}
void update_terminal_input(bool *term_open) {
if (IsKeyPressed(KEY_TAB)) {
*term_open = !*term_open;
}
}
void update_editor_input(bool *term_open) {
if (IsKeyPressed(KEY_TAB)) {
*term_open = !*term_open;
}
}
//
int main(void) {
InitWindow(1920, 1080, "Haven Engine");
SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE | FLAG_WINDOW_RESIZABLE);
RenderTexture2D fbo;
GuiLoadStyle("assets/style_terminal.rgs");
fbo = LoadRenderTexture(GetScreenWidth() - 400, GetScreenHeight() - 30);
bool show_term = false;
haven_thread_mgr_init();
SetTargetFPS(30);
// EnableEventWaiting();
while (!WindowShouldClose()) {
if (show_term) {
update_terminal_input(&show_term);
} else {
update_editor_input(&show_term);
}
BeginTextureMode(fbo);
switch (editor_mode) {
case (map_edit_mod): {
map_editor();
break;
}
case (item_edit_mod): {
item_editor();
break;
}
case (obj_edit_mod): {
obj_editor();
break;
}
case (ui_edit_mod): {
ui_editor();
break;
}
default:
break;
}
EndTextureMode();
BeginDrawing();
ClearBackground(BLACK);
DrawTextureRec(fbo.texture, (Rectangle){0, 0, (float)fbo.texture.width, (float)-fbo.texture.height}, (Vector2){200, 30}, WHITE);
right_bar();
left_bar();
// switch (error) {
// case (error_file_not_found): {
// int result = GuiMessageBox(error_bound, "#152#ERROR!", "this file doesn't exist!!", "OK");
// if (result == 1) {
// error = error_dummy;
// }
// break;
// }
// case (error_file_empty): {
// int result = GuiMessageBox(error_bound, "#152#ERROR!", "Name Empty!!", "OK");
// if (result == 1) {
// error = error_dummy;
// }
// break;
// }
// case (error_file_corrupted): {
// int result = GuiMessageBox(error_bound, "#152#ERROR!", "this file is corrupted!!", "OK");
// if (result == 1) {
// error = error_dummy;
// }
// break;
// }
// case (error_file_exist): {
// int result = GuiMessageBox(error_bound, "#152#ERROR!", "this filename already exist!!", "OK");
// if (result == 1) {
// error = error_dummy;
// }
// break;
// }
// default:break;
// }
drop_down_terminal(show_term);
top_bar();
EndDrawing();
}
haven_thread_mgr_close();
CloseWindow();
return 0;
}