summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
committerclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
commit847962910f0bff071f3bf07c9abb87764fb6cac3 (patch)
treeddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /src
downloadlibrw-master.tar.gz
librw-master.zip
Import aap/librw @ masterHEADmaster
Snapshot for re3/reVC vendoring, per @lzcnt. Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt270
-rw-r--r--src/anim.cpp293
-rw-r--r--src/base.cpp1123
-rw-r--r--src/base.err24
-rw-r--r--src/bmp.cpp284
-rw-r--r--src/camera.cpp524
-rw-r--r--src/charset.cpp244
-rw-r--r--src/clump.cpp669
-rw-r--r--src/d3d/d3d.cpp1087
-rw-r--r--src/d3d/d3d8.cpp678
-rw-r--r--src/d3d/d3d8matfx.cpp56
-rw-r--r--src/d3d/d3d8render.cpp65
-rw-r--r--src/d3d/d3d8skin.cpp56
-rw-r--r--src/d3d/d3d9.cpp853
-rw-r--r--src/d3d/d3d9matfx.cpp313
-rw-r--r--src/d3d/d3d9render.cpp185
-rw-r--r--src/d3d/d3d9skin.cpp418
-rw-r--r--src/d3d/d3ddevice.cpp2022
-rw-r--r--src/d3d/d3dimmed.cpp404
-rw-r--r--src/d3d/d3drender.cpp424
-rw-r--r--src/d3d/rwd3d.h421
-rw-r--r--src/d3d/rwd3d8.h74
-rw-r--r--src/d3d/rwd3d9.h112
-rw-r--r--src/d3d/rwd3dimpl.h81
-rw-r--r--src/d3d/rwxbox.h195
-rw-r--r--src/d3d/rwxboximpl.h11
-rw-r--r--src/d3d/shaders/default_PS.h72
-rw-r--r--src/d3d/shaders/default_PS.hlsl19
-rw-r--r--src/d3d/shaders/default_VS.hlsl51
-rw-r--r--src/d3d/shaders/default_all_VS.h491
-rw-r--r--src/d3d/shaders/default_amb_VS.h156
-rw-r--r--src/d3d/shaders/default_amb_dir_VS.h272
-rw-r--r--src/d3d/shaders/default_tex_PS.h89
-rw-r--r--src/d3d/shaders/im2d_PS.h72
-rw-r--r--src/d3d/shaders/im2d_PS.hlsl19
-rw-r--r--src/d3d/shaders/im2d_VS.h107
-rw-r--r--src/d3d/shaders/im2d_VS.hlsl30
-rw-r--r--src/d3d/shaders/im2d_tex_PS.h89
-rw-r--r--src/d3d/shaders/lighting.h44
-rw-r--r--src/d3d/shaders/make_default.cmd11
-rw-r--r--src/d3d/shaders/make_matfx.cmd7
-rw-r--r--src/d3d/shaders/make_skin.cmd4
-rw-r--r--src/d3d/shaders/matfx_env_PS.h124
-rw-r--r--src/d3d/shaders/matfx_env_PS.hlsl42
-rw-r--r--src/d3d/shaders/matfx_env_VS.hlsl59
-rw-r--r--src/d3d/shaders/matfx_env_all_VS.h535
-rw-r--r--src/d3d/shaders/matfx_env_amb_VS.h225
-rw-r--r--src/d3d/shaders/matfx_env_amb_dir_VS.h316
-rw-r--r--src/d3d/shaders/matfx_env_tex_PS.h141
-rw-r--r--src/d3d/shaders/skin_VS.hlsl63
-rw-r--r--src/d3d/shaders/skin_all_VS.h664
-rw-r--r--src/d3d/shaders/skin_amb_VS.h253
-rw-r--r--src/d3d/shaders/skin_amb_dir_VS.h503
-rw-r--r--src/d3d/shaders/standardConstants.h28
-rw-r--r--src/d3d/xbox.cpp1005
-rw-r--r--src/d3d/xboxmatfx.cpp53
-rw-r--r--src/d3d/xboxskin.cpp252
-rw-r--r--src/d3d/xboxvfmt.cpp115
-rw-r--r--src/engine.cpp573
-rw-r--r--src/error.cpp49
-rw-r--r--src/frame.cpp470
-rw-r--r--src/geometry.cpp1053
-rw-r--r--src/geoplg.cpp339
-rw-r--r--src/gl/gl3.cpp56
-rw-r--r--src/gl/gl3device.cpp2411
-rw-r--r--src/gl/gl3immed.cpp317
-rw-r--r--src/gl/gl3matfx.cpp258
-rw-r--r--src/gl/gl3pipe.cpp333
-rw-r--r--src/gl/gl3raster.cpp1045
-rw-r--r--src/gl/gl3render.cpp193
-rw-r--r--src/gl/gl3shader.cpp363
-rw-r--r--src/gl/gl3skin.cpp366
-rw-r--r--src/gl/glad/glad.c1590
-rw-r--r--src/gl/glad/glad.h2808
-rw-r--r--src/gl/glad/khrplatform.h290
-rw-r--r--src/gl/rwgl3.h307
-rw-r--r--src/gl/rwgl3impl.h92
-rw-r--r--src/gl/rwgl3plg.h16
-rw-r--r--src/gl/rwgl3shader.h69
-rw-r--r--src/gl/rwwdgl.h88
-rw-r--r--src/gl/shaders/Makefile45
-rw-r--r--src/gl/shaders/default.vert23
-rw-r--r--src/gl/shaders/default_vs_gl.inc25
-rw-r--r--src/gl/shaders/header.frag30
-rw-r--r--src/gl/shaders/header.vert128
-rw-r--r--src/gl/shaders/header_fs.inc32
-rw-r--r--src/gl/shaders/header_vs.inc130
-rw-r--r--src/gl/shaders/im2d.vert18
-rw-r--r--src/gl/shaders/im2d_gl.inc20
-rw-r--r--src/gl/shaders/im3d.vert15
-rw-r--r--src/gl/shaders/im3d_gl.inc17
-rw-r--r--src/gl/shaders/matfx_env.frag34
-rw-r--r--src/gl/shaders/matfx_env.vert31
-rw-r--r--src/gl/shaders/matfx_gl.inc69
-rw-r--r--src/gl/shaders/simple.frag14
-rw-r--r--src/gl/shaders/simple_fs_gl.inc17
-rw-r--r--src/gl/shaders/skin.vert32
-rw-r--r--src/gl/shaders/skin_gl.inc34
-rw-r--r--src/gl/wdgl.cpp875
-rw-r--r--src/hanim.cpp414
-rw-r--r--src/image.cpp1324
-rw-r--r--src/light.cpp163
-rw-r--r--src/lodepng/lodepng.cpp6410
-rw-r--r--src/lodepng/lodepng.h1945
-rw-r--r--src/matfx.cpp641
-rw-r--r--src/pipeline.cpp198
-rw-r--r--src/plg.cpp246
-rw-r--r--src/png.cpp151
-rw-r--r--src/prim.cpp70
-rw-r--r--src/ps2/pds.cpp203
-rw-r--r--src/ps2/ps2.cpp1583
-rw-r--r--src/ps2/ps2device.cpp49
-rw-r--r--src/ps2/ps2matfx.cpp68
-rw-r--r--src/ps2/ps2raster.cpp2238
-rw-r--r--src/ps2/ps2skin.cpp334
-rw-r--r--src/ps2/rwps2.h281
-rw-r--r--src/ps2/rwps2impl.h16
-rw-r--r--src/ps2/rwps2plg.h27
-rw-r--r--src/raster.cpp559
-rw-r--r--src/render.cpp96
-rw-r--r--src/rwanim.h195
-rw-r--r--src/rwbase.h716
-rw-r--r--src/rwcharset.h25
-rw-r--r--src/rwengine.h275
-rw-r--r--src/rwerror.h25
-rw-r--r--src/rwobjects.h906
-rw-r--r--src/rwpipeline.h65
-rw-r--r--src/rwplg.h85
-rw-r--r--src/rwplugins.h252
-rw-r--r--src/rwrender.h140
-rw-r--r--src/rwuserdata.h94
-rw-r--r--src/skin.cpp486
-rw-r--r--src/texture.cpp596
-rw-r--r--src/tga.cpp222
-rw-r--r--src/tristrip.cpp682
-rw-r--r--src/userdata.cpp364
-rw-r--r--src/uvanim.cpp502
-rw-r--r--src/vgafont.inc256
-rw-r--r--src/world.cpp238
139 files changed, 54612 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..e9143b7
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,270 @@
+add_library(librw
+ "${PROJECT_SOURCE_DIR}/args.h"
+ "${PROJECT_SOURCE_DIR}/rw.h"
+
+ anim.cpp
+ base.cpp
+ bmp.cpp
+ camera.cpp
+ charset.cpp
+ clump.cpp
+ engine.cpp
+ error.cpp
+ frame.cpp
+ geometry.cpp
+ geoplg.cpp
+ hanim.cpp
+ image.cpp
+ light.cpp
+ matfx.cpp
+ pipeline.cpp
+ plg.cpp
+ png.cpp
+ prim.cpp
+ raster.cpp
+ render.cpp
+ rwanim.h
+ rwengine.h
+ rwerror.h
+ rwobjects.h
+ rwpipeline.h
+ rwplg.h
+ rwplugins.h
+ rwrender.h
+ rwuserdata.h
+ skin.cpp
+ texture.cpp
+ tga.cpp
+ tristrip.cpp
+ userdata.cpp
+ uvanim.cpp
+ world.cpp
+
+ d3d/d3d8.cpp
+ d3d/d3d8matfx.cpp
+ d3d/d3d8render.cpp
+ d3d/d3d8skin.cpp
+ d3d/d3d9.cpp
+ d3d/d3d9matfx.cpp
+ d3d/d3d9render.cpp
+ d3d/d3d9skin.cpp
+ d3d/d3d.cpp
+ d3d/d3ddevice.cpp
+ d3d/d3dimmed.cpp
+ d3d/d3drender.cpp
+ d3d/rwd3d8.h
+ d3d/rwd3d9.h
+ d3d/rwd3d.h
+ d3d/rwd3dimpl.h
+ d3d/rwxbox.h
+ d3d/rwxboximpl.h
+ d3d/xbox.cpp
+ d3d/xboxmatfx.cpp
+ d3d/xboxskin.cpp
+ d3d/xboxvfmt.cpp
+
+ gl/gl3.cpp
+ gl/gl3device.cpp
+ gl/gl3immed.cpp
+ gl/gl3matfx.cpp
+ gl/gl3pipe.cpp
+ gl/gl3raster.cpp
+ gl/gl3render.cpp
+ gl/gl3shader.cpp
+ gl/gl3skin.cpp
+ gl/rwgl3.h
+ gl/rwgl3impl.h
+ gl/rwgl3plg.h
+ gl/rwgl3shader.h
+ gl/rwwdgl.h
+ gl/wdgl.cpp
+ gl/glad/glad.h
+ gl/glad/glad.c
+ gl/glad/khrplatform.h
+
+ lodepng/lodepng.h
+ lodepng/lodepng.cpp
+
+ ps2/pds.cpp
+ ps2/ps2.cpp
+ ps2/ps2device.cpp
+ ps2/ps2matfx.cpp
+ ps2/ps2raster.cpp
+ ps2/ps2skin.cpp
+ ps2/rwps2.h
+ ps2/rwps2impl.h
+ ps2/rwps2plg.h
+)
+add_library(librw::librw ALIAS librw)
+
+target_include_directories(librw
+ INTERFACE
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
+ $<INSTALL_INTERFACE:${LIBRW_INSTALL_INCLUDEDIR}>
+)
+
+target_compile_definitions(librw
+ PRIVATE
+ LODEPNG_NO_COMPILE_CPP
+ "$<IF:$<CONFIG:DEBUG>,DEBUG,NDEBUG>"
+ PUBLIC
+ "RW_${LIBRW_PLATFORM}"
+)
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ target_link_libraries(librw
+ PRIVATE
+ m
+ )
+endif()
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
+ target_compile_options(librw
+ PRIVATE
+ "-Wall"
+ )
+ if (NOT LIBRW_PLATFORM_PS2)
+ target_compile_options(librw
+ PRIVATE
+ "-Wextra"
+ "-Wdouble-promotion"
+ "-Wpedantic"
+ )
+ endif()
+elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ target_compile_options(librw
+ PUBLIC
+ /wd4996 /wd4244
+ )
+endif()
+
+set_target_properties(librw
+ PROPERTIES
+ C_STANDARD 11
+ C_EXTENSIONS OFF
+ C_STANDARD_REQUIRED ON
+ CXX_STANDARD 11
+ CXX_EXTENSIONS OFF
+ CXX_STANDARD_REQUIRED ON
+ PREFIX ""
+)
+
+if(LIBRW_PLATFORM_GL3)
+ if (LIBRW_GL3_GFXLIB STREQUAL "GLFW")
+ if (NOT TARGET glfw)
+ find_package(glfw3 REQUIRED)
+ endif()
+ target_compile_definitions(librw PUBLIC LIBRW_GLFW)
+ target_link_libraries(librw
+ PUBLIC
+ glfw
+ )
+ elseif (LIBRW_GL3_GFXLIB STREQUAL "SDL2")
+ if (NOT TARGET SDL2::SDL2)
+ find_package(SDL2 CONFIG)
+ if (NOT TARGET SDL2::SDL2)
+ find_package(SDL2 MODULE REQUIRED)
+ endif()
+ endif()
+ target_compile_definitions(librw PUBLIC LIBRW_SDL2)
+ target_link_libraries(librw
+ PUBLIC
+ SDL2::SDL2
+ )
+ elseif (LIBRW_GL3_GFXLIB STREQUAL "SDL3")
+ find_package(SDL3 CONFIG REQUIRED)
+ target_compile_definitions(librw PUBLIC LIBRW_SDL3)
+ target_link_libraries(librw
+ PUBLIC
+ SDL3::SDL3
+ )
+ endif()
+
+ set(OpenGL_GL_PREFERENCE GLVND)
+ find_package(OpenGL)
+ if(TARGET OpenGL::OpenGL)
+ target_link_libraries(librw
+ PRIVATE
+ OpenGL::OpenGL
+ )
+ elseif(TARGET OpenGL::EGL)
+ target_link_libraries(librw
+ PRIVATE
+ OpenGL::EGL
+ )
+ elseif(TARGET OpenGL::GL)
+ target_link_libraries(librw
+ PRIVATE
+ OpenGL::GL
+ )
+ else()
+ message(FATAL_ERROR "find_package(OpenGL) failed.")
+ endif()
+elseif(LIBRW_PLATFORM_D3D9)
+ target_link_libraries(librw
+ PRIVATE
+ d3d9
+ xinput
+ )
+endif()
+
+if(LIBRW_INSTALL)
+ install(
+ FILES
+ "${PROJECT_SOURCE_DIR}/args.h"
+ "${PROJECT_SOURCE_DIR}/rw.h"
+ DESTINATION "${LIBRW_INSTALL_INCLUDEDIR}"
+ )
+ install(
+ FILES
+ base.err
+ rwbase.h
+ rwcharset.h
+ rwerror.h
+ rwplg.h
+ rwrender.h
+ rwengine.h
+ rwpipeline.h
+ rwobjects.h
+ rwanim.h
+ rwplugins.h
+ rwuserdata.h
+ DESTINATION "${LIBRW_INSTALL_INCLUDEDIR}/src"
+ )
+ install(
+ FILES
+ d3d/rwxbox.h
+ d3d/rwd3d.h
+ d3d/rwd3d8.h
+ d3d/rwd3d9.h
+ DESTINATION "${LIBRW_INSTALL_INCLUDEDIR}/src/d3d"
+ )
+ install(
+ FILES
+ ps2/rwps2.h
+ ps2/rwps2plg.h
+ DESTINATION "${LIBRW_INSTALL_INCLUDEDIR}/src/ps2"
+ )
+ install(
+ FILES
+ gl/rwwdgl.h
+ gl/rwgl3.h
+ gl/rwgl3plg.h
+ gl/rwgl3shader.h
+ DESTINATION "${LIBRW_INSTALL_INCLUDEDIR}/src/gl"
+ )
+
+ install(
+ FILES
+ gl/glad/glad.h
+ gl/glad/khrplatform.h
+ DESTINATION "${LIBRW_INSTALL_INCLUDEDIR}/src/gl/glad"
+ )
+
+ install(
+ TARGETS librw
+ EXPORT librw-targets
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ )
+endif()
diff --git a/src/anim.cpp b/src/anim.cpp
new file mode 100644
index 0000000..2003f1b
--- /dev/null
+++ b/src/anim.cpp
@@ -0,0 +1,293 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwanim.h"
+#include "rwplugins.h"
+
+#define PLUGIN_ID ID_ANIMANIMATION
+
+namespace rw {
+
+//
+// AnimInterpolatorInfo
+//
+
+#define MAXINTERPINFO 10
+
+static AnimInterpolatorInfo *interpInfoList[MAXINTERPINFO];
+
+void
+AnimInterpolatorInfo::registerInterp(AnimInterpolatorInfo *interpInfo)
+{
+ for(int32 i = 0; i < MAXINTERPINFO; i++)
+ if(interpInfoList[i] == nil){
+ interpInfoList[i] = interpInfo;
+ return;
+ }
+ assert(0 && "no room for interpolatorInfo");
+}
+
+void
+AnimInterpolatorInfo::unregisterInterp(AnimInterpolatorInfo *interpInfo)
+{
+ for(int32 i = 0; i < MAXINTERPINFO; i++)
+ if(interpInfoList[i] == interpInfo){
+ rwFree(interpInfoList[i]);
+ interpInfoList[i] = nil;
+ return;
+ }
+}
+
+AnimInterpolatorInfo*
+AnimInterpolatorInfo::find(int32 id)
+{
+ for(int32 i = 0; i < MAXINTERPINFO; i++){
+ if(interpInfoList[i] && interpInfoList[i]->id == id)
+ return interpInfoList[i];
+ }
+ return nil;
+}
+
+//
+// Animation
+//
+
+Animation*
+Animation::create(AnimInterpolatorInfo *interpInfo, int32 numFrames,
+ int32 flags, float duration)
+{
+ int32 sz = sizeof(Animation) +
+ numFrames*interpInfo->animKeyFrameSize +
+ interpInfo->customDataSize;
+ uint8 *data = (uint8*)rwMalloc(sz, MEMDUR_EVENT | ID_ANIMANIMATION);
+ if(data == nil){
+ RWERROR((ERR_ALLOC, sz));
+ return nil;
+ }
+ Animation *anim = (Animation*)data;
+ data += sizeof(Animation);
+ anim->interpInfo = interpInfo;
+ anim->numFrames = numFrames;
+ anim->flags = flags;
+ anim->duration = duration;
+ anim->keyframes = data;
+ data += anim->numFrames*interpInfo->animKeyFrameSize;
+ anim->customData = data;
+ return anim;
+}
+
+void
+Animation::destroy(void)
+{
+ rwFree(this);
+}
+
+int32
+Animation::getNumNodes(void)
+{
+ int32 sz = this->interpInfo->animKeyFrameSize;
+ KeyFrameHeader *first = (KeyFrameHeader*)this->keyframes;
+ int32 n = 0;
+ for(KeyFrameHeader *f = first; f->prev != first; f = f->next(sz))
+ n++;
+ return n;
+}
+
+Animation*
+Animation::streamRead(Stream *stream)
+{
+ Animation *anim;
+ if(stream->readI32() != 0x100)
+ return nil;
+ int32 typeID = stream->readI32();
+ AnimInterpolatorInfo *interpInfo = AnimInterpolatorInfo::find(typeID);
+ int32 numFrames = stream->readI32();
+ int32 flags = stream->readI32();
+ float duration = stream->readF32();
+ anim = Animation::create(interpInfo, numFrames, flags, duration);
+ interpInfo->streamRead(stream, anim);
+ return anim;
+}
+
+Animation*
+Animation::streamReadLegacy(Stream *stream)
+{
+ Animation *anim;
+ AnimInterpolatorInfo *interpInfo = AnimInterpolatorInfo::find(1);
+ int32 numFrames = stream->readI32();
+ int32 flags = stream->readI32();
+ float duration = stream->readF32();
+ anim = Animation::create(interpInfo, numFrames, flags, duration);
+ HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes;
+ for(int32 i = 0; i < anim->numFrames; i++){
+ stream->read32(&frames[i].q, 4*4);
+ frames[i].q = conj(frames[i].q);
+ stream->read32(&frames[i].t, 3*4);
+ frames[i].time = stream->readF32();
+ int32 prev = stream->readI32()/0x24;
+ frames[i].prev = &frames[prev];
+ }
+ return anim;
+}
+
+bool
+Animation::streamWrite(Stream *stream)
+{
+ writeChunkHeader(stream, ID_ANIMANIMATION, this->streamGetSize());
+ stream->writeI32(0x100);
+ stream->writeI32(this->interpInfo->id);
+ stream->writeI32(this->numFrames);
+ stream->writeI32(this->flags);
+ stream->writeF32(this->duration);
+ this->interpInfo->streamWrite(stream, this);
+ return true;
+}
+
+bool
+Animation::streamWriteLegacy(Stream *stream)
+{
+ stream->writeI32(this->numFrames);
+ stream->writeI32(this->flags);
+ stream->writeF32(this->duration);
+ assert(interpInfo->id == 1);
+ HAnimKeyFrame *frames = (HAnimKeyFrame*)this->keyframes;
+ for(int32 i = 0; i < this->numFrames; i++){
+ frames[i].q = conj(frames[i].q);
+ stream->write32(&frames[i].q, 4*4);
+ frames[i].q = conj(frames[i].q);
+ stream->write32(&frames[i].t, 3*4);
+ stream->writeF32(frames[i].time);
+ stream->writeI32((frames[i].prev - frames)*0x24);
+ }
+ return true;
+}
+
+uint32
+Animation::streamGetSize(void)
+{
+ uint32 size = 4 + 4 + 4 + 4 + 4;
+ size += this->interpInfo->streamGetSize(this);
+ return size;
+}
+
+//
+// AnimInterpolator
+//
+
+AnimInterpolator*
+AnimInterpolator::create(int32 numNodes, int32 maxFrameSize)
+{
+ AnimInterpolator *interp;
+ int32 sz;
+ int32 realsz = maxFrameSize;
+
+ // Add some space for pointers and padding, hopefully this will be
+ // enough. Don't change maxFrameSize not to mess up streaming.
+ if(sizeof(void*) > 4)
+ realsz += 16;
+ sz = sizeof(AnimInterpolator) + numNodes*realsz;
+ interp = (AnimInterpolator*)rwMalloc(sz, MEMDUR_EVENT | ID_ANIMANIMATION);
+ if(interp == nil){
+ RWERROR((ERR_ALLOC, sz));
+ return nil;
+ }
+ interp->currentAnim = nil;
+ interp->currentTime = 0.0f;
+ interp->nextFrame = nil;
+ interp->maxInterpKeyFrameSize = maxFrameSize;
+ interp->currentInterpKeyFrameSize = maxFrameSize;
+ interp->currentAnimKeyFrameSize = -1;
+ interp->numNodes = numNodes;;
+
+ return interp;
+}
+
+void
+AnimInterpolator::destroy(void)
+{
+ rwFree(this);
+}
+
+bool32
+AnimInterpolator::setCurrentAnim(Animation *anim)
+{
+ int32 i;
+ AnimInterpolatorInfo *interpInfo = anim->interpInfo;
+ this->currentAnim = anim;
+ this->currentTime = 0.0f;
+ int32 maxkf = this->maxInterpKeyFrameSize;
+ if(sizeof(void*) > 4) // see above in create()
+ maxkf += 16;
+ if(interpInfo->interpKeyFrameSize > maxkf){
+ RWERROR((ERR_GENERAL, "interpolation frame too big"));
+ return 0;
+ }
+ this->currentInterpKeyFrameSize = interpInfo->interpKeyFrameSize;
+ this->currentAnimKeyFrameSize = interpInfo->animKeyFrameSize;
+ this->applyCB = interpInfo->applyCB;
+ this->blendCB = interpInfo->blendCB;
+ this->interpCB = interpInfo->interpCB;
+ this->addCB = interpInfo->addCB;
+ for(i = 0; i < numNodes; i++){
+ InterpFrameHeader *intf;
+ KeyFrameHeader *kf1, *kf2;
+ intf = this->getInterpFrame(i);
+ kf1 = this->getAnimFrame(i);
+ kf2 = this->getAnimFrame(i+numNodes);
+ intf->keyFrame1 = kf1;
+ intf->keyFrame2 = kf2;
+ // TODO: perhaps just implement all interpolator infos?
+ if(this->interpCB)
+ this->interpCB(intf, kf1, kf2, 0.0f, anim->customData);
+ }
+ this->nextFrame = this->getAnimFrame(numNodes*2);
+ return 1;
+}
+
+void
+AnimInterpolator::addTime(float32 t)
+{
+ int32 i;
+ if(t <= 0.0f)
+ return;
+ this->currentTime += t;
+ // reset animation
+ if(this->currentTime > this->currentAnim->duration){
+ this->setCurrentAnim(this->currentAnim);
+ return;
+ }
+ KeyFrameHeader *last = this->getAnimFrame(this->currentAnim->numFrames);
+ KeyFrameHeader *next = (KeyFrameHeader*)this->nextFrame;
+ InterpFrameHeader *ifrm = nil;
+ while(next < last && next->prev->time <= this->currentTime){
+ // find next interpolation frame to expire
+ for(i = 0; i < this->numNodes; i++){
+ ifrm = this->getInterpFrame(i);
+ if(ifrm->keyFrame2 == next->prev)
+ break;
+ }
+ // advance interpolation frame
+ ifrm->keyFrame1 = ifrm->keyFrame2;
+ ifrm->keyFrame2 = next;
+ // ... and next frame
+ next = (KeyFrameHeader*)((uint8*)this->nextFrame +
+ currentAnimKeyFrameSize);
+ this->nextFrame = next;
+ }
+ for(i = 0; i < this->numNodes; i++){
+ ifrm = this->getInterpFrame(i);
+ this->interpCB(ifrm, ifrm->keyFrame1, ifrm->keyFrame2,
+ this->currentTime,
+ this->currentAnim->customData);
+ }
+}
+
+}
diff --git a/src/base.cpp b/src/base.cpp
new file mode 100644
index 0000000..3c3ca08
--- /dev/null
+++ b/src/base.cpp
@@ -0,0 +1,1123 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <ctype.h>
+
+#define PSEP_C '/'
+#define PSEP_S "/"
+#ifdef __unix__
+#include <sys/types.h>
+#include <dirent.h>
+#endif
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+namespace rw {
+
+#define PLUGIN_ID 0
+
+int32 version = 0x36003;
+int32 build = 0xFFFF;
+#ifdef RW_PS2
+ int32 platform = PLATFORM_PS2;
+#elif RW_WDGL
+ int32 platform = PLATFORM_WDGL;
+#elif RW_GL3
+ int32 platform = PLATFORM_GL3;
+#elif RW_D3D9
+ int32 platform = PLATFORM_D3D9;
+#else
+ int32 platform = PLATFORM_NULL;
+#endif
+bool32 streamAppendFrames = 0;
+char *debugFile = nil;
+
+static Matrix identMat = {
+ { 1.0f, 0.0f, 0.0f }, Matrix::IDENTITY|Matrix::TYPEORTHONORMAL,
+ { 0.0f, 1.0f, 0.0f }, 0,
+ { 0.0f, 0.0f, 1.0f }, 0,
+ { 0.0f, 0.0f, 0.0f }, 0
+};
+
+// lazy implementation
+int
+strcmp_ci(const char *s1, const char *s2)
+{
+ char c1, c2;
+ for(;;){
+ c1 = tolower(*s1);
+ c2 = tolower(*s2);
+ if(c1 != c2)
+ return c1 - c2;
+ if(c1 == '\0')
+ return 0;
+ s1++;
+ s2++;
+ }
+}
+
+int
+strncmp_ci(const char *s1, const char *s2, int n)
+{
+ char c1, c2;
+ while(n--){
+ c1 = tolower(*s1);
+ c2 = tolower(*s2);
+ if(c1 != c2)
+ return c1 - c2;
+ if(c1 == '\0')
+ return 0;
+ s1++;
+ s2++;
+ }
+ return 0;
+}
+
+Quat
+mult(const Quat &q, const Quat &p)
+{
+ return makeQuat(q.w*p.w - q.x*p.x - q.y*p.y - q.z*p.z,
+ q.w*p.x + q.x*p.w + q.y*p.z - q.z*p.y,
+ q.w*p.y + q.y*p.w + q.z*p.x - q.x*p.z,
+ q.w*p.z + q.z*p.w + q.x*p.y - q.y*p.x);
+}
+
+
+Quat*
+Quat::rotate(const V3d *axis, float32 angle, CombineOp op)
+{
+ Quat rot = rotation(angle, *axis);
+ switch(op){
+ case COMBINEREPLACE:
+ *this = rot;
+ break;
+ case COMBINEPRECONCAT:
+ *this = mult(*this, rot);
+ break;
+ case COMBINEPOSTCONCAT:
+ *this = mult(rot, *this);
+ break;
+ }
+ return this;
+}
+
+Quat
+lerp(const Quat &q, const Quat &p, float32 r)
+{
+ float32 c;
+ Quat q1 = q;
+ c = dot(q1, p);
+ if(c < 0.0f){
+ c = -c;
+ q1 = negate(q1);
+ }
+ return makeQuat(q1.w + r*(p.w - q1.w),
+ q1.x + r*(p.x - q1.x),
+ q1.y + r*(p.y - q1.y),
+ q1.z + r*(p.z - q1.z));
+};
+
+Quat
+slerp(const Quat &q, const Quat &p, float32 a)
+{
+ float32 c;
+ Quat q1 = q;
+ c = dot(q1, p);
+ if(c < 0.0f){
+ c = -c;
+ q1 = negate(q1);
+ }
+ float32 phi = acosf(c);
+ if(phi > 0.00001f){
+ float32 s = sinf(phi);
+ return add(scale(q1, sinf((1.0f-a)*phi)/s),
+ scale(p, sinf(a*phi)/s));
+ }
+ return q1;
+}
+
+//
+// V3d
+//
+
+V3d
+cross(const V3d &a, const V3d &b)
+{
+ return makeV3d(a.y*b.z - a.z*b.y,
+ a.z*b.x - a.x*b.z,
+ a.x*b.y - a.y*b.x);
+}
+
+void
+V3d::transformPoints(V3d *out, const V3d *in, int32 n, const Matrix *m)
+{
+ int32 i;
+ V3d tmp;
+ for(i = 0; i < n; i++){
+ tmp.x = in[i].x*m->right.x + in[i].y*m->up.x + in[i].z*m->at.x + m->pos.x;
+ tmp.y = in[i].x*m->right.y + in[i].y*m->up.y + in[i].z*m->at.y + m->pos.y;
+ tmp.z = in[i].x*m->right.z + in[i].y*m->up.z + in[i].z*m->at.z + m->pos.z;
+ out[i] = tmp;
+ }
+}
+
+void
+V3d::transformVectors(V3d *out, const V3d *in, int32 n, const Matrix *m)
+{
+ int32 i;
+ V3d tmp;
+ for(i = 0; i < n; i++){
+ tmp.x = in[i].x*m->right.x + in[i].y*m->up.x + in[i].z*m->at.x;
+ tmp.y = in[i].x*m->right.y + in[i].y*m->up.y + in[i].z*m->at.y;
+ tmp.z = in[i].x*m->right.z + in[i].y*m->up.z + in[i].z*m->at.z;
+ out[i] = tmp;
+ }
+}
+
+//
+// RawMatrix
+//
+
+void
+RawMatrix::mult(RawMatrix *dst, RawMatrix *src1, RawMatrix *src2)
+{
+ dst->right.x = src1->right.x*src2->right.x + src1->right.y*src2->up.x + src1->right.z*src2->at.x + src1->rightw*src2->pos.x;
+ dst->right.y = src1->right.x*src2->right.y + src1->right.y*src2->up.y + src1->right.z*src2->at.y + src1->rightw*src2->pos.y;
+ dst->right.z = src1->right.x*src2->right.z + src1->right.y*src2->up.z + src1->right.z*src2->at.z + src1->rightw*src2->pos.z;
+ dst->rightw = src1->right.x*src2->rightw + src1->right.y*src2->upw + src1->right.z*src2->atw + src1->rightw*src2->posw;
+ dst->up.x = src1->up.x*src2->right.x + src1->up.y*src2->up.x + src1->up.z*src2->at.x + src1->upw*src2->pos.x;
+ dst->up.y = src1->up.x*src2->right.y + src1->up.y*src2->up.y + src1->up.z*src2->at.y + src1->upw*src2->pos.y;
+ dst->up.z = src1->up.x*src2->right.z + src1->up.y*src2->up.z + src1->up.z*src2->at.z + src1->upw*src2->pos.z;
+ dst->upw = src1->up.x*src2->rightw + src1->up.y*src2->upw + src1->up.z*src2->atw + src1->upw*src2->posw;
+ dst->at.x = src1->at.x*src2->right.x + src1->at.y*src2->up.x + src1->at.z*src2->at.x + src1->atw*src2->pos.x;
+ dst->at.y = src1->at.x*src2->right.y + src1->at.y*src2->up.y + src1->at.z*src2->at.y + src1->atw*src2->pos.y;
+ dst->at.z = src1->at.x*src2->right.z + src1->at.y*src2->up.z + src1->at.z*src2->at.z + src1->atw*src2->pos.z;
+ dst->atw = src1->at.x*src2->rightw + src1->at.y*src2->upw + src1->at.z*src2->atw + src1->atw*src2->posw;
+ dst->pos.x = src1->pos.x*src2->right.x + src1->pos.y*src2->up.x + src1->pos.z*src2->at.x + src1->posw*src2->pos.x;
+ dst->pos.y = src1->pos.x*src2->right.y + src1->pos.y*src2->up.y + src1->pos.z*src2->at.y + src1->posw*src2->pos.y;
+ dst->pos.z = src1->pos.x*src2->right.z + src1->pos.y*src2->up.z + src1->pos.z*src2->at.z + src1->posw*src2->pos.z;
+ dst->posw = src1->pos.x*src2->rightw + src1->pos.y*src2->upw + src1->pos.z*src2->atw + src1->posw*src2->posw;
+}
+
+void
+RawMatrix::transpose(RawMatrix *dst, RawMatrix *src)
+{
+ dst->right.x = src->right.x;
+ dst->up.x = src->right.y;
+ dst->at.x = src->right.z;
+ dst->pos.x = src->rightw;
+ dst->right.y = src->up.x;
+ dst->up.y = src->up.y;
+ dst->at.y = src->up.z;
+ dst->pos.y = src->upw;
+ dst->right.z = src->at.x;
+ dst->up.z = src->at.y;
+ dst->at.z = src->at.z;
+ dst->pos.z = src->atw;
+ dst->rightw = src->pos.x;
+ dst->upw = src->pos.y;
+ dst->atw = src->pos.z;
+ dst->posw = src->posw;
+}
+
+void
+RawMatrix::setIdentity(RawMatrix *dst)
+{
+ static RawMatrix identity = {
+ { 1.0f, 0.0f, 0.0f }, 0.0f,
+ { 0.0f, 1.0f, 0.0f }, 0.0f,
+ { 0.0f, 0.0f, 1.0f }, 0.0f,
+ { 0.0f, 0.0f, 0.0f }, 1.0f
+ };
+ *dst = identity;
+}
+
+//
+// Matrix
+//
+
+static Matrix::Tolerance matrixDefaultTolerance = { 0.01f, 0.01f, 0.01f };
+
+Matrix*
+Matrix::create(void)
+{
+ Matrix *m = (Matrix*)rwMalloc(sizeof(Matrix), MEMDUR_EVENT | ID_MATRIX);
+ m->setIdentity();
+ return m;
+}
+
+void
+Matrix::destroy(void)
+{
+ rwFree(this);
+}
+
+void
+Matrix::setIdentity(void)
+{
+ *this = identMat;
+}
+
+void
+Matrix::optimize(Tolerance *tolerance)
+{
+ bool32 isnormal, isorthogonal, isidentity;
+ if(tolerance == nil)
+ tolerance = &matrixDefaultTolerance;
+ isnormal = normalError() <= tolerance->normal;
+ isorthogonal = orthogonalError() <= tolerance->orthogonal;
+ isidentity = isnormal && isorthogonal && identityError() <= tolerance->identity;
+ if(isnormal)
+ flags |= TYPENORMAL;
+ else
+ flags &= ~TYPENORMAL;
+ if(isorthogonal)
+ flags |= TYPEORTHOGONAL;
+ else
+ flags &= ~TYPEORTHOGONAL;
+ if(isidentity)
+ flags |= IDENTITY;
+ else
+ flags &= ~IDENTITY;
+}
+
+Matrix*
+Matrix::mult(Matrix *dst, const Matrix *src1, const Matrix *src2)
+{
+ if(src1->flags & IDENTITY)
+ *dst = *src2;
+ else if(src2->flags & IDENTITY)
+ *dst = *src1;
+ else{
+ mult_(dst, src1, src2);
+ dst->flags = src1->flags & src2->flags;
+ }
+ return dst;
+}
+
+Matrix*
+Matrix::invert(Matrix *dst, const Matrix *src)
+{
+ if(src->flags & IDENTITY)
+ *dst = *src;
+ else if((src->flags & TYPEMASK) == TYPEORTHONORMAL)
+ invertOrthonormal(dst, src);
+ else
+ return invertGeneral(dst, src);
+ return dst;
+}
+
+// transpose the 3x3 submatrix, pos is set to 0
+Matrix*
+Matrix::transpose(Matrix *dst, const Matrix *src)
+{
+ if(src->flags & IDENTITY)
+ *dst = *src;
+ dst->right.x = src->right.x;
+ dst->up.x = src->right.y;
+ dst->at.x = src->right.z;
+ dst->right.y = src->up.x;
+ dst->up.y = src->up.y;
+ dst->at.y = src->up.z;
+ dst->right.z = src->at.x;
+ dst->up.z = src->at.y;
+ dst->at.z = src->at.z;
+ dst->pos.x = 0.0;
+ dst->pos.y = 0.0;
+ dst->pos.z = 0.0;
+ return dst;
+}
+
+Matrix*
+Matrix::rotate(const V3d *axis, float32 angle, CombineOp op)
+{
+ Matrix tmp, rot;
+ makeRotation(&rot, axis, angle);
+ switch(op){
+ case COMBINEREPLACE:
+ *this = rot;
+ break;
+ case COMBINEPRECONCAT:
+ mult(&tmp, &rot, this);
+ *this = tmp;
+ break;
+ case COMBINEPOSTCONCAT:
+ mult(&tmp, this, &rot);
+ *this = tmp;
+ break;
+ }
+ return this;
+}
+
+Matrix*
+Matrix::rotate(const Quat &q, CombineOp op)
+{
+ Matrix tmp, rot;
+ makeRotation(&rot, q);
+ switch(op){
+ case COMBINEREPLACE:
+ *this = rot;
+ break;
+ case COMBINEPRECONCAT:
+ mult(&tmp, &rot, this);
+ *this = tmp;
+ break;
+ case COMBINEPOSTCONCAT:
+ mult(&tmp, this, &rot);
+ *this = tmp;
+ break;
+ }
+ return this;
+}
+Matrix*
+Matrix::translate(const V3d *translation, CombineOp op)
+{
+ Matrix tmp;
+ Matrix trans = identMat;
+ trans.pos = *translation;
+ trans.flags &= ~IDENTITY;
+ switch(op){
+ case COMBINEREPLACE:
+ *this = trans;
+ break;
+ case COMBINEPRECONCAT:
+ mult(&tmp, &trans, this);
+ *this = tmp;
+ break;
+ case COMBINEPOSTCONCAT:
+ mult(&tmp, this, &trans);
+ *this = tmp;
+ break;
+ }
+ return this;
+}
+
+Matrix*
+Matrix::scale(const V3d *scale, CombineOp op)
+{
+ Matrix tmp;
+ Matrix scl = identMat;
+ scl.right.x = scale->x;
+ scl.up.y = scale->y;
+ scl.at.z = scale->z;
+ scl.flags &= ~IDENTITY;
+ switch(op){
+ case COMBINEREPLACE:
+ *this = scl;
+ break;
+ case COMBINEPRECONCAT:
+ mult(&tmp, &scl, this);
+ *this = tmp;
+ break;
+ case COMBINEPOSTCONCAT:
+ mult(&tmp, this, &scl);
+ *this = tmp;
+ break;
+ }
+ return this;
+}
+
+Matrix*
+Matrix::transform(const Matrix *mat, CombineOp op)
+{
+ Matrix tmp;
+ switch(op){
+ case COMBINEREPLACE:
+ *this = *mat;
+ break;
+ case COMBINEPRECONCAT:
+ mult(&tmp, mat, this);
+ *this = tmp;
+ break;
+ case COMBINEPOSTCONCAT:
+ mult(&tmp, this, mat);
+ *this = tmp;
+ break;
+ }
+ return this;
+}
+
+Quat
+Matrix::getRotation(void)
+{
+ Quat q = { 0.0f, 0.0f, 0.0f, 1.0f };
+ float32 tr = right.x + up.y + at.z;
+ float s;
+ if(tr > 0.0f){
+ s = sqrtf(1.0f + tr) * 2.0f;
+ q.w = s / 4.0f;
+ q.x = (up.z - at.y) / s;
+ q.y = (at.x - right.z) / s;
+ q.z = (right.y - up.x) / s;
+ }else if(right.x > up.y && right.x > at.z){
+ s = sqrtf(1.0f + right.x - up.y - at.z) * 2.0f;
+ q.w = (up.z - at.y) / s;
+ q.x = s / 4.0f;
+ q.y = (up.x + right.y) / s;
+ q.z = (at.x + right.z) / s;
+ }else if(up.y > at.z){
+ s = sqrtf(1.0f + up.y - right.x - at.z) * 2.0f;
+ q.w = (at.x - right.z) / s;
+ q.x = (up.x + right.y) / s;
+ q.y = s / 4.0f;
+ q.z = (at.y + up.z) / s;
+ }else{
+ s = sqrtf(1.0f + at.z - right.x - up.y) * 2.0f;
+ q.w = (right.y - up.x) / s;
+ q.x = (at.x + right.z) / s;
+ q.y = (at.y + up.z) / s;
+ q.z = s / 4.0f;
+ }
+ return q;
+}
+
+void
+Matrix::lookAt(const V3d &dir, const V3d &up)
+{
+ // this->right is really pointing left
+ this->at = normalize(dir);
+ this->right = normalize(cross(up, this->at));
+ this->up = cross(this->at, this->right);
+ this->flags = TYPEORTHONORMAL;
+}
+
+/* For a row-major representation, this calculates src1 * src2.
+ * For column-major src2 * src1.
+ * i.e. a vector is first xformed by src1, then by src2
+ */
+void
+Matrix::mult_(Matrix *dst, const Matrix *src1, const Matrix *src2)
+{
+ dst->right.x = src1->right.x*src2->right.x + src1->right.y*src2->up.x + src1->right.z*src2->at.x;
+ dst->right.y = src1->right.x*src2->right.y + src1->right.y*src2->up.y + src1->right.z*src2->at.y;
+ dst->right.z = src1->right.x*src2->right.z + src1->right.y*src2->up.z + src1->right.z*src2->at.z;
+ dst->up.x = src1->up.x*src2->right.x + src1->up.y*src2->up.x + src1->up.z*src2->at.x;
+ dst->up.y = src1->up.x*src2->right.y + src1->up.y*src2->up.y + src1->up.z*src2->at.y;
+ dst->up.z = src1->up.x*src2->right.z + src1->up.y*src2->up.z + src1->up.z*src2->at.z;
+ dst->at.x = src1->at.x*src2->right.x + src1->at.y*src2->up.x + src1->at.z*src2->at.x;
+ dst->at.y = src1->at.x*src2->right.y + src1->at.y*src2->up.y + src1->at.z*src2->at.y;
+ dst->at.z = src1->at.x*src2->right.z + src1->at.y*src2->up.z + src1->at.z*src2->at.z;
+ dst->pos.x = src1->pos.x*src2->right.x + src1->pos.y*src2->up.x + src1->pos.z*src2->at.x + src2->pos.x;
+ dst->pos.y = src1->pos.x*src2->right.y + src1->pos.y*src2->up.y + src1->pos.z*src2->at.y + src2->pos.y;
+ dst->pos.z = src1->pos.x*src2->right.z + src1->pos.y*src2->up.z + src1->pos.z*src2->at.z + src2->pos.z;
+}
+
+void
+Matrix::invertOrthonormal(Matrix *dst, const Matrix *src)
+{
+ dst->right.x = src->right.x;
+ dst->right.y = src->up.x;
+ dst->right.z = src->at.x;
+ dst->up.x = src->right.y;
+ dst->up.y = src->up.y;
+ dst->up.z = src->at.y;
+ dst->at.x = src->right.z;
+ dst->at.y = src->up.z;
+ dst->at.z = src->at.z;
+ dst->pos.x = -(src->pos.x*src->right.x +
+ src->pos.y*src->right.y +
+ src->pos.z*src->right.z);
+ dst->pos.y = -(src->pos.x*src->up.x +
+ src->pos.y*src->up.y +
+ src->pos.z*src->up.z);
+ dst->pos.z = -(src->pos.x*src->at.x +
+ src->pos.y*src->at.y +
+ src->pos.z*src->at.z);
+ dst->flags = TYPEORTHONORMAL;
+}
+
+Matrix*
+Matrix::invertGeneral(Matrix *dst, const Matrix *src)
+{
+ float32 det, invdet;
+ // calculate a few cofactors
+ dst->right.x = src->up.y*src->at.z - src->up.z*src->at.y;
+ dst->right.y = src->at.y*src->right.z - src->at.z*src->right.y;
+ dst->right.z = src->right.y*src->up.z - src->right.z*src->up.y;
+ // get the determinant from that
+ det = src->up.x * dst->right.y + src->at.x * dst->right.z + dst->right.x * src->right.x;
+ invdet = 1.0;
+ if(det != 0.0f)
+ invdet = 1.0f/det;
+ dst->right.x *= invdet;
+ dst->right.y *= invdet;
+ dst->right.z *= invdet;
+ dst->up.x = invdet * (src->up.z*src->at.x - src->up.x*src->at.z);
+ dst->up.y = invdet * (src->at.z*src->right.x - src->at.x*src->right.z);
+ dst->up.z = invdet * (src->right.z*src->up.x - src->right.x*src->up.z);
+ dst->at.x = invdet * (src->up.x*src->at.y - src->up.y*src->at.x);
+ dst->at.y = invdet * (src->at.x*src->right.y - src->at.y*src->right.x);
+ dst->at.z = invdet * (src->right.x*src->up.y - src->right.y*src->up.x);
+ dst->pos.x = -(src->pos.x*dst->right.x + src->pos.y*dst->up.x + src->pos.z*dst->at.x);
+ dst->pos.y = -(src->pos.x*dst->right.y + src->pos.y*dst->up.y + src->pos.z*dst->at.y);
+ dst->pos.z = -(src->pos.x*dst->right.z + src->pos.y*dst->up.z + src->pos.z*dst->at.z);
+ dst->flags &= ~IDENTITY;
+ return dst;
+}
+
+void
+Matrix::makeRotation(Matrix *dst, const V3d *axis, float32 angle)
+{
+// V3d v = normalize(*axis);
+ float32 len = dot(*axis, *axis);
+ if(len != 0.0f) len = 1.0f/sqrtf(len);
+ V3d v = rw::scale(*axis, len);
+ angle = angle*(float)M_PI/180.0f;
+ float32 s = sinf(angle);
+ float32 c = cosf(angle);
+ float32 t = 1.0f - c;
+
+ dst->right.x = c + v.x*v.x*t;
+ dst->right.y = v.x*v.y*t + v.z*s;
+ dst->right.z = v.z*v.x*t - v.y*s;
+ dst->up.x = v.x*v.y*t - v.z*s;
+ dst->up.y = c + v.y*v.y*t;
+ dst->up.z = v.y*v.z*t + v.x*s;
+ dst->at.x = v.z*v.x*t + v.y*s;
+ dst->at.y = v.y*v.z*t - v.x*s;
+ dst->at.z = c + v.z*v.z*t;
+ dst->pos.x = 0.0;
+ dst->pos.y = 0.0;
+ dst->pos.z = 0.0;
+ dst->flags = TYPEORTHONORMAL;
+ dst->pad1 = dst->pad2 = dst->pad3 = 0;
+}
+
+/* q must be normalized */
+void
+Matrix::makeRotation(Matrix *dst, const Quat &q)
+{
+ float xx = q.x*q.x;
+ float yy = q.y*q.y;
+ float zz = q.z*q.z;
+ float yz = q.y*q.z;
+ float zx = q.z*q.x;
+ float xy = q.x*q.y;
+ float wx = q.w*q.x;
+ float wy = q.w*q.y;
+ float wz = q.w*q.z;
+
+ dst->right.x = 1.0f - 2.0f*(yy + zz);
+ dst->right.y = 2.0f*(xy + wz);
+ dst->right.z = 2.0f*(zx - wy);
+ dst->up.x = 2.0f*(xy - wz);
+ dst->up.y = 1.0f - 2.0f*(xx + zz);
+ dst->up.z = 2.0f*(yz + wx);
+ dst->at.x = 2.0f*(zx + wy);
+ dst->at.y = 2.0f*(yz - wx);
+ dst->at.z = 1.0f - 2.0f*(xx + yy);
+ dst->pos.x = 0.0;
+ dst->pos.y = 0.0;
+ dst->pos.z = 0.0;
+ dst->flags = TYPEORTHONORMAL;
+ dst->pad1 = dst->pad2 = dst->pad3 = 0;
+}
+
+float32
+Matrix::normalError(void)
+{
+ float32 x, y, z;
+ x = dot(right, right) - 1.0f;
+ y = dot(up, up) - 1.0f;
+ z = dot(at, at) - 1.0f;
+ return x*x + y*y + z*z;
+}
+
+float32
+Matrix::orthogonalError(void)
+{
+ float32 x, y, z;
+ x = dot(at, up);
+ y = dot(at, right);
+ z = dot(up, right);
+ return x*x + y*y + z*z;
+}
+
+float32
+Matrix::identityError(void)
+{
+ V3d r = { right.x-1.0f, right.y, right.z };
+ V3d u = { up.x, up.y-1.0f, up.z };
+ V3d a = { at.x, at.y, at.z-1.0f };
+ return dot(r,r) + dot(u,u) + dot(a,a) + dot(pos,pos);
+}
+
+void
+correctPathCase(char *filename)
+{
+#ifdef __unix__
+ DIR *direct;
+ struct dirent *dirent;
+
+ char *dir, *arg;
+ char copy[1024], sofar[1024] = ".";
+ strncpy(copy, filename, 1024);
+ arg = copy;
+ // hack for absolute paths
+ if(filename[0] == '/'){
+ sofar[0] = '/';
+ sofar[1] = '/';
+ sofar[2] = '\0';
+ arg++;
+ }
+ while((dir = strtok(arg, PSEP_S))){
+ arg = nil;
+ if(direct = opendir(sofar), dir == nil)
+ return;
+ while(dirent = readdir(direct), dirent != nil)
+ if(strncmp_ci(dirent->d_name, dir, 1024) == 0){
+ strncat(sofar, PSEP_S, 1024);
+ strncat(sofar, dirent->d_name, 1024);
+ break;
+ }
+ closedir(direct);
+ if(dirent == nil)
+ return;
+ }
+ strcpy(filename, sofar+2);
+#endif
+}
+
+void
+makePath(char *filename)
+{
+ size_t len = strlen(filename);
+ for(size_t i = 0; i < len; i++)
+ if(filename[i] == '/' || filename[i] == '\\')
+ filename[i] = PSEP_C;
+#ifdef __unix__
+ correctPathCase(filename);
+#endif
+}
+
+void
+memNative32_func(void *data, uint32 size)
+{
+ uint8 *bytes = (uint8*)data;
+ uint32 *words = (uint32*)data;
+ size >>= 2;
+ while(size--){
+ *words++ = (uint32)bytes[0] | (uint32)bytes[1]<<8 |
+ (uint32)bytes[2]<<16 | (uint32)bytes[3]<<24;
+ bytes += 4;
+ }
+}
+
+void
+memNative16_func(void *data, uint32 size)
+{
+ uint8 *bytes = (uint8*)data;
+ uint16 *words = (uint16*)data;
+ size >>= 1;
+ while(size--){
+ *words++ = (uint16)bytes[0] | (uint16)bytes[1]<<8;
+ bytes += 2;
+ }
+}
+
+void
+memLittle32_func(void *data, uint32 size)
+{
+ uint32 w;
+ uint8 *bytes = (uint8*)data;
+ uint32 *words = (uint32*)data;
+ size >>= 2;
+ while(size--){
+ w = *words++;
+ *bytes++ = w;
+ *bytes++ = w >> 8;
+ *bytes++ = w >> 16;
+ *bytes++ = w >> 24;
+ }
+}
+
+void
+memLittle16_func(void *data, uint32 size)
+{
+ uint16 w;
+ uint8 *bytes = (uint8*)data;
+ uint16 *words = (uint16*)data;
+ size >>= 1;
+ while(size--){
+ w = *words++;
+ *bytes++ = (uint8)w;
+ *bytes++ = w >> 8;
+ }
+}
+
+uint32
+Stream::write32(const void *data, uint32 length)
+{
+#ifdef BIGENDIAN
+ uint8 *src = (uint8*)data;
+ uint32 buf[256];
+ int32 n, len;
+ for(len = length >>= 2; len > 0; len -= 256){
+ n = len < 256 ? len : 256;
+ memcpy(buf, src, n*4);
+ memLittle32(buf, n*4);
+ write8(buf, n*4);
+ src += n*4;
+ }
+ return length;
+#else
+ return write8(data, length);
+#endif
+}
+
+uint32
+Stream::write16(const void *data, uint32 length)
+{
+#ifdef BIGENDIAN
+ uint8 *src = (uint8*)data;
+ uint16 buf[256];
+ int32 n, len;
+ for(len = length >>= 1; len > 0; len -= 256){
+ n = len < 256 ? len : 256;
+ memcpy(buf, src, n*2);
+ memLittle16(buf, n*2);
+ write8(buf, n*2);
+ src += n*2;
+ }
+ return length;
+#else
+ return write8(data, length);
+#endif
+}
+
+uint32
+Stream::read32(void *data, uint32 length)
+{
+ uint32 ret;
+ ret = read8(data, length);
+ memNative32(data, length);
+ return ret;
+}
+
+uint32
+Stream::read16(void *data, uint32 length)
+{
+ uint32 ret;
+ ret = read8(data, length);
+ memNative16(data, length);
+ return ret;
+}
+
+int32
+Stream::writeI8(int8 val)
+{
+ return write8(&val, sizeof(int8));
+}
+
+int32
+Stream::writeU8(uint8 val)
+{
+ return write8(&val, sizeof(uint8));
+}
+
+int32
+Stream::writeI16(int16 val)
+{
+ return write16(&val, sizeof(int16));
+}
+
+int32
+Stream::writeU16(uint16 val)
+{
+ return write16(&val, sizeof(uint16));
+}
+
+int32
+Stream::writeI32(int32 val)
+{
+ return write32(&val, sizeof(int32));
+}
+
+int32
+Stream::writeU32(uint32 val)
+{
+ return write32(&val, sizeof(uint32));
+}
+
+int32
+Stream::writeF32(float32 val)
+{
+ return write32(&val, sizeof(float32));
+}
+
+int8
+Stream::readI8(void)
+{
+ int8 tmp;
+ read8(&tmp, sizeof(int8));
+ return tmp;
+}
+
+uint8
+Stream::readU8(void)
+{
+ uint8 tmp;
+ read8(&tmp, sizeof(uint8));
+ return tmp;
+}
+
+int16
+Stream::readI16(void)
+{
+ int16 tmp;
+ read16(&tmp, sizeof(int16));
+ return tmp;
+}
+
+uint16
+Stream::readU16(void)
+{
+ uint16 tmp;
+ read16(&tmp, sizeof(uint16));
+ return tmp;
+}
+
+int32
+Stream::readI32(void)
+{
+ int32 tmp;
+ read32(&tmp, sizeof(int32));
+ return tmp;
+}
+
+uint32
+Stream::readU32(void)
+{
+ uint32 tmp;
+ read32(&tmp, sizeof(uint32));
+ return tmp;
+}
+
+float32
+Stream::readF32(void)
+{
+ float32 tmp;
+ read32(&tmp, sizeof(float32));
+ return tmp;
+}
+
+
+
+void
+StreamMemory::close(void)
+{
+}
+
+uint32
+StreamMemory::write8(const void *data, uint32 len)
+{
+ if(this->eof())
+ return 0;
+ uint32 l = len;
+ if(this->position+l > this->length){
+ if(this->position+l > this->capacity)
+ l = this->capacity-this->position;
+ this->length = this->position+l;
+ }
+ memcpy(&this->data[this->position], data, l);
+ this->position += l;
+ if(len != l)
+ this->position = S_EOF;
+ return l;
+}
+
+uint32
+StreamMemory::read8(void *data, uint32 len)
+{
+ if(this->eof())
+ return 0;
+ uint32 l = len;
+ if(this->position+l > this->length)
+ l = this->length-this->position;
+ memcpy(data, &this->data[this->position], l);
+ this->position += l;
+ if(len != l)
+ this->position = S_EOF;
+ return l;
+}
+
+void
+StreamMemory::seek(int32 offset, int32 whence)
+{
+ if(whence == 0)
+ this->position = offset;
+ else if(whence == 1)
+ this->position += offset;
+ else
+ this->position = this->length-offset;
+ if(this->position > this->length){
+ // TODO: ideally this would depend on the mode
+ if(this->position > this->capacity)
+ this->position = S_EOF;
+ else
+ this->length = this->position;
+ }
+}
+
+uint32
+StreamMemory::tell(void)
+{
+ return this->position;
+}
+
+bool
+StreamMemory::eof(void)
+{
+ return this->position == S_EOF;
+}
+
+StreamMemory*
+StreamMemory::open(uint8 *data, uint32 length, uint32 capacity)
+{
+ this->data = data;
+ this->capacity = capacity;
+ this->length = length;
+ if(this->capacity < this->length)
+ this->capacity = this->length;
+ this->position = 0;
+ return this;
+}
+
+uint32
+StreamMemory::getLength(void)
+{
+ return this->length;
+}
+
+
+StreamFile*
+StreamFile::open(const char *path, const char *mode)
+{
+ assert(this->file == nil);
+ this->file = engine->filefuncs.rwfopen(path, mode);
+ if(this->file == nil){
+ RWERROR((ERR_FILE, path));
+ return nil;
+ }
+ return this;
+}
+
+void
+StreamFile::close(void)
+{
+ assert(this->file);
+ engine->filefuncs.rwfclose(this->file);
+ this->file = nil;
+}
+
+uint32
+StreamFile::write8(const void *data, uint32 length)
+{
+ return (uint32)engine->filefuncs.rwfwrite(data, 1, length, this->file);
+}
+
+uint32
+StreamFile::read8(void *data, uint32 length)
+{
+ return (uint32)engine->filefuncs.rwfread(data, 1, length, this->file);
+}
+
+void
+StreamFile::seek(int32 offset, int32 whence)
+{
+ engine->filefuncs.rwfseek(this->file, offset, whence);
+}
+
+uint32
+StreamFile::tell(void)
+{
+ return engine->filefuncs.rwftell(this->file);
+}
+
+bool
+StreamFile::eof(void)
+{
+ return engine->filefuncs.rwfeof(this->file) != 0;
+}
+
+bool
+writeChunkHeader(Stream *s, int32 type, int32 size)
+{
+ struct {
+ int32 type, size;
+ uint32 id;
+ } buf = { type, size, libraryIDPack(version, build) };
+ s->write32(&buf, 12);
+ return true;
+}
+
+bool
+readChunkHeaderInfo(Stream *s, ChunkHeaderInfo *header)
+{
+ struct {
+ int32 type, size;
+ uint32 id;
+ } buf;
+ s->read32(&buf, 12);
+ if(s->eof())
+ return false;
+ assert(header != nil);
+ header->type = buf.type;
+ header->length = buf.size;
+ header->version = libraryIDUnpackVersion(buf.id);
+ header->build = libraryIDUnpackBuild(buf.id);
+ return true;
+}
+
+bool
+findChunk(Stream *s, uint32 type, uint32 *length, uint32 *version)
+{
+ ChunkHeaderInfo header;
+ while(readChunkHeaderInfo(s, &header)){
+ if(header.type == ID_NAOBJECT)
+ return false;
+ if(header.type == type){
+ if(length)
+ *length = header.length;
+ if(version)
+ *version = header.version;
+ return true;
+ }
+ s->seek(header.length);
+ }
+ return false;
+}
+
+int32
+findPointer(void *p, void **list, int32 num)
+{
+ int i;
+ for(i = 0; i < num; i++)
+ if(list[i] == p)
+ return i;
+ return -1;
+}
+
+uint8*
+getFileContents(const char *name, uint32 *len)
+{
+ void *cf = engine->filefuncs.rwfopen(name, "rb");
+ if(cf == nil)
+ return nil;
+ engine->filefuncs.rwfseek(cf, 0, SEEK_END);
+ *len = engine->filefuncs.rwftell(cf);
+ engine->filefuncs.rwfseek(cf, 0, SEEK_SET);
+ uint8 *data = rwNewT(uint8, *len, MEMDUR_EVENT);
+ engine->filefuncs.rwfread(data, 1, *len, cf);
+ engine->filefuncs.rwfclose(cf);
+ return data;
+}
+
+}
diff --git a/src/base.err b/src/base.err
new file mode 100644
index 0000000..2d413e8
--- /dev/null
+++ b/src/base.err
@@ -0,0 +1,24 @@
+ECODE(ERR_GENERAL,
+ "Error: %s"),
+ECODE(ERR_ALLOC,
+ "Couldn't allocate 0x%X bytes"),
+ECODE(ERR_FILE,
+ "Couldn't open file %s"),
+ECODE(ERR_CHUNK,
+ "Couldn't find chunk %s"),
+ECODE(ERR_VERSION,
+ "Unsupported version %X"),
+ECODE(ERR_PLATFORM,
+ "Unsupported platform %d"),
+ECODE(ERR_ENGINEINIT,
+ "Engine could not be initialized"),
+ECODE(ERR_ENGINEOPEN,
+ "Engine could not be opened"),
+ECODE(ERR_ENGINESTART,
+ "Engine could not be started"),
+ECODE(ERR_INVRASTER,
+ "Invalid raster format"),
+ECODE(ERR_NOTEXTURE,
+ "Could not create texture"),
+ECODE(ERR_FORMAT_UNSUPPORTED,
+ "Unsupported raster format")
diff --git a/src/bmp.cpp b/src/bmp.cpp
new file mode 100644
index 0000000..f69e58d
--- /dev/null
+++ b/src/bmp.cpp
@@ -0,0 +1,284 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+
+// NB: this has padding and cannot be streamed directly!
+struct BMPheader
+{
+ uint16 magic;
+ uint32 size;
+ uint16 reserved[2];
+ uint32 offset;
+
+ bool32 read(Stream *stream);
+ void write(Stream *stream);
+};
+
+// This one is aligned and can be streamed directly
+struct DIBheader
+{
+ uint32 headerSize;
+ int32 width;
+ int32 height;
+ int16 numPlanes;
+ int16 depth;
+ uint32 compression;
+ uint32 imgSize;
+ int32 hres;
+ int32 vres;
+ int32 paletteLen;
+ int32 numImportant;
+ // end of 40 btyes
+
+ uint32 rmask, gmask, bmask, amask;
+};
+
+bool32
+BMPheader::read(Stream *stream)
+{
+ magic = stream->readU16();
+ size = stream->readU32();
+ reserved[0] = stream->readU16();
+ reserved[1] = stream->readU16();
+ offset = stream->readU32();
+ return magic == 0x4D42;
+}
+
+void
+BMPheader::write(Stream *stream)
+{
+
+ stream->writeU16(magic);
+ stream->writeU32(size);
+ stream->writeU16(reserved[0]);
+ stream->writeU16(reserved[1]);
+ stream->writeU32(offset);
+}
+
+Image*
+readBMP(const char *filename)
+{
+ ASSERTLITTLE;
+ Image *image;
+ uint32 length;
+ uint8 *data;
+ StreamMemory file;
+ int i, x, y;
+
+ bool32 noalpha;
+ int pad;
+
+ data = getFileContents(filename, &length);
+ if(data == nil)
+ return nil;
+ file.open(data, length);
+
+ /* read headers */
+ BMPheader bmp;
+ DIBheader dib;
+ if(!bmp.read(&file))
+ goto lose;
+ file.read8(&dib, sizeof(dib));
+ file.seek(dib.headerSize-sizeof(dib)); // skip the part of the header we're ignoring
+ if(dib.headerSize <= 16){
+ dib.compression = 0;
+ dib.paletteLen = 0;
+ }
+
+ noalpha = true;
+
+ // Recognize 32 bit formats
+ if(dib.compression == 3){
+ if(dib.rmask != 0xFF0000 ||
+ dib.gmask != 0x00FF00 ||
+ dib.bmask != 0x0000FF)
+ goto lose;
+ dib.compression = 0;
+ if(dib.headerSize > 52 && dib.amask == 0xFF000000)
+ noalpha = false;
+ }
+
+ if(dib.compression != 0)
+ goto lose;
+
+ image = Image::create(dib.width, dib.height, dib.depth);
+ image->allocate();
+
+
+ if(image->palette){
+ int len = 1<<dib.depth;
+ uint8 (*color)[4] = (uint8 (*)[4])image->palette;
+ for(i = 0; i < len; i++){
+ color[i][2] = file.readU8(); // blue
+ color[i][1] = file.readU8(); // green
+ color[i][0] = file.readU8(); // red
+ color[i][3] = file.readU8(); // alpha
+ if(noalpha)
+ color[i][3] = 0xFF;
+ }
+ }
+
+ file.seek(bmp.offset, 0);
+
+ pad = image->width*image->bpp % 4;
+
+ uint8 *px, *line;
+ line = image->pixels + (image->height-1)*image->stride;
+ for(y = 0; y < image->height; y++){
+ px = line;
+ for(x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 4:
+ i = file.readU8();;
+ px[x+0] = (i>>4)&0xF;
+ px[x+1] = i&0xF;
+ x++;
+ break;
+
+ case 8:
+ px[x] = file.readU8();
+ break;
+
+ case 16:
+ // TODO: what format is this even? and what does Image expect?
+ px[x*2 + 0] = file.readU8();
+ px[x*2 + 1] = file.readU8();
+ break;
+
+ case 24:
+ px[x*3 + 2] = file.readU8();
+ px[x*3 + 1] = file.readU8();
+ px[x*3 + 0] = file.readU8();
+ break;
+
+ case 32:
+ px[x*4 + 2] = file.readU8();
+ px[x*4 + 1] = file.readU8();
+ px[x*4 + 0] = file.readU8();
+ px[x*4 + 3] = file.readU8();
+ if(noalpha)
+ px[x*4 + 3] = 0xFF;
+ break;
+
+ default:
+ goto lose;
+ }
+ }
+
+ line -= image->stride;
+ file.seek(pad);
+ }
+
+
+ file.close();
+ rwFree(data);
+ return image;
+
+lose:
+ file.close();
+ rwFree(data);
+ return nil;
+}
+
+/* can't write alpha */
+void
+writeBMP(Image *image, const char *filename)
+{
+ ASSERTLITTLE;
+ uint8 *p;
+ StreamFile file;
+ if(!file.open(filename, "wb")){
+ RWERROR((ERR_FILE, filename));
+ return;
+ }
+
+ int32 pallen = image->depth > 8 ? 0 :
+ image->depth == 4 ? 16 : 256;
+ int32 stride = image->width*image->depth/8;
+ int32 depth = image->depth == 32 ? 24 : image->depth;
+ stride = stride+3 & ~3;
+
+ // File headers
+ BMPheader bmp;
+ bmp.magic = 0x4D42; // BM
+ bmp.size = 0x36 + 4*pallen + image->height*stride;
+ bmp.reserved[0] = 0;
+ bmp.reserved[1] = 0;
+ bmp.offset = 0x36 + 4*pallen;
+ bmp.write(&file);
+
+ DIBheader dib;
+ dib.headerSize = 0x28;
+ dib.width = image->width;
+ dib.height = image->height;
+ dib.numPlanes = 1;
+ dib.depth = depth;
+ dib.compression = 0;
+ dib.imgSize = 0;
+ dib.hres = 2835; // 72dpi
+ dib.vres = 2835; // 72dpi
+ dib.paletteLen = 0;
+ dib.numImportant = 0;
+ file.write8(&dib, dib.headerSize);
+
+ for(int i = 0; i < pallen; i++){
+ file.writeU8(image->palette[i*4+2]);
+ file.writeU8(image->palette[i*4+1]);
+ file.writeU8(image->palette[i*4+0]);
+ file.writeU8(0xFF);
+ }
+
+ uint8 *line = image->pixels + (image->height-1)*image->stride;
+ int32 n;
+ for(int y = 0; y < image->height; y++){
+ p = line;
+ for(int x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 4:
+ file.writeU8((p[0]&0xF)<<4 | (p[1]&0xF));
+ p += 2;
+ x++;
+ break;
+ case 8:
+ file.writeU8(*p++);
+ break;
+ case 16:
+ file.writeU8(p[0]);
+ file.writeU8(p[1]);
+ p += 2;
+ break;
+ case 24:
+ file.writeU8(p[2]);
+ file.writeU8(p[1]);
+ file.writeU8(p[0]);
+ p += 3;
+ break;
+ case 32:
+ file.writeU8(p[2]);
+ file.writeU8(p[1]);
+ file.writeU8(p[0]);
+ p += 4;
+ }
+ }
+ n = (p-line) % 4;
+ while(n--)
+ file.writeU8(0);
+ line -= image->stride;
+ }
+
+ file.close();
+}
+
+}
diff --git a/src/camera.cpp b/src/camera.cpp
new file mode 100644
index 0000000..4a87ed3
--- /dev/null
+++ b/src/camera.cpp
@@ -0,0 +1,524 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID ID_CAMERA
+
+namespace rw {
+
+int32 Camera::numAllocated;
+
+PluginList Camera::s_plglist(sizeof(Camera));
+
+void
+defaultBeginUpdateCB(Camera *cam)
+{
+ engine->currentCamera = cam;
+ Frame::syncDirty();
+ engine->device.beginUpdate(cam);
+}
+
+void
+defaultEndUpdateCB(Camera *cam)
+{
+ engine->device.endUpdate(cam);
+}
+
+static void
+buildPlanes(Camera *cam)
+{
+ V3d *c = cam->frustumCorners;
+ FrustumPlane *p = cam->frustumPlanes;
+ V3d v51 = sub(c[1], c[5]);
+ V3d v73 = sub(c[3], c[7]);
+
+ /* Far plane */
+ p[0].plane.normal = cam->getFrame()->getLTM()->at;
+ p[0].plane.distance = dot(p[0].plane.normal, c[4]);
+ p[0].closestX = p[0].plane.normal.x < 0.0f ? 0 : 1;
+ p[0].closestY = p[0].plane.normal.y < 0.0f ? 0 : 1;
+ p[0].closestZ = p[0].plane.normal.z < 0.0f ? 0 : 1;
+
+ /* Near plane */
+ p[1].plane.normal = neg(p[0].plane.normal);
+ p[1].plane.distance = dot(p[1].plane.normal, c[0]);
+ p[1].closestX = p[1].plane.normal.x < 0.0f ? 0 : 1;
+ p[1].closestY = p[1].plane.normal.y < 0.0f ? 0 : 1;
+ p[1].closestZ = p[1].plane.normal.z < 0.0f ? 0 : 1;
+
+ /* Right plane */
+ p[2].plane.normal = normalize(cross(v51,
+ sub(c[6], c[5])));
+ p[2].plane.distance = dot(p[2].plane.normal, c[1]);
+ p[2].closestX = p[2].plane.normal.x < 0.0f ? 0 : 1;
+ p[2].closestY = p[2].plane.normal.y < 0.0f ? 0 : 1;
+ p[2].closestZ = p[2].plane.normal.z < 0.0f ? 0 : 1;
+
+ /* Top plane */
+ p[3].plane.normal = normalize(cross(sub(c[4], c[5]),
+ v51));
+ p[3].plane.distance = dot(p[3].plane.normal, c[1]);
+ p[3].closestX = p[3].plane.normal.x < 0.0f ? 0 : 1;
+ p[3].closestY = p[3].plane.normal.y < 0.0f ? 0 : 1;
+ p[3].closestZ = p[3].plane.normal.z < 0.0f ? 0 : 1;
+
+ /* Left plane */
+ p[4].plane.normal = normalize(cross(v73,
+ sub(c[4], c[7])));
+ p[4].plane.distance = dot(p[4].plane.normal, c[3]);
+ p[4].closestX = p[4].plane.normal.x < 0.0f ? 0 : 1;
+ p[4].closestY = p[4].plane.normal.y < 0.0f ? 0 : 1;
+ p[4].closestZ = p[4].plane.normal.z < 0.0f ? 0 : 1;
+
+ /* Bottom plane */
+ p[5].plane.normal = normalize(cross(sub(c[6], c[7]),
+ v73));
+ p[5].plane.distance = dot(p[5].plane.normal, c[3]);
+ p[5].closestX = p[5].plane.normal.x < 0.0f ? 0 : 1;
+ p[5].closestY = p[5].plane.normal.y < 0.0f ? 0 : 1;
+ p[5].closestZ = p[5].plane.normal.z < 0.0f ? 0 : 1;
+}
+
+static void
+buildClipPersp(Camera *cam)
+{
+ Matrix *ltm = cam->getFrame()->getLTM();
+
+ /* First we calculate the 4 points on the view window. */
+ V3d up = scale(ltm->up, cam->viewWindow.y);
+ V3d left = scale(ltm->right, cam->viewWindow.x);
+ V3d *c = cam->frustumCorners;
+ c[0] = add(add(ltm->at, up), left); // top left
+ c[1] = sub(add(ltm->at, up), left); // top right
+ c[2] = sub(sub(ltm->at, up), left); // bottom right
+ c[3] = add(sub(ltm->at, up), left); // bottom left
+
+ /* Now Calculate near and far corners. */
+ V3d off = sub(scale(ltm->up, cam->viewOffset.y),
+ scale(ltm->right, cam->viewOffset.x));
+ for(int32 i = 0; i < 4; i++){
+ V3d corner = sub(cam->frustumCorners[i], off);
+ V3d pos = add(ltm->pos, off);
+ c[i] = add(scale(corner, cam->nearPlane), pos);
+ c[i+4] = add(scale(corner, cam->farPlane), pos);
+ }
+
+ buildPlanes(cam);
+}
+
+static void
+buildClipParallel(Camera *cam)
+{
+ Matrix *ltm = cam->getFrame()->getLTM();
+ float32 nearoffx = -(1.0f - cam->nearPlane)*cam->viewOffset.x;
+ float32 nearoffy = (1.0f - cam->nearPlane)*cam->viewOffset.y;
+ float32 faroffx = -(1.0f - cam->farPlane)*cam->viewOffset.x;
+ float32 faroffy = (1.0f - cam->farPlane)*cam->viewOffset.y;
+
+ V3d *c = cam->frustumCorners;
+ c[0].x = nearoffx + cam->viewWindow.x;
+ c[0].y = nearoffy + cam->viewWindow.y;
+ c[0].z = cam->nearPlane;
+
+ c[1].x = nearoffx - cam->viewWindow.x;
+ c[1].y = nearoffy + cam->viewWindow.y;
+ c[1].z = cam->nearPlane;
+
+ c[2].x = nearoffx - cam->viewWindow.x;
+ c[2].y = nearoffy - cam->viewWindow.y;
+ c[2].z = cam->nearPlane;
+
+ c[3].x = nearoffx + cam->viewWindow.x;
+ c[3].y = nearoffy - cam->viewWindow.y;
+ c[3].z = cam->nearPlane;
+
+ c[4].x = faroffx + cam->viewWindow.x;
+ c[4].y = faroffy + cam->viewWindow.y;
+ c[4].z = cam->farPlane;
+
+ c[5].x = faroffx - cam->viewWindow.x;
+ c[5].y = faroffy + cam->viewWindow.y;
+ c[5].z = cam->farPlane;
+
+ c[6].x = faroffx - cam->viewWindow.x;
+ c[6].y = faroffy - cam->viewWindow.y;
+ c[6].z = cam->farPlane;
+
+ c[7].x = faroffx + cam->viewWindow.x;
+ c[7].y = faroffy - cam->viewWindow.y;
+ c[7].z = cam->farPlane;
+
+ V3d::transformPoints(c, c, 8, ltm);
+
+ buildPlanes(cam);
+}
+
+static void
+cameraSync(ObjectWithFrame *obj)
+{
+ /*
+ * RW projection matrix looks like this:
+ * (cf. Camera View Matrix white paper)
+ * w = viewWindow width
+ * h = viewWindow height
+ * o = view offset
+ *
+ * perspective:
+ * 1/2w 0 ox/2w + 1/2 -ox/2w
+ * 0 -1/2h -oy/2h + 1/2 oy/2h
+ * 0 0 1 0
+ * 0 0 1 0
+ *
+ * parallel:
+ * 1/2w 0 ox/2w -ox/2w + 1/2
+ * 0 -1/2h -oy/2h oy/2h + 1/2
+ * 0 0 1 0
+ * 0 0 0 1
+ *
+ * The view matrix transforms from world to clip space, it is however
+ * not used for OpenGL or D3D since transformation to camera space
+ * and to clip space are handled by separate matrices there.
+ * On these platforms the two matrices are built in the platform's
+ * beginUpdate function.
+ * On the PS2 the z- and w-rows are the same and the
+ * 1/2 translation/shear is removed again on the VU1 by
+ * subtracting the w-row/2 from the x- and y-rows.
+ *
+ * perspective:
+ * 1/2w 0 ox/2w -ox/2w
+ * 0 -1/2h -oy/2h oy/2h
+ * 0 0 1 0
+ * 0 0 1 0
+ *
+ * parallel:
+ * 1/2w 0 ox/2w -ox/2w
+ * 0 -1/2h -oy/2h oy/2h
+ * 0 0 1 0
+ * 0 0 0 1
+ *
+ * RW builds this matrix directly without using explicit
+ * inversion and matrix multiplication.
+ */
+
+ Camera *cam = (Camera*)obj;
+ Matrix inv, proj;
+ Matrix::invertOrthonormal(&inv, cam->getFrame()->getLTM());
+
+ inv.right.x = -inv.right.x;
+ inv.up.x = -inv.up.x;
+ inv.at.x = -inv.at.x;
+ inv.pos.x = -inv.pos.x;
+
+ float32 xscl = 1.0f/(2.0f*cam->viewWindow.x);
+ float32 yscl = 1.0f/(2.0f*cam->viewWindow.y);
+
+ proj.flags = 0;
+ proj.right.x = xscl;
+ proj.right.y = 0.0f;
+ proj.right.z = 0.0f;
+
+ proj.up.x = 0.0f;
+ proj.up.y = -yscl;
+ proj.up.z = 0.0f;
+
+ if(cam->projection == Camera::PERSPECTIVE){
+ proj.pos.x = -cam->viewOffset.x*xscl;
+ proj.pos.y = cam->viewOffset.y*yscl;
+ proj.pos.z = 0.0f;
+
+ proj.at.x = -proj.pos.x + 0.5f;
+ proj.at.y = -proj.pos.y + 0.5f;
+ proj.at.z = 1.0f;
+ proj.optimize();
+ Matrix::mult(&cam->viewMatrix, &inv, &proj);
+ buildClipPersp(cam);
+ }else{
+ proj.at.x = cam->viewOffset.x*xscl;
+ proj.at.y = -cam->viewOffset.y*yscl;
+ proj.at.z = 1.0f;
+
+ proj.pos.x = -proj.at.x + 0.5f;
+ proj.pos.y = -proj.at.y + 0.5f;
+ proj.pos.z = 0.0f;
+ proj.optimize();
+ Matrix::mult(&cam->viewMatrix, &inv, &proj);
+ buildClipParallel(cam);
+ }
+ cam->frustumBoundBox.calculate(cam->frustumCorners, 8);
+}
+
+void
+worldBeginUpdateCB(Camera *cam)
+{
+ engine->currentWorld = cam->world;
+ cam->originalBeginUpdate(cam);
+}
+
+void
+worldEndUpdateCB(Camera *cam)
+{
+ cam->originalEndUpdate(cam);
+}
+
+static void
+worldCameraSync(ObjectWithFrame *obj)
+{
+ Camera *camera = (Camera*)obj;
+ camera->originalSync(obj);
+}
+
+Camera*
+Camera::create(void)
+{
+ Camera *cam = (Camera*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_CAMERA);
+ if(cam == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ cam->object.object.init(Camera::ID, 0);
+ cam->object.syncCB = cameraSync;
+ cam->beginUpdateCB = defaultBeginUpdateCB;
+ cam->endUpdateCB = defaultEndUpdateCB;
+ cam->viewWindow.set(1.0f, 1.0f);
+ cam->viewOffset.set(0.0f, 0.0f);
+ cam->nearPlane = 0.05f;
+ cam->farPlane = 10.0f;
+ cam->fogPlane = 5.0f;
+ cam->projection = Camera::PERSPECTIVE;
+
+ cam->frameBuffer = nil;
+ cam->zBuffer = nil;
+
+ // clump extension
+ cam->clump = nil;
+ cam->inClump.init();
+
+ // world extension
+ cam->world = nil;
+ cam->originalSync = cam->object.syncCB;
+ cam->originalBeginUpdate = cam->beginUpdateCB;
+ cam->originalEndUpdate = cam->endUpdateCB;
+ cam->object.syncCB = worldCameraSync;
+ cam->beginUpdateCB = worldBeginUpdateCB;
+ cam->endUpdateCB = worldEndUpdateCB;
+
+ s_plglist.construct(cam);
+ return cam;
+}
+
+Camera*
+Camera::clone(void)
+{
+ Camera *cam = Camera::create();
+ if(cam == nil)
+ return nil;
+ cam->object.object.copy(&this->object.object);
+ cam->setFrame(this->getFrame());
+ cam->viewWindow = this->viewWindow;
+ cam->viewOffset = this->viewOffset;
+ cam->nearPlane = this->nearPlane;
+ cam->farPlane = this->farPlane;
+ cam->fogPlane = this->fogPlane;
+ cam->projection = this->projection;
+
+ cam->frameBuffer = this->frameBuffer;
+ cam->zBuffer = this->zBuffer;
+
+ if(this->world)
+ this->world->addCamera(cam);
+
+ s_plglist.copy(cam, this);
+ return cam;
+}
+
+void
+Camera::destroy(void)
+{
+ s_plglist.destruct(this);
+ assert(this->clump == nil);
+ assert(this->world == nil);
+ this->setFrame(nil);
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+Camera::clear(RGBA *col, uint32 mode)
+{
+ engine->device.clearCamera(this, col, mode);
+}
+
+void
+Camera::showRaster(uint32 flags)
+{
+ this->frameBuffer->show(flags);
+}
+
+void
+calczShiftScale(Camera *cam)
+{
+ float32 n = cam->nearPlane;
+ float32 f = cam->farPlane;
+ float32 N = engine->device.zNear;
+ float32 F = engine->device.zFar;
+ // RW does this
+ N += (F - N)/10000.0f;
+ F -= (F - N)/10000.0f;
+ if(cam->projection == Camera::PERSPECTIVE){
+ cam->zScale = (N - F)*n*f/(f - n);
+ cam->zShift = (F*f - N*n)/(f - n);
+ }else{
+ cam->zScale = (F - N)/(f -n);
+ cam->zShift = (N*f - F*n)/(f - n);
+ }
+}
+
+void
+Camera::setNearPlane(float32 near)
+{
+ this->nearPlane = near;
+ calczShiftScale(this);
+ if(this->getFrame())
+ this->getFrame()->updateObjects();
+}
+
+void
+Camera::setFarPlane(float32 far)
+{
+ this->farPlane = far;
+ calczShiftScale(this);
+ if(this->getFrame())
+ this->getFrame()->updateObjects();
+}
+
+void
+Camera::setViewWindow(const V2d *window)
+{
+ this->viewWindow = *window;
+ if(this->getFrame())
+ this->getFrame()->updateObjects();
+}
+
+void
+Camera::setViewOffset(const V2d *offset)
+{
+ this->viewOffset = *offset;
+ if(this->getFrame())
+ this->getFrame()->updateObjects();
+}
+
+void
+Camera::setProjection(int32 proj)
+{
+ this->projection = proj;
+ if(this->getFrame())
+ this->getFrame()->updateObjects();
+}
+
+int32
+Camera::frustumTestSphere(const Sphere *s) const
+{
+ int32 res = SPHEREINSIDE;
+ const FrustumPlane *p = this->frustumPlanes;
+ for(int32 i = 0; i < 6; i++){
+ float32 dist = dot(p->plane.normal, s->center) - p->plane.distance;
+ if(s->radius < dist)
+ return SPHEREOUTSIDE;
+ if(s->radius > -dist)
+ res = SPHEREBOUNDARY;
+ p++;
+ }
+ return res;
+}
+
+struct CameraChunkData
+{
+ V2d viewWindow;
+ V2d viewOffset;
+ float32 nearPlane, farPlane;
+ float32 fogPlane;
+ int32 projection;
+};
+
+Camera*
+Camera::streamRead(Stream *stream)
+{
+ CameraChunkData buf;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ stream->read32(&buf, sizeof(CameraChunkData));
+ Camera *cam = Camera::create();
+ cam->viewWindow = buf.viewWindow;
+ cam->viewOffset = buf.viewOffset;
+ cam->nearPlane = buf.nearPlane;
+ cam->farPlane = buf.farPlane;
+ cam->fogPlane = buf.fogPlane;
+ cam->projection = buf.projection;
+ if(s_plglist.streamRead(stream, cam))
+ return cam;
+ cam->destroy();
+ return nil;
+}
+
+bool
+Camera::streamWrite(Stream *stream)
+{
+ CameraChunkData buf;
+ writeChunkHeader(stream, ID_CAMERA, this->streamGetSize());
+ writeChunkHeader(stream, ID_STRUCT, sizeof(CameraChunkData));
+ buf.viewWindow = this->viewWindow;
+ buf.viewOffset = this->viewOffset;
+ buf.nearPlane = this->nearPlane;
+ buf.farPlane = this->farPlane;
+ buf.fogPlane = this->fogPlane;
+ buf.projection = this->projection;
+ stream->write32(&buf, sizeof(CameraChunkData));
+ s_plglist.streamWrite(stream, this);
+ return true;
+}
+
+uint32
+Camera::streamGetSize(void)
+{
+ return 12 + sizeof(CameraChunkData) + 12 +
+ s_plglist.streamGetSize(this);
+}
+
+// Assumes horizontal FOV for 4:3, but we convert to vertical FOV
+void
+Camera::setFOV(float32 hfov, float32 ratio)
+{
+ V2d v;
+ float w, h;
+
+ w = (float)this->frameBuffer->width;
+ h = (float)this->frameBuffer->height;
+ if(w < 1 || h < 1){
+ w = 1;
+ h = 1;
+ }
+ hfov = hfov*3.14159f/360.0f; // deg to rad and halved
+
+ float ar1 = 4.0f/3.0f;
+ float ar2 = w/h;
+ float vfov = atanf(tanf(hfov/2) / ar1) *2;
+ hfov = atanf(tanf(vfov/2) * ar2) *2;
+
+ float32 a = tanf(hfov);
+ v.set(a, a/ratio);
+ this->setViewWindow(&v);
+ v.set(0.0f, 0.0f);
+ this->setViewOffset(&v);
+}
+
+}
diff --git a/src/charset.cpp b/src/charset.cpp
new file mode 100644
index 0000000..5188b80
--- /dev/null
+++ b/src/charset.cpp
@@ -0,0 +1,244 @@
+#include <stdio.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwrender.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwcharset.h"
+
+#include "ps2/rwps2.h"
+#include "d3d/rwd3d.h"
+#include "gl/rwgl3.h"
+
+
+#define PLUGIN_ID 1000 // TODO: find a better ID
+
+#ifndef RW_NULL
+
+#ifdef RWHALFPIXEL
+#define HALFPX (0.5f)
+#else
+#define HALFPX (0.0f)
+#endif
+
+namespace rw {
+
+const uint8 fontbits[256*256] = {
+#include "vgafont.inc"
+};
+
+#define NUMCHARS 100
+static uint16 *indices;
+static RWDEVICE::Im2DVertex *vertices;
+static int32 numChars;
+static Raster *lastRaster;
+
+bool32
+Charset::open(void)
+{
+ if(indices || vertices)
+ return 0;
+ numChars = 0;
+ lastRaster = nil;
+ indices = rwNewT(uint16, NUMCHARS*6, MEMDUR_EVENT);
+ vertices = rwNewT(RWDEVICE::Im2DVertex, NUMCHARS*4, MEMDUR_EVENT);
+ if(indices == nil || vertices == nil){
+ close();
+ return 0;
+ }
+ return 1;
+}
+
+void
+Charset::close(void)
+{
+ rwFree(indices);
+ indices = nil;
+ rwFree(vertices);
+ vertices = nil;
+}
+
+Charset*
+Charset::create(const RGBA *foreground, const RGBA *background)
+{
+ Charset *charset = (Charset*)rwMalloc(sizeof(Charset), MEMDUR_EVENT);
+ if(charset == nil){
+ RWERROR((ERR_ALLOC, sizeof(Charset)));
+ return nil;
+ }
+ charset->raster = nil;
+ if(charset->setColors(foreground, background) == nil){
+ charset->destroy();
+ return nil;
+ }
+ charset->desc.count = 256;
+ charset->desc.tileWidth = 28;
+ charset->desc.tileHeight = 10;
+ charset->desc.width = 9;
+ charset->desc.height = 16;
+ charset->desc.width_internal = 9;
+ charset->desc.height_internal = 16;
+ return charset;
+}
+
+void
+Charset::destroy(void)
+{
+ if(raster)
+ raster->destroy();
+ rwFree(this);
+}
+
+Charset*
+Charset::setColors(const RGBA *foreground, const RGBA *background)
+{
+ Image *img = Image::create(256, 256, 8);
+ if(img == nil)
+ return nil;
+ img->pixels = (uint8*)fontbits;
+ img->stride = 256;
+ img->allocate();
+ img->palette[0] = background->red;
+ img->palette[1] = background->green;
+ img->palette[2] = background->blue;
+ img->palette[3] = background->alpha;
+ img->palette[4] = foreground->red;
+ img->palette[5] = foreground->green;
+ img->palette[6] = foreground->blue;
+ img->palette[7] = foreground->alpha;
+
+ Raster *newRaster = Raster::createFromImage(img);
+ img->destroy();
+ if(newRaster == nil)
+ return nil;
+ if(this->raster)
+ this->raster->destroy();
+ this->raster = newRaster;
+ return this;
+}
+
+void
+Charset::flushBuffer(void)
+{
+ if(numChars){
+ rw::SetRenderStatePtr(rw::TEXTURERASTER, lastRaster);
+ rw::SetRenderState(rw::TEXTUREADDRESS, rw::Texture::WRAP);
+ rw::SetRenderState(rw::TEXTUREFILTER, rw::Texture::NEAREST);
+
+ uint32 cull = rw::GetRenderState(rw::CULLMODE);
+ uint32 ztest = rw::GetRenderState(rw::ZTESTENABLE);
+ rw::SetRenderState(rw::CULLMODE, rw::CULLNONE);
+ rw::SetRenderState(rw::ZTESTENABLE, 0);
+
+ im2d::RenderIndexedPrimitive(rw::PRIMTYPETRILIST,
+ vertices, numChars*4, indices, numChars*6);
+
+ rw::SetRenderState(rw::CULLMODE, cull);
+ rw::SetRenderState(rw::ZTESTENABLE, ztest);
+ }
+
+ numChars = 0;
+ lastRaster = nil;
+}
+
+void
+Charset::printChar(int32 c, int32 x, int32 y)
+{
+ Camera *cam;
+ float recipZ;
+ float u, v, du, dv;
+ RWDEVICE::Im2DVertex *vert;
+ uint16 *ix;
+
+ if(c >= this->desc.count)
+ return;
+
+ if(this->raster != lastRaster || numChars >= NUMCHARS)
+ flushBuffer();
+ lastRaster = this->raster;
+
+ cam = (Camera*)engine->currentCamera;
+ vert = &vertices[numChars*4];
+ ix = &indices[numChars*6];
+ recipZ = 1.0f/cam->nearPlane;
+
+ u = ((c % this->desc.tileWidth)*this->desc.width_internal + HALFPX) / (float32)this->raster->width;
+ v = ((c / this->desc.tileWidth)*this->desc.height_internal + HALFPX) / (float32)this->raster->height;
+ du = this->desc.width_internal/(float32)this->raster->width;
+ dv = this->desc.height_internal/(float32)this->raster->height;
+
+ vert->setScreenX((float)x);
+ vert->setScreenY((float)y);
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u, recipZ);
+ vert->setV(v, recipZ);
+ vert++;
+
+ vert->setScreenX(float(x+this->desc.width_internal));
+ vert->setScreenY((float)y);
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u+du, recipZ);
+ vert->setV(v, recipZ);
+ vert++;
+
+ vert->setScreenX((float)x);
+ vert->setScreenY(float(y+this->desc.height_internal));
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u, recipZ);
+ vert->setV(v+dv, recipZ);
+ vert++;
+
+ vert->setScreenX(float(x+this->desc.width_internal));
+ vert->setScreenY(float(y+this->desc.height_internal));
+ vert->setScreenZ(rw::im2d::GetNearZ());
+ vert->setCameraZ(cam->nearPlane);
+ vert->setRecipCameraZ(recipZ);
+ vert->setColor(255, 255, 255, 255);
+ vert->setU(u+du, recipZ);
+ vert->setV(v+dv, recipZ);
+ vert++;
+
+ *ix++ = numChars*4;
+ *ix++ = numChars*4+1;
+ *ix++ = numChars*4+2;
+ *ix++ = numChars*4+2;
+ *ix++ = numChars*4+1;
+ *ix++ = numChars*4+3;
+
+ numChars++;
+}
+
+void
+Charset::print(const char *str, int32 x, int32 y, bool32 hideSpaces)
+{
+ this->printBuffered(str, x, y, hideSpaces);
+ flushBuffer();
+}
+
+void
+Charset::printBuffered(const char *str, int32 x, int32 y, bool32 hideSpaces)
+{
+ while(*str){
+ if(!hideSpaces || *str != ' ')
+ printChar((uint8)*str, x, y);
+ x += this->desc.width;
+ str++;
+ }
+}
+
+}
+
+#endif
+
diff --git a/src/clump.cpp b/src/clump.cpp
new file mode 100644
index 0000000..f228298
--- /dev/null
+++ b/src/clump.cpp
@@ -0,0 +1,669 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+
+int32 Clump::numAllocated;
+int32 Atomic::numAllocated;
+
+PluginList Clump::s_plglist(sizeof(Clump));
+PluginList Atomic::s_plglist(sizeof(Atomic));
+
+//
+// Clump
+//
+
+Clump*
+Clump::create(void)
+{
+ Clump *clump = (Clump*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_CLUMP);
+ if(clump == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ clump->object.init(Clump::ID, 0);
+ clump->atomics.init();
+ clump->lights.init();
+ clump->cameras.init();
+
+ // World extension
+ clump->world = nil;
+ clump->inWorld.init();
+
+ s_plglist.construct(clump);
+ return clump;
+}
+
+Clump*
+Clump::clone(void)
+{
+ Clump *clump = Clump::create();
+ Frame *root = this->getFrame()->cloneAndLink();
+ clump->setFrame(root);
+ FORLIST(lnk, this->atomics){
+ Atomic *a = Atomic::fromClump(lnk);
+ Atomic *atomic = a->clone();
+ atomic->setFrame(a->getFrame()->root);
+ clump->addAtomic(atomic);
+ }
+ this->getFrame()->purgeClone();
+
+ // World extension
+ if(this->world)
+ this->world->addClump(clump);
+
+ s_plglist.copy(clump, this);
+ return clump;
+}
+
+void
+Clump::destroy(void)
+{
+ Frame *f;
+ s_plglist.destruct(this);
+ FORLIST(lnk, this->atomics){
+ Atomic *a = Atomic::fromClump(lnk);
+ this->removeAtomic(a);
+ a->destroy();
+ }
+ FORLIST(lnk, this->lights){
+ Light *l = Light::fromClump(lnk);
+ this->removeLight(l);
+ l->destroy();
+ }
+ FORLIST(lnk, this->cameras){
+ Camera *c = Camera::fromClump(lnk);
+ this->removeCamera(c);
+ c->destroy();
+ }
+ if(f = this->getFrame(), f)
+ f->destroyHierarchy();
+ assert(this->world == nil);
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+Clump::addAtomic(Atomic *a)
+{
+ assert(a->clump == nil);
+ a->clump = this;
+ this->atomics.append(&a->inClump);
+}
+
+void
+Clump::removeAtomic(Atomic *a)
+{
+ assert(a->clump == this);
+ a->inClump.remove();
+ a->clump = nil;
+}
+
+void
+Clump::addLight(Light *l)
+{
+ assert(l->clump == nil);
+ l->clump = this;
+ this->lights.append(&l->inClump);
+}
+
+void
+Clump::removeLight(Light *l)
+{
+ assert(l->clump == this);
+ l->inClump.remove();
+ l->clump = nil;
+}
+
+void
+Clump::addCamera(Camera *c)
+{
+ assert(c->clump == nil);
+ c->clump = this;
+ this->cameras.append(&c->inClump);
+}
+
+void
+Clump::removeCamera(Camera *c)
+{
+ assert(c->clump == this);
+ c->inClump.remove();
+ c->clump = nil;
+}
+
+
+Clump*
+Clump::streamRead(Stream *stream)
+{
+ uint32 length, version;
+ int32 buf[3];
+ Clump *clump;
+ int32 numGeometries;
+ Geometry **geometryList;
+
+ if(!findChunk(stream, ID_STRUCT, &length, &version)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ stream->read32(buf, length);
+ int32 numAtomics = buf[0];
+ int32 numLights = 0;
+ int32 numCameras = 0;
+ if(version > 0x33000){
+ numLights = buf[1];
+ numCameras = buf[2];
+ }
+ clump = Clump::create();
+ if(clump == nil)
+ return nil;
+
+ // Frame list
+ FrameList_ frmlst;
+ frmlst.frames = nil;
+ if(!findChunk(stream, ID_FRAMELIST, nil, nil)){
+ RWERROR((ERR_CHUNK, "FRAMELIST"));
+ goto fail;
+ }
+ if(frmlst.streamRead(stream) == nil)
+ goto fail;
+ clump->setFrame(frmlst.frames[0]);
+
+ // Geometry list
+ numGeometries = 0;
+ geometryList = nil;
+ if(version >= 0x30400){
+ if(!findChunk(stream, ID_GEOMETRYLIST, nil, nil)){
+ RWERROR((ERR_CHUNK, "GEOMETRYLIST"));
+ goto fail;
+ }
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ numGeometries = stream->readI32();
+ if(numGeometries){
+ size_t sz = numGeometries*sizeof(Geometry*);
+ geometryList = (Geometry**)rwMalloc(sz, MEMDUR_FUNCTION | ID_CLUMP);
+ if(geometryList == nil){
+ RWERROR((ERR_ALLOC, sz));
+ goto fail;
+ }
+ memset(geometryList, 0, sz);
+ }
+ for(int32 i = 0; i < numGeometries; i++){
+ if(!findChunk(stream, ID_GEOMETRY, nil, nil)){
+ RWERROR((ERR_CHUNK, "GEOMETRY"));
+ goto failgeo;
+ }
+ geometryList[i] = Geometry::streamRead(stream);
+ if(geometryList[i] == nil)
+ goto failgeo;
+ }
+ }
+
+ // Atomics
+ Atomic *a;
+ for(int32 i = 0; i < numAtomics; i++){
+ if(!findChunk(stream, ID_ATOMIC, nil, nil)){
+ RWERROR((ERR_CHUNK, "ATOMIC"));
+ goto failgeo;
+ }
+ a = Atomic::streamReadClump(stream, &frmlst, geometryList);
+ if(a == nil)
+ goto failgeo;
+ clump->addAtomic(a);
+ }
+
+ // Lights
+ int32 frm;
+ Light *l;
+ for(int32 i = 0; i < numLights; i++){
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto failgeo;
+ }
+ frm = stream->readI32();
+ if(!findChunk(stream, ID_LIGHT, nil, nil)){
+ RWERROR((ERR_CHUNK, "LIGHT"));
+ goto failgeo;
+ }
+ l = Light::streamRead(stream);
+ if(l == nil)
+ goto failgeo;
+ l->setFrame(frmlst.frames[frm]);
+ clump->addLight(l);
+ }
+
+ // Cameras
+ Camera *cam;
+ for(int32 i = 0; i < numCameras; i++){
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto failgeo;
+ }
+ frm = stream->readI32();
+ if(!findChunk(stream, ID_CAMERA, nil, nil)){
+ RWERROR((ERR_CHUNK, "CAMERA"));
+ goto failgeo;
+ }
+ cam = Camera::streamRead(stream);
+ if(cam == nil)
+ goto failgeo;
+ cam->setFrame(frmlst.frames[frm]);
+ clump->addCamera(cam);
+ }
+
+ for(int32 i = 0; i < numGeometries; i++)
+ if(geometryList[i])
+ geometryList[i]->destroy();
+ rwFree(geometryList);
+ rwFree(frmlst.frames);
+ if(s_plglist.streamRead(stream, clump))
+ return clump;
+
+failgeo:
+ for(int32 i = 0; i < numGeometries; i++)
+ if(geometryList[i])
+ geometryList[i]->destroy();
+ rwFree(geometryList);
+fail:
+ rwFree(frmlst.frames);
+ clump->destroy();
+ return nil;
+}
+
+bool
+Clump::streamWrite(Stream *stream)
+{
+ int size = this->streamGetSize();
+ writeChunkHeader(stream, ID_CLUMP, size);
+ int32 numAtomics = this->countAtomics();
+ int32 numLights = this->countLights();
+ int32 numCameras = this->countCameras();
+ int32 buf[3] = { numAtomics, numLights, numCameras };
+ size = version > 0x33000 ? 12 : 4;
+ writeChunkHeader(stream, ID_STRUCT, size);
+ stream->write32(buf, size);
+
+ FrameList_ frmlst;
+ frmlst.numFrames = this->getFrame()->count();
+ frmlst.frames = (Frame**)rwMalloc(frmlst.numFrames*sizeof(Frame*), MEMDUR_FUNCTION | ID_CLUMP);
+ makeFrameList(this->getFrame(), frmlst.frames);
+ frmlst.streamWrite(stream);
+
+ if(rw::version >= 0x30400){
+ size = 12+4;
+ FORLIST(lnk, this->atomics)
+ size += 12 + Atomic::fromClump(lnk)->geometry->streamGetSize();
+ writeChunkHeader(stream, ID_GEOMETRYLIST, size);
+ writeChunkHeader(stream, ID_STRUCT, 4);
+ stream->writeI32(numAtomics); // same as numGeometries
+ FORLIST(lnk, this->atomics)
+ Atomic::fromClump(lnk)->geometry->streamWrite(stream);
+ }
+
+ FORLIST(lnk, this->atomics)
+ Atomic::fromClump(lnk)->streamWriteClump(stream, &frmlst);
+
+ FORLIST(lnk, this->lights){
+ Light *l = Light::fromClump(lnk);
+ int frm = findPointer(l->getFrame(), (void**)frmlst.frames, frmlst.numFrames);
+ if(frm < 0)
+ return false;
+ writeChunkHeader(stream, ID_STRUCT, 4);
+ stream->writeI32(frm);
+ l->streamWrite(stream);
+ }
+
+ FORLIST(lnk, this->cameras){
+ Camera *c = Camera::fromClump(lnk);
+ int frm = findPointer(c->getFrame(), (void**)frmlst.frames, frmlst.numFrames);
+ if(frm < 0)
+ return false;
+ writeChunkHeader(stream, ID_STRUCT, 4);
+ stream->writeI32(frm);
+ c->streamWrite(stream);
+ }
+
+ rwFree(frmlst.frames);
+
+ s_plglist.streamWrite(stream, this);
+ return true;
+}
+
+uint32
+Clump::streamGetSize(void)
+{
+ uint32 size = 0;
+ size += 12; // Struct
+ size += 4; // numAtomics
+ if(version > 0x33000)
+ size += 8; // numLights, numCameras
+
+ // Frame list
+ size += FrameList_::streamGetSize(this->getFrame());
+
+ if(rw::version >= 0x30400){
+ // Geometry list
+ size += 12 + 12 + 4;
+ FORLIST(lnk, this->atomics)
+ size += 12 + Atomic::fromClump(lnk)->geometry->streamGetSize();
+ }
+
+ // Atomics
+ FORLIST(lnk, this->atomics)
+ size += 12 + Atomic::fromClump(lnk)->streamGetSize();
+
+ // Lights
+ FORLIST(lnk, this->lights)
+ size += 16 + 12 + Light::fromClump(lnk)->streamGetSize();
+
+ // Cameras
+ FORLIST(lnk, this->cameras)
+ size += 16 + 12 + Camera::fromClump(lnk)->streamGetSize();
+
+ size += 12 + s_plglist.streamGetSize(this);
+ return size;
+}
+
+void
+Clump::render(void)
+{
+ Atomic *a;
+ FORLIST(lnk, this->atomics){
+ a = Atomic::fromClump(lnk);
+ if(a->object.object.flags & Atomic::RENDER)
+ a->render();
+ }
+}
+
+//
+// Atomic
+//
+
+static void
+atomicSync(ObjectWithFrame *obj)
+{
+ // TODO: interpolate
+ obj->object.privateFlags |= Atomic::WORLDBOUNDDIRTY;
+}
+
+
+static void
+worldAtomicSync(ObjectWithFrame *obj)
+{
+ Atomic *atomic = (Atomic*)obj;
+ atomic->originalSync(obj);
+}
+
+Atomic*
+Atomic::create(void)
+{
+ Atomic *atomic = (Atomic*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_ATOMIC);
+ if(atomic == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ atomic->object.object.init(Atomic::ID, 0);
+ atomic->object.syncCB = atomicSync;
+ atomic->geometry = nil;
+ atomic->boundingSphere.center.set(0.0f, 0.0f, 0.0f);
+ atomic->boundingSphere.radius = 0.0f;
+ atomic->worldBoundingSphere.center.set(0.0f, 0.0f, 0.0f);
+ atomic->worldBoundingSphere.radius = 0.0f;
+ atomic->setFrame(nil);
+ atomic->object.object.privateFlags |= WORLDBOUNDDIRTY;
+ atomic->clump = nil;
+ atomic->inClump.init();
+ atomic->pipeline = nil;
+ atomic->renderCB = Atomic::defaultRenderCB;
+ atomic->object.object.flags = Atomic::COLLISIONTEST | Atomic::RENDER;
+ // TODO: interpolator
+
+ // World extension
+ atomic->world = nil;
+ atomic->originalSync = atomic->object.syncCB;
+ atomic->object.syncCB = worldAtomicSync;
+
+ s_plglist.construct(atomic);
+ return atomic;
+}
+
+Atomic*
+Atomic::clone()
+{
+ Atomic *atomic = Atomic::create();
+ if(atomic == nil)
+ return nil;
+ atomic->object.object.copy(&this->object.object);
+ atomic->object.object.privateFlags |= WORLDBOUNDDIRTY;
+ if(this->geometry)
+ atomic->setGeometry(this->geometry, 0);
+ atomic->renderCB = this->renderCB;
+ atomic->pipeline = this->pipeline;
+
+ // World extension doesn't add to world
+
+ s_plglist.copy(atomic, this);
+ return atomic;
+}
+
+void
+Atomic::destroy(void)
+{
+ s_plglist.destruct(this);
+ if(this->geometry)
+ this->geometry->destroy();
+ assert(this->clump == nil);
+ assert(this->world == nil);
+ this->setFrame(nil);
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+Atomic::setGeometry(Geometry *geo, uint32 flags)
+{
+ if(this->geometry)
+ this->geometry->destroy();
+ if(geo)
+ geo->addRef();
+ this->geometry = geo;
+ if(flags & SAMEBOUNDINGSPHERE)
+ return;
+ if(geo){
+ this->boundingSphere = geo->morphTargets[0].boundingSphere;
+ if(this->getFrame()) // TODO: && getWorld???
+ this->getFrame()->updateObjects();
+ }
+}
+
+Sphere*
+Atomic::getWorldBoundingSphere(void)
+{
+ Sphere *s = &this->worldBoundingSphere;
+ // TODO: if we ever support morphing, check interpolation
+ if(!this->getFrame()->dirty() &&
+ (this->object.object.privateFlags & WORLDBOUNDDIRTY) == 0)
+ return s;
+ Matrix *ltm = this->getFrame()->getLTM();
+ // TODO: support scaling
+ V3d::transformPoints(&s->center, &this->boundingSphere.center, 1, ltm);
+ s->radius = this->boundingSphere.radius;
+ this->object.object.privateFlags &= ~WORLDBOUNDDIRTY;
+ return s;
+}
+
+static uint32 atomicRights[2];
+
+Atomic*
+Atomic::streamReadClump(Stream *stream,
+ FrameList_ *frameList, Geometry **geometryList)
+{
+ int32 buf[4];
+ uint32 version;
+ if(!findChunk(stream, ID_STRUCT, nil, &version)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ stream->read32(buf, version < 0x30400 ? 12 : 16);
+ Atomic *atomic = Atomic::create();
+ if(atomic == nil)
+ return nil;
+ atomic->setFrame(frameList->frames[buf[0]]);
+ Geometry *g;
+ if(version < 0x30400){
+ if(!findChunk(stream, ID_GEOMETRY, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ g = Geometry::streamRead(stream);
+ if(g == nil)
+ goto fail;
+ atomic->setGeometry(g, 0);
+ g->destroy();
+ }else
+ atomic->setGeometry(geometryList[buf[1]], 0);
+ atomic->object.object.flags = buf[2];
+
+ atomicRights[0] = 0;
+ if(!s_plglist.streamRead(stream, atomic))
+ goto fail;
+ if(atomicRights[0])
+ s_plglist.assertRights(atomic, atomicRights[0], atomicRights[1]);
+ return atomic;
+
+fail:
+ atomic->destroy();
+ return nil;
+}
+
+bool
+Atomic::streamWriteClump(Stream *stream, FrameList_ *frmlst)
+{
+ int32 buf[4] = { 0, 0, 0, 0 };
+ Clump *c = this->clump;
+ if(c == nil)
+ return false;
+ writeChunkHeader(stream, ID_ATOMIC, this->streamGetSize());
+ writeChunkHeader(stream, ID_STRUCT, rw::version < 0x30400 ? 12 : 16);
+ buf[0] = findPointer(this->getFrame(), (void**)frmlst->frames, frmlst->numFrames);
+
+ if(version < 0x30400){
+ buf[1] = this->object.object.flags;
+ stream->write32(buf, sizeof(int32[3]));
+ this->geometry->streamWrite(stream);
+ }else{
+ buf[1] = 0;
+ FORLIST(lnk, c->atomics){
+ if(Atomic::fromClump(lnk)->geometry == this->geometry)
+ goto foundgeo;
+ buf[1]++;
+ }
+ return false;
+ foundgeo:
+ buf[2] = this->object.object.flags;
+ stream->write32(buf, sizeof(buf));
+ }
+
+ s_plglist.streamWrite(stream, this);
+ return true;
+}
+
+uint32
+Atomic::streamGetSize(void)
+{
+ uint32 size = 12 + 12 + 12 + s_plglist.streamGetSize(this);
+ if(rw::version < 0x30400)
+ size += 12 + this->geometry->streamGetSize();
+ else
+ size += 4;
+ return size;
+}
+
+ObjPipeline*
+Atomic::getPipeline(void)
+{
+ return this->pipeline ?
+ this->pipeline :
+ engine->driver[platform]->defaultPipeline;
+}
+
+void
+Atomic::instance(void)
+{
+ if(this->geometry->flags & Geometry::NATIVE)
+ return;
+ this->getPipeline()->instance(this);
+ this->geometry->flags |= Geometry::NATIVE;
+}
+
+void
+Atomic::uninstance(void)
+{
+ if(!(this->geometry->flags & Geometry::NATIVE))
+ return;
+ this->getPipeline()->uninstance(this);
+ // this should be done by the CB already, just make sure
+ this->geometry->flags &= ~Geometry::NATIVE;
+}
+
+void
+Atomic::defaultRenderCB(Atomic *atomic)
+{
+ atomic->getPipeline()->render(atomic);
+}
+
+// Atomic Rights plugin
+
+static Stream*
+readAtomicRights(Stream *stream, int32, void *, int32, int32)
+{
+ stream->read32(atomicRights, 8);
+ return stream;
+}
+
+static Stream*
+writeAtomicRights(Stream *stream, int32, void *object, int32, int32)
+{
+ Atomic *atomic = (Atomic*)object;
+ uint32 buffer[2];
+ buffer[0] = atomic->pipeline->pluginID;
+ buffer[1] = atomic->pipeline->pluginData;
+ stream->write32(buffer, 8);
+ return stream;
+}
+
+static int32
+getSizeAtomicRights(void *object, int32, int32)
+{
+ Atomic *atomic = (Atomic*)object;
+ if(atomic->pipeline == nil || atomic->pipeline->pluginID == 0)
+ return 0;
+ return 8;
+}
+
+void
+registerAtomicRightsPlugin(void)
+{
+ Atomic::registerPlugin(0, ID_RIGHTTORENDER, nil, nil, nil);
+ Atomic::registerPluginStream(ID_RIGHTTORENDER,
+ readAtomicRights,
+ writeAtomicRights,
+ getSizeAtomicRights);
+}
+
+}
diff --git a/src/d3d/d3d.cpp b/src/d3d/d3d.cpp
new file mode 100644
index 0000000..f1f5f49
--- /dev/null
+++ b/src/d3d/d3d.cpp
@@ -0,0 +1,1087 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwd3d.h"
+#include "rwd3dimpl.h"
+
+#define PLUGIN_ID ID_DRIVER
+
+namespace rw {
+namespace d3d {
+
+bool32 isP8supported = 1; // set to 0 when actual d3d device is used
+
+// stolen from d3d8to9
+static uint32
+calculateTextureSize(uint32 width, uint32 height, uint32 depth, uint32 format)
+{
+#define D3DFMT_W11V11U10 65
+ switch(format){
+ default:
+ case D3DFMT_UNKNOWN:
+ return 0;
+ case D3DFMT_R3G3B2:
+ case D3DFMT_A8:
+ case D3DFMT_P8:
+ case D3DFMT_L8:
+ case D3DFMT_A4L4:
+ return width * height * depth;
+ case D3DFMT_R5G6B5:
+ case D3DFMT_X1R5G5B5:
+ case D3DFMT_A1R5G5B5:
+ case D3DFMT_A4R4G4B4:
+ case D3DFMT_A8R3G3B2:
+ case D3DFMT_X4R4G4B4:
+ case D3DFMT_A8P8:
+ case D3DFMT_A8L8:
+ case D3DFMT_V8U8:
+ case D3DFMT_L6V5U5:
+ case D3DFMT_D16_LOCKABLE:
+ case D3DFMT_D15S1:
+ case D3DFMT_D16:
+ case D3DFMT_UYVY:
+ case D3DFMT_YUY2:
+ return width * 2 * height * depth;
+ case D3DFMT_R8G8B8:
+ return width * 3 * height * depth;
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_X8R8G8B8:
+ case D3DFMT_A2B10G10R10:
+ case D3DFMT_A8B8G8R8:
+ case D3DFMT_X8B8G8R8:
+ case D3DFMT_G16R16:
+ case D3DFMT_X8L8V8U8:
+ case D3DFMT_Q8W8V8U8:
+ case D3DFMT_V16U16:
+ case D3DFMT_W11V11U10:
+ case D3DFMT_A2W10V10U10:
+ case D3DFMT_D32:
+ case D3DFMT_D24S8:
+ case D3DFMT_D24X8:
+ case D3DFMT_D24X4S4:
+ return width * 4 * height * depth;
+ case D3DFMT_DXT1:
+ assert(depth <= 1);
+ return ((width + 3) >> 2) * ((height + 3) >> 2) * 8;
+ case D3DFMT_DXT2:
+ case D3DFMT_DXT3:
+ case D3DFMT_DXT4:
+ case D3DFMT_DXT5:
+ assert(depth <= 1);
+ return ((width + 3) >> 2) * ((height + 3) >> 2) * 16;
+ }
+}
+
+int vertFormatMap[] = {
+ -1, VERT_FLOAT2, VERT_FLOAT3, VERT_FLOAT4, VERT_ARGB, VERT_RGBA /* blend indices */
+};
+
+void*
+createIndexBuffer(uint32 length, bool dynamic)
+{
+#ifdef RW_D3D9
+ IDirect3DIndexBuffer9 *ibuf;
+ if(dynamic)
+ d3ddevice->CreateIndexBuffer(length, D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &ibuf, 0);
+ else
+ d3ddevice->CreateIndexBuffer(length, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &ibuf, 0);
+ if(ibuf)
+ d3d9Globals.numIndexBuffers++;
+ return ibuf;
+#else
+ return rwNewT(uint8, length, MEMDUR_EVENT | ID_DRIVER);
+#endif
+}
+
+void
+destroyIndexBuffer(void *indexBuffer)
+{
+#ifdef RW_D3D9
+ if(indexBuffer){
+ if(((IUnknown*)indexBuffer)->Release() != 0)
+ printf("indexBuffer wasn't destroyed\n");
+ d3d9Globals.numIndexBuffers--;
+ }
+#else
+ rwFree(indexBuffer);
+#endif
+}
+
+uint16*
+lockIndices(void *indexBuffer, uint32 offset, uint32 size, uint32 flags)
+{
+ if(indexBuffer == nil)
+ return nil;
+#ifdef RW_D3D9
+ uint16 *indices;
+ IDirect3DIndexBuffer9 *ibuf = (IDirect3DIndexBuffer9*)indexBuffer;
+ ibuf->Lock(offset, size, (void**)&indices, flags);
+ return indices;
+#else
+ (void)offset;
+ (void)size;
+ (void)flags;
+ return (uint16*)indexBuffer;
+#endif
+}
+
+void
+unlockIndices(void *indexBuffer)
+{
+ if(indexBuffer == nil)
+ return;
+#ifdef RW_D3D9
+ IDirect3DIndexBuffer9 *ibuf = (IDirect3DIndexBuffer9*)indexBuffer;
+ ibuf->Unlock();
+#endif
+}
+
+void*
+createVertexBuffer(uint32 length, uint32 fvf, bool dynamic)
+{
+#ifdef RW_D3D9
+ IDirect3DVertexBuffer9 *vbuf;
+ if(dynamic)
+ d3ddevice->CreateVertexBuffer(length, D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC, fvf, D3DPOOL_DEFAULT, &vbuf, 0);
+ else
+ d3ddevice->CreateVertexBuffer(length, D3DUSAGE_WRITEONLY, fvf, D3DPOOL_MANAGED, &vbuf, 0);
+ if(vbuf)
+ d3d9Globals.numVertexBuffers++;
+ return vbuf;
+#else
+ (void)fvf;
+ return rwNewT(uint8, length, MEMDUR_EVENT | ID_DRIVER);
+#endif
+}
+
+void
+destroyVertexBuffer(void *vertexBuffer)
+{
+#ifdef RW_D3D9
+ if(vertexBuffer){
+ if(((IUnknown*)vertexBuffer)->Release() != 0)
+ printf("vertexBuffer wasn't destroyed\n");
+ d3d9Globals.numVertexBuffers--;
+ }
+#else
+ rwFree(vertexBuffer);
+#endif
+}
+
+uint8*
+lockVertices(void *vertexBuffer, uint32 offset, uint32 size, uint32 flags)
+{
+ if(vertexBuffer == nil)
+ return nil;
+#ifdef RW_D3D9
+ uint8 *verts;
+ IDirect3DVertexBuffer9 *vertbuf = (IDirect3DVertexBuffer9*)vertexBuffer;
+ vertbuf->Lock(offset, size, (void**)&verts, flags);
+ return verts;
+#else
+ (void)offset;
+ (void)size;
+ (void)flags;
+ return (uint8*)vertexBuffer;
+#endif
+}
+
+void
+unlockVertices(void *vertexBuffer)
+{
+ if(vertexBuffer == nil)
+ return;
+#ifdef RW_D3D9
+ IDirect3DVertexBuffer9 *vertbuf = (IDirect3DVertexBuffer9*)vertexBuffer;
+ vertbuf->Unlock();
+#endif
+}
+
+void*
+createTexture(int32 width, int32 height, int32 numlevels, uint32 usage, uint32 format)
+{
+#ifdef RW_D3D9
+ IDirect3DTexture9 *tex;
+ d3ddevice->CreateTexture(width, height, numlevels, usage,
+ (D3DFORMAT)format, D3DPOOL_MANAGED, &tex, nil);
+ if(tex)
+ d3d9Globals.numTextures++;
+ return tex;
+#else
+ int32 w = width;
+ int32 h = height;
+ int32 size = 0;
+ for(int32 i = 0; i < numlevels; i++){
+ size += calculateTextureSize(w, h, 1, format);
+ w /= 2;
+ if(w == 0) w = 1;
+ h /= 2;
+ if(h == 0) h = 1;
+ }
+ uint8 *data = (uint8*)rwNew(sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1)+size,
+ MEMDUR_EVENT | ID_DRIVER);
+ RasterLevels *levels = (RasterLevels*)data;
+ data += sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1);
+ levels->numlevels = numlevels;
+ levels->format = format;
+ w = width;
+ h = height;
+ for(int32 i = 0; i < numlevels; i++){
+ levels->levels[i].width = w;
+ levels->levels[i].height = h;
+ levels->levels[i].data = data;
+ levels->levels[i].size = calculateTextureSize(w, h, 1, format);
+ data += levels->levels[i].size;
+ w /= 2;
+ if(w == 0) w = 1;
+ h /= 2;
+ if(h == 0) h = 1;
+ }
+ return levels;
+#endif
+}
+
+void
+destroyTexture(void *texture)
+{
+#ifdef RW_D3D9
+ if(texture){
+ if(((IUnknown*)texture)->Release() != 0)
+ printf("texture wasn't destroyed\n");
+ d3d9Globals.numTextures--;
+ }
+#else
+ rwFree(texture);
+#endif
+}
+
+// Native Raster
+
+int32 nativeRasterOffset;
+
+struct RasterFormatInfo
+{
+ uint32 d3dformat;
+ int32 depth;
+ bool32 hasAlpha;
+ uint32 rwFormat;
+};
+
+// indexed directly by RW format
+static RasterFormatInfo formatInfoRW[16] = {
+ { 0, 0, 0, 0},
+ { D3DFMT_A1R5G5B5, 16, 1, Raster::C1555 },
+ { D3DFMT_R5G6B5, 16, 0, Raster::C565 },
+ { D3DFMT_A4R4G4B4, 16, 1, Raster::C4444 },
+ { D3DFMT_L8, 8, 0, Raster::LUM8 },
+ { D3DFMT_A8R8G8B8, 32, 1, Raster::C8888 },
+ { D3DFMT_X8R8G8B8, 32, 0, Raster::C888 },
+ { D3DFMT_D16, 16, 0, Raster::D16 },
+ { D3DFMT_D24X8, 32, 0, Raster::D24 },
+ { D3DFMT_D32, 32, 0, Raster::D32 },
+ { D3DFMT_X1R5G5B5, 16, 0, Raster::C555 },
+};
+
+static RasterFormatInfo formatInfoFull[] = {
+ { D3DFMT_R8G8B8, 0, 24, 0 },
+ { D3DFMT_A8R8G8B8, 1, 32, Raster::C8888 },
+ { D3DFMT_X8R8G8B8, 0, 32, Raster::C888 },
+ { D3DFMT_R5G6B5, 0, 16, Raster::C565 },
+ { D3DFMT_X1R5G5B5, 0, 16, Raster::C555 },
+ { D3DFMT_A1R5G5B5, 1, 16, Raster::C1555 },
+ { D3DFMT_A4R4G4B4, 1, 16, Raster::C4444 },
+ { D3DFMT_R3G3B2, 0, 8, 0 },
+ { D3DFMT_A8, 1, 8, 0 },
+ { D3DFMT_A8R3G3B2, 1, 16, 0 },
+ { D3DFMT_X4R4G4B4, 0, 16, 0 },
+ { D3DFMT_A2B10G10R10, 1, 32, 0 },
+ { D3DFMT_A8B8G8R8, 1, 32, 0 },
+ { D3DFMT_X8B8G8R8, 0, 32, 0 },
+ { D3DFMT_G16R16, 0, 32, 0 },
+ { D3DFMT_A2R10G10B10, 1, 32, 0 },
+ { D3DFMT_A16B16G16R16, 1, 64, 0 },
+ { D3DFMT_A8P8, 1, 16, 0 },
+// { D3DFMT_P8, 0, 8, ... },
+// { D3DFMT_L8, 0, 8, ... },
+ { D3DFMT_A8L8, 1, 16, 0 },
+ { D3DFMT_A4L4, 1, 8, 0 },
+ { D3DFMT_V8U8, 0, 16, 0 },
+ { D3DFMT_L6V5U5, 0, 16, 0 },
+ { D3DFMT_X8L8V8U8, 0, 32, 0 },
+ { D3DFMT_Q8W8V8U8, 0, 32, 0 },
+ { D3DFMT_V16U16, 0, 32, 0 },
+ { D3DFMT_A2W10V10U10, 1, 32, 0 },
+ { D3DFMT_D16_LOCKABLE, 0, 16, Raster::D16 },
+ { D3DFMT_D32, 0, 32, Raster::D32 },
+ { D3DFMT_D15S1, 0, 16, Raster::D16 },
+ { D3DFMT_D24S8, 0, 32, Raster::D32 },
+ { D3DFMT_D24X8, 0, 32, Raster::D32 },
+ { D3DFMT_D24X4S4, 0, 32, Raster::D32 },
+ { D3DFMT_D16, 0, 16, Raster::D16 },
+ { D3DFMT_D32F_LOCKABLE, 0, 32, Raster::D32 },
+ { D3DFMT_D24FS8, 0, 32, Raster::D32 },
+ { D3DFMT_L16, 0, 16, 0 },
+ { D3DFMT_Q16W16V16U16, 0, 64, 0 },
+ { D3DFMT_R16F, 0, 16, 0 },
+ { D3DFMT_G16R16F, 0, 32, 0 },
+ { D3DFMT_A16B16G16R16F, 1, 64, 0 },
+ { D3DFMT_R32F, 0, 32, 0 },
+ { D3DFMT_G32R32F, 0, 64, 0 },
+ { D3DFMT_A32B32G32R32F, 1, 128, 0 },
+ { D3DFMT_CxV8U8, 0, 16, 0 },
+};
+
+RasterFormatInfo*
+findFormatInfoD3D(uint32 d3dformat)
+{
+ static RasterFormatInfo fake = { 0, 0, 0, 0 };
+ int i;
+ for(i = 0; i < (int)nelem(formatInfoFull); i++)
+ if(formatInfoFull[i].d3dformat == d3dformat)
+ return &formatInfoFull[i];
+ return &fake;
+}
+
+
+static void
+rasterSetFormat(Raster *raster)
+{
+ if(raster->format == 0){
+ // have to find a format first
+ // this could perhaps be a bit more intelligent
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ raster->format = Raster::C8888;
+ break;
+
+#ifdef RW_D3D9
+ case Raster::ZBUFFER:
+ // TODO: allow other formats
+ raster->format = findFormatInfoD3D(d3d9Globals.present.AutoDepthStencilFormat)->rwFormat;
+ // can this even happen? just do something...
+ if(raster->format == 0)
+ raster->format = Raster::D32;
+ break;
+
+ case Raster::CAMERATEXTURE:
+// let's not use this because we apparently don't want alpha
+// raster->format = findFormatInfoD3D(d3d9Globals.present.BackBufferFormat)->rwFormat;
+ raster->format = findFormatInfoD3D(d3d9Globals.startMode.mode.Format)->rwFormat;
+ // can this even happen? just do something...
+ if(raster->format == 0)
+ raster->format = Raster::C888;
+ break;
+ case Raster::CAMERA:
+ raster->format = findFormatInfoD3D(d3d9Globals.present.BackBufferFormat)->rwFormat;
+ // can this even happen? just do something...
+ if(raster->format == 0)
+ raster->format = Raster::C8888;
+ break;
+#endif
+ }
+ }
+
+
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+ if(raster->format & (Raster::PAL4 | Raster::PAL8)){
+ // TODO: do we even allow PAL4?
+ natras->format = D3DFMT_P8;
+ raster->depth = 8;
+ }else{
+ natras->format = formatInfoRW[(raster->format >> 8) & 0xF].d3dformat;
+ raster->depth = formatInfoRW[(raster->format >> 8) & 0xF].depth;
+ }
+ natras->bpp = raster->depth/8;
+ natras->hasAlpha = formatInfoRW[(raster->format >> 8) & 0xF].hasAlpha;
+ raster->stride = raster->width*natras->bpp;
+
+ natras->autogenMipmap = (raster->format & (Raster::MIPMAP|Raster::AUTOMIPMAP)) == (Raster::MIPMAP|Raster::AUTOMIPMAP);
+}
+
+static Raster*
+rasterCreateTexture(Raster *raster)
+{
+ int32 levels;
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+
+ if(natras->format == D3DFMT_P8)
+ natras->palette = (uint8*)rwNew(4*256, MEMDUR_EVENT | ID_DRIVER);
+ if(natras->autogenMipmap)
+ levels = 0;
+ else if(raster->format & Raster::MIPMAP)
+ levels = Raster::calculateNumLevels(raster->width, raster->height);
+ else
+ levels = 1;
+
+ assert(natras->texture == nil);
+ natras->texture = createTexture(raster->width, raster->height,
+ levels,
+ natras->autogenMipmap ? D3DUSAGE_AUTOGENMIPMAP : 0,
+ natras->format);
+ if(natras->texture == nil){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+ return raster;
+}
+
+#ifdef RW_D3D9
+
+static Raster*
+rasterCreateCameraTexture(Raster *raster)
+{
+ if(raster->format & (Raster::PAL4 | Raster::PAL8)){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+
+ int32 levels;
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+ if(natras->autogenMipmap)
+ levels = 0;
+ else if(raster->format & Raster::MIPMAP)
+ levels = Raster::calculateNumLevels(raster->width, raster->height);
+ else
+ levels = 1;
+
+ IDirect3DTexture9 *tex;
+ d3ddevice->CreateTexture(raster->width, raster->height,
+ levels,
+ (natras->autogenMipmap ? D3DUSAGE_AUTOGENMIPMAP : 0) | D3DUSAGE_RENDERTARGET,
+ (D3DFORMAT)natras->format, D3DPOOL_DEFAULT, &tex, nil);
+ assert(natras->texture == nil);
+ natras->texture = tex;
+ if(natras->texture == nil){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+ d3d9Globals.numTextures++;
+ addVidmemRaster(raster);
+ return raster;
+}
+
+static Raster*
+rasterCreateCamera(Raster *raster)
+{
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+
+ natras->autogenMipmap = 0;
+
+ natras->format = d3d9Globals.present.BackBufferFormat;
+ raster->depth = findFormatDepth(natras->format);
+
+ natras->texture = nil; // global default render target
+ return raster;
+}
+
+static Raster*
+rasterCreateZbuffer(Raster *raster)
+{
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+
+ natras->autogenMipmap = 0;
+
+ // TODO: allow other formats
+ natras->format = d3d9Globals.present.AutoDepthStencilFormat;
+ raster->depth = findFormatDepth(natras->format);
+
+ RECT rect;
+ GetClientRect(d3d9Globals.window, &rect);
+ // This check is done by RW but it's rather strange...
+ if(rect.right == raster->width && rect.bottom == raster->height)
+ natras->texture = d3d9Globals.defaultDepthSurf;
+ else{
+ IDirect3DSurface9 *surf = nil;
+ d3ddevice->CreateDepthStencilSurface(raster->width, raster->height, (D3DFORMAT)natras->format,
+ d3d9Globals.present.MultiSampleType, d3d9Globals.present.MultiSampleQuality,
+ FALSE, &surf, nil);
+ assert(natras->texture == nil);
+ natras->texture = surf;
+ if(natras->texture == nil){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+ }
+ addVidmemRaster(raster);
+
+ return raster;
+}
+#endif
+
+Raster*
+rasterCreate(Raster *raster)
+{
+ rasterSetFormat(raster);
+
+ Raster *ret = raster;
+
+ if(raster->width == 0 || raster->height == 0){
+ raster->flags |= Raster::DONTALLOCATE;
+ raster->stride = 0;
+ goto ret;
+ }
+ if(raster->flags & Raster::DONTALLOCATE)
+ goto ret;
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ ret = rasterCreateTexture(raster);
+ break;
+
+#ifdef RW_D3D9
+ case Raster::CAMERATEXTURE:
+ ret = rasterCreateCameraTexture(raster);
+ break;
+ case Raster::ZBUFFER:
+ ret = rasterCreateZbuffer(raster);
+ break;
+ case Raster::CAMERA:
+ ret = rasterCreateCamera(raster);
+ break;
+#endif
+
+ default:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ret:
+ raster->originalWidth = raster->width;
+ raster->originalHeight = raster->height;
+ raster->originalStride = raster->stride;
+ raster->originalPixels = raster->pixels;
+ return ret;
+}
+
+uint8*
+rasterLock(Raster *raster, int32 level, int32 lockMode)
+{
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+
+ // check if already locked
+ if(raster->privateFlags & (Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE))
+ return nil;
+
+#ifdef RW_D3D9
+ DWORD flags = D3DLOCK_NOSYSLOCK;
+ if(lockMode & Raster::LOCKREAD)
+ flags |= D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE;
+ IDirect3DTexture9 *tex = (IDirect3DTexture9*)natras->texture;
+ IDirect3DSurface9 *surf, *rt;
+ D3DLOCKED_RECT lr;
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE: {
+ tex->GetSurfaceLevel(level, &surf);
+ natras->lockedSurf = surf;
+ HRESULT res = surf->LockRect(&lr, 0, flags);
+ assert(res == D3D_OK);
+ break;
+ }
+
+ case Raster::CAMERATEXTURE:
+ case Raster::CAMERA: {
+ if(lockMode & Raster::PRIVATELOCK_WRITE)
+ assert(0 && "can't lock framebuffer for writing");
+ if(raster->type == Raster::CAMERA)
+ rt = d3d9Globals.defaultRenderTarget;
+ else
+ tex->GetSurfaceLevel(level, &rt);
+ D3DSURFACE_DESC desc;
+ rt->GetDesc(&desc);
+ HRESULT res = d3ddevice->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surf, nil);
+ if(res != D3D_OK)
+ return nil;
+ d3ddevice->GetRenderTargetData(rt, surf);
+ natras->lockedSurf = surf;
+ res = surf->LockRect(&lr, 0, flags);
+ assert(res == D3D_OK);
+ break;
+ }
+
+ default:
+ assert(0 && "can't lock this raster type (yet)");
+ }
+
+ raster->pixels = (uint8*)lr.pBits;
+ raster->width >>= level;
+ raster->height >>= level;
+ raster->stride = lr.Pitch;
+ if(raster->width == 0) raster->width = 1;
+ if(raster->height == 0) raster->height = 1;
+#else
+ RasterLevels *levels = (RasterLevels*)natras->texture;
+ raster->pixels = levels->levels[level].data;
+ raster->width = levels->levels[level].width;
+ raster->height = levels->levels[level].height;
+ raster->stride = raster->width*natras->bpp;
+#endif
+ if(lockMode & Raster::LOCKREAD) raster->privateFlags |= Raster::PRIVATELOCK_READ;
+ if(lockMode & Raster::LOCKWRITE) raster->privateFlags |= Raster::PRIVATELOCK_WRITE;
+
+ return raster->pixels;
+}
+
+void
+rasterUnlock(Raster *raster, int32 level)
+{
+#if RW_D3D9
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+ IDirect3DSurface9 *surf = (IDirect3DSurface9*)natras->lockedSurf;
+ surf->UnlockRect();
+ surf->Release();
+ natras->lockedSurf = nil;
+#endif
+ raster->width = raster->originalWidth;
+ raster->height = raster->originalHeight;
+ raster->stride = raster->originalStride;
+ raster->pixels = raster->originalPixels;
+
+ raster->privateFlags &= ~(Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE);
+}
+
+int32
+rasterNumLevels(Raster *raster)
+{
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+#ifdef RW_D3D9
+ IDirect3DTexture9 *tex = (IDirect3DTexture9*)natras->texture;
+ return tex->GetLevelCount();
+#else
+ RasterLevels *levels = (RasterLevels*)natras->texture;
+ return levels->numlevels;
+#endif
+}
+
+// Almost the same as ps2 and gl3 function
+bool32
+imageFindRasterFormat(Image *img, int32 type,
+ int32 *pWidth, int32 *pHeight, int32 *pDepth, int32 *pFormat)
+{
+ int32 width, height, depth, format;
+
+ assert((type&0xF) == Raster::TEXTURE);
+
+// for(width = 1; width < img->width; width <<= 1);
+// for(height = 1; height < img->height; height <<= 1);
+ // Perhaps non-power-of-2 textures are acceptable?
+ width = img->width;
+ height = img->height;
+
+ depth = img->depth;
+
+ if(depth <= 8 && !isP8supported)
+ depth = 32;
+
+ switch(depth){
+ case 32:
+ if(img->hasAlpha())
+ format = Raster::C8888;
+ else{
+ format = Raster::C888;
+ depth = 24;
+ }
+ break;
+ case 24:
+ format = Raster::C888;
+ break;
+ case 16:
+ format = Raster::C1555;
+ break;
+ case 8:
+ format = Raster::PAL8 | Raster::C8888;
+ break;
+ case 4:
+ format = Raster::PAL4 | Raster::C8888;
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ format |= type;
+
+ *pWidth = width;
+ *pHeight = height;
+ *pDepth = depth;
+ *pFormat = format;
+
+ return 1;
+}
+
+bool32
+rasterFromImage(Raster *raster, Image *image)
+{
+ if((raster->type&0xF) != Raster::TEXTURE)
+ return 0;
+
+ void (*conv)(uint8 *out, uint8 *in) = nil;
+
+ // Unpalettize image if necessary but don't change original
+ Image *truecolimg = nil;
+ if(image->depth <= 8 && !isP8supported){
+ truecolimg = Image::create(image->width, image->height, image->depth);
+ truecolimg->pixels = image->pixels;
+ truecolimg->stride = image->stride;
+ truecolimg->palette = image->palette;
+ truecolimg->unpalettize();
+ image = truecolimg;
+ }
+
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+ int32 format = raster->format&(Raster::PAL8 | Raster::PAL4 | 0xF00);
+ switch(image->depth){
+ case 32:
+ if(format == Raster::C8888)
+ conv = conv_BGRA8888_from_RGBA8888;
+ else if(format == Raster::C888)
+ conv = conv_BGR888_from_RGB888;
+ else
+ goto err;
+ break;
+ case 24:
+ if(format == Raster::C8888)
+ conv = conv_BGRA8888_from_RGB888;
+ else if(format == Raster::C888)
+ conv = conv_BGR888_from_RGB888;
+ else
+ goto err;
+ break;
+ case 16:
+ if(format == Raster::C1555)
+ conv = conv_ARGB1555_from_ARGB1555;
+ else
+ goto err;
+ break;
+ case 8:
+ if(format == (Raster::PAL8 | Raster::C8888))
+ conv = conv_8_from_8;
+ else
+ goto err;
+ break;
+ case 4:
+ if(format == (Raster::PAL4 | Raster::C8888) ||
+ format == (Raster::PAL8 | Raster::C8888))
+ conv = conv_8_from_8;
+ else
+ goto err;
+ break;
+ default:
+ err:
+fprintf(stderr, "%d %x\n", image->depth, format); fflush(stdout);
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ uint8 *in, *out;
+ int pallength = 0;
+ if(raster->format & Raster::PAL4)
+ pallength = 16;
+ else if(raster->format & Raster::PAL8)
+ pallength = 256;
+ if(pallength){
+ in = image->palette;
+ out = (uint8*)natras->palette;
+ for(int32 i = 0; i < pallength; i++){
+ conv_RGBA8888_from_RGBA8888(out, in);
+ in += 4;
+ out += 4;
+ }
+ }
+
+ bool unlock = false;
+ if(raster->pixels == nil){
+ raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ unlock = true;
+ }
+
+ uint8 *pixels = raster->pixels;
+ assert(pixels);
+ uint8 *imgpixels = image->pixels;
+
+ int x, y;
+ assert(image->width == raster->width);
+ assert(image->height == raster->height);
+ for(y = 0; y < image->height; y++){
+ uint8 *imgrow = imgpixels;
+ uint8 *rasrow = pixels;
+ for(x = 0; x < image->width; x++){
+ conv(rasrow, imgrow);
+ imgrow += image->bpp;
+ rasrow += natras->bpp;
+ }
+ imgpixels += image->stride;
+ pixels += raster->stride;
+ }
+ if(unlock)
+ raster->unlock(0);
+
+ if(truecolimg)
+ truecolimg->destroy();
+
+ return 1;
+}
+
+Image*
+rasterToImage(Raster *raster)
+{
+ int32 depth;
+ Image *image;
+
+ bool unlock = false;
+ if(raster->pixels == nil){
+ raster->lock(0, Raster::LOCKREAD);
+ unlock = true;
+ }
+
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+ if(natras->customFormat){
+ int w = raster->width;
+ int h = raster->height;
+ // pixels are in the upper right corner
+ if(w < 4) w = 4;
+ if(h < 4) h = 4;
+ image = Image::create(w, h, 32);
+ image->allocate();
+ uint8 *pix = raster->pixels;
+ switch(natras->format){
+ case D3DFMT_DXT1:
+ image->setPixelsDXT(1, pix);
+ if((raster->format & 0xF00) == Raster::C565)
+ image->removeMask();
+ break;
+ case D3DFMT_DXT3:
+ image->setPixelsDXT(3, pix);
+ break;
+ case D3DFMT_DXT5:
+ image->setPixelsDXT(5, pix);
+ break;
+ default:
+ image->destroy();
+ if(unlock)
+ raster->unlock(0);
+ return nil;
+ }
+ // fix it up again
+ image->width = raster->width;
+ image->height = raster->height;
+
+ if(unlock)
+ raster->unlock(0);
+ return image;
+ }
+
+ void (*conv)(uint8 *out, uint8 *in) = nil;
+ switch(raster->format & 0xF00){
+ case Raster::C1555:
+ depth = 16;
+ conv = conv_ARGB1555_from_ARGB1555;
+ break;
+ case Raster::C8888:
+ depth = 32;
+ conv = conv_RGBA8888_from_BGRA8888;
+ break;
+ case Raster::C888:
+ depth = 24;
+ conv = conv_RGB888_from_BGR888;
+ break;
+ case Raster::C555:
+ depth = 16;
+ conv = conv_ARGB1555_from_RGB555;
+ break;
+
+ default:
+ case Raster::C565:
+ case Raster::C4444:
+ case Raster::LUM8:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+ int32 pallength = 0;
+ if((raster->format & Raster::PAL4) == Raster::PAL4){
+ depth = 4;
+ pallength = 16;
+ }else if((raster->format & Raster::PAL8) == Raster::PAL8){
+ depth = 8;
+ pallength = 256;
+ }
+
+ uint8 *in, *out;
+ image = Image::create(raster->width, raster->height, depth);
+ image->allocate();
+
+ if(pallength){
+ out = image->palette;
+ in = (uint8*)natras->palette;
+ for(int32 i = 0; i < pallength; i++){
+ conv_RGBA8888_from_RGBA8888(out, in);
+ in += 4;
+ out += 4;
+ }
+ }
+
+ uint8 *imgpixels = image->pixels;
+ uint8 *pixels = raster->pixels;
+
+ int x, y;
+ assert(image->width == raster->width);
+ assert(image->height == raster->height);
+ for(y = 0; y < image->height; y++){
+ uint8 *imgrow = imgpixels;
+ uint8 *rasrow = pixels;
+ for(x = 0; x < image->width; x++){
+ conv(imgrow, rasrow);
+ imgrow += image->bpp;
+ rasrow += natras->bpp;
+ }
+ imgpixels += image->stride;
+ pixels += raster->stride;
+ }
+ image->compressPalette();
+
+ if(unlock)
+ raster->unlock(0);
+
+ return image;
+}
+
+int32
+getLevelSize(Raster *raster, int32 level)
+{
+ D3dRaster *ras = GETD3DRASTEREXT(raster);
+#ifdef RW_D3D9
+ IDirect3DTexture9 *tex = (IDirect3DTexture9*)ras->texture;
+ D3DSURFACE_DESC desc;
+ tex->GetLevelDesc(level, &desc);
+ return calculateTextureSize(desc.Width, desc.Height, 1, desc.Format);
+#else
+ RasterLevels *levels = (RasterLevels*)ras->texture;
+ return levels->levels[level].size;
+#endif
+}
+
+void
+allocateDXT(Raster *raster, int32 dxt, int32 numLevels, bool32 hasAlpha)
+{
+ static uint32 dxtMap[] = {
+ 0x31545844, // DXT1
+ 0x32545844, // DXT2
+ 0x33545844, // DXT3
+ 0x34545844, // DXT4
+ 0x35545844, // DXT5
+ };
+ D3dRaster *ras = GETD3DRASTEREXT(raster);
+ ras->format = dxtMap[dxt-1];
+ ras->hasAlpha = hasAlpha;
+ ras->customFormat = 1;
+ if(ras->autogenMipmap)
+ numLevels = 0;
+ else if(raster->format & Raster::MIPMAP)
+ {}
+ else
+ numLevels = 1;
+ ras->texture = createTexture(raster->width, raster->height,
+ numLevels,
+ ras->autogenMipmap ? D3DUSAGE_AUTOGENMIPMAP : 0,
+ ras->format);
+ raster->flags &= ~Raster::DONTALLOCATE;
+}
+
+void
+setPalette(Raster *raster, void *palette, int32 size)
+{
+ D3dRaster *ras = GETD3DRASTEREXT(raster);
+ memcpy(ras->palette, palette, 4*size);
+}
+
+void
+setTexels(Raster *raster, void *texels, int32 level)
+{
+ uint8 *dst = raster->lock(level, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ memcpy(dst, texels, getLevelSize(raster, level));
+ raster->unlock(level);
+}
+
+static void*
+createNativeRaster(void *object, int32 offset, int32)
+{
+ D3dRaster *raster = PLUGINOFFSET(D3dRaster, object, offset);
+ raster->texture = nil;
+ raster->palette = nil;
+ raster->lockedSurf = nil;
+ raster->format = 0;
+ raster->hasAlpha = 0;
+ raster->customFormat = 0;
+ return object;
+}
+
+static void*
+destroyNativeRaster(void *object, int32 offset, int32)
+{
+ Raster *raster = (Raster*)object;
+ D3dRaster *natras = PLUGINOFFSET(D3dRaster, raster, offset);
+#ifdef RW_D3D9
+ removeVidmemRaster(raster);
+ evictD3D9Raster(raster);
+#endif
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ case Raster::CAMERATEXTURE:
+ destroyTexture(natras->texture);
+ break;
+
+ case Raster::ZBUFFER:
+#ifdef RW_D3D9
+ if(raster->flags & Raster::DONTALLOCATE)
+ break;
+ if(natras->texture != d3d9Globals.defaultDepthSurf)
+ ((IDirect3DSurface9*)natras->texture)->Release();
+ natras->texture = nil;
+#endif
+ break;
+ case Raster::CAMERA:
+ break;
+ }
+ rwFree(natras->palette);
+ return object;
+}
+
+static void*
+copyNativeRaster(void *dst, void *, int32 offset, int32)
+{
+ D3dRaster *raster = PLUGINOFFSET(D3dRaster, dst, offset);
+ raster->texture = nil;
+ raster->palette = nil;
+ raster->lockedSurf = nil;
+ raster->format = 0;
+ raster->hasAlpha = 0;
+ raster->customFormat = 0;
+ return dst;
+}
+
+void
+registerNativeRaster(void)
+{
+ nativeRasterOffset = Raster::registerPlugin(sizeof(D3dRaster),
+ ID_RASTERD3D9,
+ createNativeRaster,
+ destroyNativeRaster,
+ copyNativeRaster);
+}
+
+}
+}
diff --git a/src/d3d/d3d8.cpp b/src/d3d/d3d8.cpp
new file mode 100644
index 0000000..671948f
--- /dev/null
+++ b/src/d3d/d3d8.cpp
@@ -0,0 +1,678 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwd3d.h"
+#include "rwd3d8.h"
+
+#include "rwd3dimpl.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace d3d8 {
+using namespace d3d;
+
+static void*
+driverOpen(void *o, int32, int32)
+{
+ engine->driver[PLATFORM_D3D8]->defaultPipeline = makeDefaultPipeline();
+
+ engine->driver[PLATFORM_D3D8]->rasterNativeOffset = nativeRasterOffset;
+ engine->driver[PLATFORM_D3D8]->rasterCreate = rasterCreate;
+ engine->driver[PLATFORM_D3D8]->rasterLock = rasterLock;
+ engine->driver[PLATFORM_D3D8]->rasterUnlock = rasterUnlock;
+ engine->driver[PLATFORM_D3D8]->rasterNumLevels = rasterNumLevels;
+ engine->driver[PLATFORM_D3D8]->imageFindRasterFormat = imageFindRasterFormat;
+ engine->driver[PLATFORM_D3D8]->rasterFromImage = rasterFromImage;
+ engine->driver[PLATFORM_D3D8]->rasterToImage = rasterToImage;
+ return o;
+}
+
+static void*
+driverClose(void *o, int32, int32)
+{
+ return o;
+}
+
+void
+registerPlatformPlugins(void)
+{
+ Driver::registerPlugin(PLATFORM_D3D8, 0, PLATFORM_D3D8,
+ driverOpen, driverClose);
+ // shared between D3D8 and 9
+ if(nativeRasterOffset == 0)
+ registerNativeRaster();
+}
+
+uint32
+makeFVFDeclaration(uint32 flags, int32 numTex)
+{
+ uint32 fvf = 0x2;
+ if(flags & Geometry::NORMALS)
+ fvf |= 0x10;
+ if(flags & Geometry::PRELIT)
+ fvf |= 0x40;
+ fvf |= numTex << 8;
+ return fvf;
+}
+
+int32
+getStride(uint32 flags, int32 numTex)
+{
+ int32 stride = 12;
+ if(flags & Geometry::NORMALS)
+ stride += 12;;
+ if(flags & Geometry::PRELIT)
+ stride += 4;
+ stride += numTex*8;
+ return stride;
+}
+
+void*
+destroyNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_D3D8)
+ return object;
+ InstanceDataHeader *header =
+ (InstanceDataHeader*)geometry->instData;
+ geometry->instData = nil;
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ destroyIndexBuffer(inst->indexBuffer);
+ destroyVertexBuffer(inst->vertexBuffer);
+ inst++;
+ }
+ rwFree(header->inst);
+ rwFree(header);
+ return object;
+}
+
+Stream*
+readNativeData(Stream *stream, int32, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_D3D8){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geometry->instData = header;
+ header->platform = PLATFORM_D3D8;
+
+ int32 size = stream->readI32();
+ uint8 *data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_GEOMETRY);
+ stream->read8(data, size);
+ uint8 *p = data;
+ header->serialNumber = *(uint16*)p; p += 2;
+ header->numMeshes = *(uint16*)p; p += 2;
+ header->inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ inst->minVert = *(uint32*)p; p += 4;
+ inst->stride = *(uint32*)p; p += 4;
+ inst->numVertices = *(uint32*)p; p += 4;
+ inst->numIndices = *(uint32*)p; p += 4;
+ uint32 matid = *(uint32*)p; p += 4;
+ inst->material = geometry->matList.materials[matid];
+ inst->vertexShader = *(uint32*)p; p += 4;
+ inst->primType = *(uint32*)p; p += 4;
+ inst->indexBuffer = nil; p += 4;
+ inst->vertexBuffer = nil; p += 4;
+ inst->baseIndex = 0; p += 4;
+ inst->vertexAlpha = *p++;
+ inst->managed = 0; p++;
+ inst->remapped = 0; p++; // TODO: really unused? and what's that anyway?
+ inst++;
+ }
+ rwFree(data);
+
+ inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ assert(inst->indexBuffer == nil);
+ inst->indexBuffer = createIndexBuffer(inst->numIndices*2, false);
+ uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0);
+ stream->read8(indices, 2*inst->numIndices);
+ unlockIndices(inst->indexBuffer);
+
+ inst->managed = 1;
+ assert(inst->vertexBuffer == nil);
+ inst->vertexBuffer = createVertexBuffer(inst->stride*inst->numVertices, 0, false);
+ uint8 *verts = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ stream->read8(verts, inst->stride*inst->numVertices);
+ unlockVertices(inst->vertexBuffer);
+
+ inst++;
+ }
+ return stream;
+}
+
+Stream*
+writeNativeData(Stream *stream, int32 len, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_D3D8)
+ return stream;
+ stream->writeU32(PLATFORM_D3D8);
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+
+ int32 size = 4 + geometry->meshHeader->numMeshes*0x2C;
+ uint8 *data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_GEOMETRY);
+ stream->writeI32(size);
+ uint8 *p = data;
+ *(uint16*)p = header->serialNumber; p += 2;
+ *(uint16*)p = header->numMeshes; p += 2;
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ *(uint32*)p = inst->minVert; p += 4;
+ *(uint32*)p = inst->stride; p += 4;
+ *(uint32*)p = inst->numVertices; p += 4;
+ *(uint32*)p = inst->numIndices; p += 4;
+ int32 matid = geometry->matList.findIndex(inst->material);
+ *(int32*)p = matid; p += 4;
+ *(uint32*)p = inst->vertexShader; p += 4;
+ *(uint32*)p = inst->primType; p += 4;
+ *(uint32*)p = 0; p += 4; // index buffer
+ *(uint32*)p = 0; p += 4; // vertex buffer
+ *(uint32*)p = inst->baseIndex; p += 4;
+ *p++ = inst->vertexAlpha;
+ *p++ = inst->managed;
+ *p++ = inst->remapped;
+ inst++;
+ }
+ stream->write8(data, size);
+ rwFree(data);
+
+ inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0);
+ stream->write8(indices, 2*inst->numIndices);
+ unlockIndices(inst->indexBuffer);
+
+ uint8 *verts = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ stream->write8(verts, inst->stride*inst->numVertices);
+ unlockVertices(inst->vertexBuffer);
+ inst++;
+ }
+ return stream;
+}
+
+int32
+getSizeNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_D3D8)
+ return 0;
+
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ InstanceData *inst = header->inst;
+ int32 size = 12 + 4 + 4 + 4 + header->numMeshes*0x2C;
+ for(int32 i = 0; i < header->numMeshes; i++){
+ size += inst->numIndices*2 + inst->numVertices*inst->stride;
+ inst++;
+ }
+ return size;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+
+static void
+instance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // TODO: allow for REINSTANCE
+ if(geo->instData)
+ return;
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ MeshHeader *meshh = geo->meshHeader;
+ geo->instData = header;
+ header->platform = PLATFORM_D3D8;
+
+ header->serialNumber = meshh->serialNum;
+ header->numMeshes = meshh->numMeshes;
+ header->inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+
+ InstanceData *inst = header->inst;
+ Mesh *mesh = meshh->getMeshes();
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ findMinVertAndNumVertices(mesh->indices, mesh->numIndices,
+ &inst->minVert, &inst->numVertices);
+ inst->numIndices = mesh->numIndices;
+ inst->material = mesh->material;
+ inst->vertexShader = 0;
+ inst->primType = meshh->flags == 1 ? D3DPT_TRIANGLESTRIP : D3DPT_TRIANGLELIST;
+ inst->vertexBuffer = nil;
+ inst->baseIndex = 0; // (maybe) not used by us
+ inst->vertexAlpha = 0;
+ inst->managed = 0;
+ inst->remapped = 0;
+
+ inst->indexBuffer = createIndexBuffer(inst->numIndices*2, false);
+ uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0);
+ if(inst->minVert == 0)
+ memcpy(indices, mesh->indices, inst->numIndices*2);
+ else
+ for(int32 j = 0; j < inst->numIndices; j++)
+ indices[j] = mesh->indices[j] - inst->minVert;
+ unlockIndices(inst->indexBuffer);
+
+ pipe->instanceCB(geo, inst);
+ mesh++;
+ inst++;
+ }
+}
+
+static void
+uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ if((geo->flags & Geometry::NATIVE) == 0)
+ return;
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_D3D8);
+ geo->numTriangles = geo->meshHeader->guessNumTriangles();
+ geo->allocateData();
+ geo->allocateMeshes(geo->meshHeader->numMeshes, geo->meshHeader->totalIndices, 0);
+
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ InstanceData *inst = header->inst;
+ Mesh *mesh = geo->meshHeader->getMeshes();
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ uint16 *indices = lockIndices(inst->indexBuffer, 0, 0, 0);
+ if(inst->minVert == 0)
+ memcpy(mesh->indices, indices, inst->numIndices*2);
+ else
+ for(int32 j = 0; j < inst->numIndices; j++)
+ mesh->indices[j] = indices[j] + inst->minVert;
+ unlockIndices(inst->indexBuffer);
+
+ pipe->uninstanceCB(geo, inst);
+ mesh++;
+ inst++;
+ }
+ geo->generateTriangles();
+ geo->flags &= ~Geometry::NATIVE;
+ destroyNativeData(geo, 0, 0);
+}
+
+static void
+render(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // TODO: allow for REINSTANCE
+ if(geo->instData == nil)
+ pipe->instance(atomic);
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_D3D8);
+ if(pipe->renderCB)
+ pipe->renderCB(atomic, (InstanceDataHeader*)geo->instData);
+}
+
+void
+ObjPipeline::init(void)
+{
+ this->rw::ObjPipeline::init(PLATFORM_D3D8);
+ this->impl.instance = d3d8::instance;
+ this->impl.uninstance = d3d8::uninstance;
+ this->impl.render = d3d8::render;
+ this->instanceCB = nil;
+ this->uninstanceCB = nil;
+ this->renderCB = nil;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+void
+defaultInstanceCB(Geometry *geo, InstanceData *inst)
+{
+ inst->vertexShader = makeFVFDeclaration(geo->flags, geo->numTexCoordSets);
+ inst->stride = getStride(geo->flags, geo->numTexCoordSets);
+
+ assert(inst->vertexBuffer == nil);
+ inst->vertexBuffer = createVertexBuffer(inst->numVertices*inst->stride,
+ inst->vertexShader, false);
+ inst->managed = 1;
+
+ uint8 *dst = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ instV3d(VERT_FLOAT3, dst,
+ &geo->morphTargets[0].vertices[inst->minVert],
+ inst->numVertices, inst->stride);
+ dst += 12;
+
+ if(geo->flags & Geometry::NORMALS){
+ instV3d(VERT_FLOAT3, dst,
+ &geo->morphTargets[0].normals[inst->minVert],
+ inst->numVertices, inst->stride);
+ dst += 12;
+ }
+
+ inst->vertexAlpha = 0;
+ if(geo->flags & Geometry::PRELIT){
+ inst->vertexAlpha = instColor(VERT_ARGB, dst, &geo->colors[inst->minVert],
+ inst->numVertices, inst->stride);
+ dst += 4;
+ }
+
+ for(int32 i = 0; i < geo->numTexCoordSets; i++){
+ instTexCoords(VERT_FLOAT2, dst, &geo->texCoords[i][inst->minVert],
+ inst->numVertices, inst->stride);
+ dst += 8;
+ }
+ unlockVertices(inst->vertexBuffer);
+}
+
+void
+defaultUninstanceCB(Geometry *geo, InstanceData *inst)
+{
+ uint8 *src = lockVertices(inst->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ uninstV3d(VERT_FLOAT3,
+ &geo->morphTargets[0].vertices[inst->minVert],
+ src, inst->numVertices, inst->stride);
+ src += 12;
+
+ if(geo->flags & Geometry::NORMALS){
+ uninstV3d(VERT_FLOAT3,
+ &geo->morphTargets[0].normals[inst->minVert],
+ src, inst->numVertices, inst->stride);
+ src += 12;
+ }
+
+ inst->vertexAlpha = 0;
+ if(geo->flags & Geometry::PRELIT){
+ uninstColor(VERT_ARGB, &geo->colors[inst->minVert], src,
+ inst->numVertices, inst->stride);
+ src += 4;
+ }
+
+ for(int32 i = 0; i < geo->numTexCoordSets; i++){
+ uninstTexCoords(VERT_FLOAT2, &geo->texCoords[i][inst->minVert], src,
+ inst->numVertices, inst->stride);
+ src += 8;
+ }
+ unlockVertices(inst->vertexBuffer);
+}
+
+ObjPipeline*
+makeDefaultPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->renderCB = defaultRenderCB;
+ return pipe;
+}
+
+// Native Texture and Raster
+
+// only handles 4 and 8 bit textures right now
+Raster*
+readAsImage(Stream *stream, int32 width, int32 height, int32 depth, int32 format, int32 numLevels)
+{
+ uint8 palette[256*4];
+ int32 pallen = 0;
+ uint8 *data = nil;
+
+ Image *img = Image::create(width, height, 32);
+ img->allocate();
+
+ if(format & Raster::PAL4){
+ pallen = 16;
+ stream->read8(palette, 4*32);
+ }else if(format & Raster::PAL8){
+ pallen = 256;
+ stream->read8(palette, 4*256);
+ }
+ if(!Raster::formatHasAlpha(format))
+ for(int32 i = 0; i < pallen; i++)
+ palette[i*4+3] = 0xFF;
+
+ Raster *ras = nil;
+
+ for(int i = 0; i < numLevels; i++){
+ uint32 size = stream->readU32();
+
+ // don't read levels that don't exist
+ if(ras && i >= ras->getNumLevels()){
+ stream->seek(size);
+ continue;
+ }
+
+ // one allocation is enough, first level is largest
+ if(data == nil)
+ data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_IMAGE);
+ stream->read8(data, size);
+
+ if(ras){
+ ras->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ img->width = ras->width;
+ img->height = ras->height;
+ img->stride = img->width*img->bpp;
+ }
+
+ if(format & (Raster::PAL4 | Raster::PAL8)){
+ uint8 *idx = data;
+ uint8 *pixels = img->pixels;
+ for(int y = 0; y < img->height; y++){
+ uint8 *line = pixels;
+ for(int x = 0; x < img->width; x++){
+ line[0] = palette[*idx*4+0];
+ line[1] = palette[*idx*4+1];
+ line[2] = palette[*idx*4+2];
+ if(img->bpp > 3)
+ line[3] = palette[*idx*4+3];
+ line += img->bpp;
+ idx++;
+ }
+ pixels += img->stride;
+ }
+ }
+
+ if(ras == nil){
+ // Important to have filled the image with data
+ int32 newformat;
+ Raster::imageFindRasterFormat(img, format&7, &width, &height, &depth, &newformat);
+ newformat |= format & (Raster::MIPMAP | Raster::AUTOMIPMAP);
+ ras = Raster::create(width, height, depth, newformat);
+ ras->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ }
+
+ ras->setFromImage(img);
+ ras->unlock(i);
+ }
+
+ rwFree(data);
+ img->destroy();
+ return ras;
+}
+
+Texture*
+readNativeTexture(Stream *stream)
+{
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_D3D8){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ Texture *tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+
+ // Texture
+ tex->filterAddressing = stream->readU32();
+ stream->read8(tex->name, 32);
+ stream->read8(tex->mask, 32);
+
+ // Raster
+ uint32 format = stream->readU32();
+ bool32 hasAlpha = stream->readI32();
+ int32 width = stream->readU16();
+ int32 height = stream->readU16();
+ int32 depth = stream->readU8();
+ int32 numLevels = stream->readU8();
+ int32 type = stream->readU8();
+ int32 compression = stream->readU8();
+
+ int32 pallength = 0;
+ if(format & Raster::PAL4 || format & Raster::PAL8){
+ pallength = format & Raster::PAL4 ? 32 : 256;
+ if(!d3d::isP8supported){
+ tex->raster = readAsImage(stream, width, height, depth, format|type, numLevels);
+ return tex;
+ }
+ }
+
+ Raster *raster;
+ D3dRaster *ras;
+ if(compression){
+ raster = Raster::create(width, height, depth, format | type | Raster::DONTALLOCATE, PLATFORM_D3D8);
+ ras = GETD3DRASTEREXT(raster);
+ allocateDXT(raster, compression, numLevels, hasAlpha);
+ ras->customFormat = 1;
+ }else{
+ raster = Raster::create(width, height, depth, format | type, PLATFORM_D3D8);
+ ras = GETD3DRASTEREXT(raster);
+ }
+ tex->raster = raster;
+
+ // TODO: check if format supported and convert if necessary
+
+ if(pallength != 0)
+ stream->read8(ras->palette, 4*pallength);
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = stream->readU32();
+ if(i < raster->getNumLevels()){
+ data = raster->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ stream->read8(data, size);
+ raster->unlock(i);
+ }else
+ stream->seek(size);
+ }
+ return tex;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+ int32 chunksize = getSizeNativeTexture(tex);
+ writeChunkHeader(stream, ID_STRUCT, chunksize-12);
+ stream->writeU32(PLATFORM_D3D8);
+
+ // Texture
+ stream->writeU32(tex->filterAddressing);
+ stream->write8(tex->name, 32);
+ stream->write8(tex->mask, 32);
+
+ // Raster
+ Raster *raster = tex->raster;
+ D3dRaster *ras = GETD3DRASTEREXT(raster);
+ int32 numLevels = raster->getNumLevels();
+ stream->writeI32(raster->format);
+ stream->writeI32(ras->hasAlpha);
+ stream->writeU16(raster->width);
+ stream->writeU16(raster->height);
+ stream->writeU8(raster->depth);
+ stream->writeU8(numLevels);
+ stream->writeU8(raster->type);
+ int32 compression = 0;
+ if(ras->format)
+ switch(ras->format){
+ case 0x31545844: // DXT1
+ compression = 1;
+ break;
+ case 0x32545844: // DXT2
+ compression = 2;
+ break;
+ case 0x33545844: // DXT3
+ compression = 3;
+ break;
+ case 0x34545844: // DXT4
+ compression = 4;
+ break;
+ case 0x35545844: // DXT5
+ compression = 5;
+ break;
+ }
+ stream->writeU8(compression);
+
+ if(raster->format & Raster::PAL4)
+ stream->write8(ras->palette, 4*32);
+ else if(raster->format & Raster::PAL8)
+ stream->write8(ras->palette, 4*256);
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = getLevelSize(raster, i);
+ stream->writeU32(size);
+ data = raster->lock(i, Raster::LOCKREAD);
+ stream->write8(data, size);
+ raster->unlock(i);
+ }
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+ uint32 size = 12 + 72 + 16;
+ int32 levels = tex->raster->getNumLevels();
+ if(tex->raster->format & Raster::PAL4)
+ size += 4*32;
+ else if(tex->raster->format & Raster::PAL8)
+ size += 4*256;
+ for(int32 i = 0; i < levels; i++)
+ size += 4 + getLevelSize(tex->raster, i);
+ return size;
+}
+
+}
+}
diff --git a/src/d3d/d3d8matfx.cpp b/src/d3d/d3d8matfx.cpp
new file mode 100644
index 0000000..580203e
--- /dev/null
+++ b/src/d3d/d3d8matfx.cpp
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwd3d.h"
+#include "rwd3d8.h"
+
+namespace rw {
+namespace d3d8 {
+using namespace d3d;
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+ matFXGlobals.pipelines[PLATFORM_D3D8] = makeMatFXPipeline();
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_D3D8])->destroy();
+ matFXGlobals.pipelines[PLATFORM_D3D8] = nil;
+ return o;
+}
+
+void
+initMatFX(void)
+{
+ Driver::registerPlugin(PLATFORM_D3D8, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+}
+
+ObjPipeline*
+makeMatFXPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->renderCB = defaultRenderCB;
+ pipe->pluginID = ID_MATFX;
+ pipe->pluginData = 0;
+ return pipe;
+}
+
+}
+}
diff --git a/src/d3d/d3d8render.cpp b/src/d3d/d3d8render.cpp
new file mode 100644
index 0000000..c5375cb
--- /dev/null
+++ b/src/d3d/d3d8render.cpp
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwd3d.h"
+#include "rwd3d8.h"
+
+namespace rw {
+namespace d3d8 {
+using namespace d3d;
+
+#ifndef RW_D3D9
+void defaultRenderCB(Atomic*, InstanceDataHeader*) {}
+#else
+
+// This is a bit abandoned, use d3d9 instead
+
+void
+defaultRenderCB(Atomic *atomic, InstanceDataHeader *header)
+{
+ RawMatrix world;
+
+ d3d::lightingCB_Fix(atomic);
+
+ uint32 flags = atomic->geometry->flags;
+ d3d::setRenderState(D3DRS_LIGHTING, !!(flags & rw::Geometry::LIGHT));
+
+ Frame *f = atomic->getFrame();
+ convMatrix(&world, f->getLTM());
+ d3ddevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)&world);
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ d3d::setTexture(0, inst->material->texture);
+ d3d::setMaterial(flags, inst->material->color, inst->material->surfaceProps);
+
+
+ d3d::setRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
+ d3d::setRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
+ if(flags & Geometry::PRELIT)
+ d3d::setRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
+ else
+ d3d::setRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
+
+ d3ddevice->SetFVF(inst->vertexShader);
+ setStreamSource(0, (IDirect3DVertexBuffer9*)inst->vertexBuffer, 0, inst->stride);
+ setIndices((IDirect3DIndexBuffer9*)inst->indexBuffer);
+ uint32 numPrim = inst->primType == D3DPT_TRIANGLESTRIP ? inst->numIndices-2 : inst->numIndices/3;
+ d3d::flushCache();
+ d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)inst->primType, inst->baseIndex,
+ 0, inst->numVertices, 0, numPrim);
+ inst++;
+ }
+}
+
+#endif
+}
+}
diff --git a/src/d3d/d3d8skin.cpp b/src/d3d/d3d8skin.cpp
new file mode 100644
index 0000000..d9b0921
--- /dev/null
+++ b/src/d3d/d3d8skin.cpp
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwd3d.h"
+#include "rwd3d8.h"
+
+namespace rw {
+namespace d3d8 {
+using namespace d3d;
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+ skinGlobals.pipelines[PLATFORM_D3D8] = makeSkinPipeline();
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_D3D8])->destroy();
+ skinGlobals.pipelines[PLATFORM_D3D8] = nil;
+ return o;
+}
+
+void
+initSkin(void)
+{
+ Driver::registerPlugin(PLATFORM_D3D8, 0, ID_SKIN,
+ skinOpen, skinClose);
+}
+
+ObjPipeline*
+makeSkinPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->renderCB = defaultRenderCB;
+ pipe->pluginID = ID_SKIN;
+ pipe->pluginData = 1;
+ return pipe;
+}
+
+}
+}
diff --git a/src/d3d/d3d9.cpp b/src/d3d/d3d9.cpp
new file mode 100644
index 0000000..851397b
--- /dev/null
+++ b/src/d3d/d3d9.cpp
@@ -0,0 +1,853 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwd3d.h"
+#include "rwd3d9.h"
+
+#include "rwd3dimpl.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace d3d9 {
+using namespace d3d;
+
+// TODO: move to header, but not as #define
+#ifndef RW_D3D9
+static VertexElement _d3ddec_end = {0xFF,0,D3DDECLTYPE_UNUSED,0,0,0};
+#define D3DDECL_END() _d3ddec_end
+#endif
+
+#define NUMDECLELT 12
+
+static void*
+driverOpen(void *o, int32, int32)
+{
+#ifdef RW_D3D9
+ createDefaultShaders();
+#endif
+ engine->driver[PLATFORM_D3D9]->defaultPipeline = makeDefaultPipeline();
+
+ engine->driver[PLATFORM_D3D9]->rasterNativeOffset = nativeRasterOffset;
+ engine->driver[PLATFORM_D3D9]->rasterCreate = rasterCreate;
+ engine->driver[PLATFORM_D3D9]->rasterLock = rasterLock;
+ engine->driver[PLATFORM_D3D9]->rasterUnlock = rasterUnlock;
+ engine->driver[PLATFORM_D3D9]->rasterNumLevels = rasterNumLevels;
+ engine->driver[PLATFORM_D3D9]->imageFindRasterFormat = imageFindRasterFormat;
+ engine->driver[PLATFORM_D3D9]->rasterFromImage = rasterFromImage;
+ engine->driver[PLATFORM_D3D9]->rasterToImage = rasterToImage;
+ return o;
+}
+
+static void*
+driverClose(void *o, int32, int32)
+{
+#ifdef RW_D3D9
+ destroyDefaultShaders();
+#endif
+ return o;
+}
+
+void
+registerPlatformPlugins(void)
+{
+ Driver::registerPlugin(PLATFORM_D3D9, 0, PLATFORM_D3D9,
+ driverOpen, driverClose);
+ // shared between D3D8 and 9
+ if(nativeRasterOffset == 0)
+ registerNativeRaster();
+}
+
+void*
+createVertexDeclaration(VertexElement *elements)
+{
+#ifdef RW_D3D9
+ IDirect3DVertexDeclaration9 *decl = 0;
+ d3ddevice->CreateVertexDeclaration((D3DVERTEXELEMENT9*)elements, &decl);
+ if(decl)
+ d3d9Globals.numVertexDeclarations++;
+ return decl;
+#else
+ int n = 0;
+ VertexElement *e = (VertexElement*)elements;
+ while(e[n++].stream != 0xFF)
+ ;
+ e = rwNewT(VertexElement, n, MEMDUR_EVENT | ID_DRIVER);
+ memcpy(e, elements, n*sizeof(VertexElement));
+ return e;
+#endif
+}
+
+void
+destroyVertexDeclaration(void *declaration)
+{
+#ifdef RW_D3D9
+ if(declaration){
+ if(((IUnknown*)declaration)->Release() != 0)
+ printf("declaration wasn't destroyed\n");
+ d3d9Globals.numVertexDeclarations--;
+ }
+#else
+ rwFree(declaration);
+#endif
+}
+
+uint32
+getDeclaration(void *declaration, VertexElement *elements)
+{
+#ifdef RW_D3D9
+ IDirect3DVertexDeclaration9 *decl = (IDirect3DVertexDeclaration9*)declaration;
+ UINT numElt;
+ decl->GetDeclaration((D3DVERTEXELEMENT9*)elements, &numElt);
+ return numElt;
+#else
+ int n = 0;
+ VertexElement *e = (VertexElement*)declaration;
+ while(e[n++].stream != 0xFF)
+ ;
+ if(elements)
+ memcpy(elements, declaration, n*sizeof(VertexElement));
+ return n;
+#endif
+}
+
+void
+freeInstanceData(Geometry *geometry)
+{
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_D3D9)
+ return;
+ InstanceDataHeader *header =
+ (InstanceDataHeader*)geometry->instData;
+ geometry->instData = nil;
+ destroyVertexDeclaration(header->vertexDeclaration);
+ destroyIndexBuffer(header->indexBuffer);
+ destroyVertexBuffer(header->vertexStream[0].vertexBuffer);
+ destroyVertexBuffer(header->vertexStream[1].vertexBuffer);
+ rwFree(header->inst);
+ rwFree(header);
+ return;
+}
+
+
+void*
+destroyNativeData(void *object, int32, int32)
+{
+ freeInstanceData((Geometry*)object);
+ return object;
+}
+
+Stream*
+readNativeData(Stream *stream, int32, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_D3D9){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geometry->instData = header;
+ header->platform = PLATFORM_D3D9;
+
+ int32 size = stream->readI32();
+ uint8 *data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_GEOMETRY);
+ stream->read8(data, size);
+ uint8 *p = data;
+ header->serialNumber = *(uint32*)p; p += 4;
+ header->numMeshes = *(uint32*)p; p += 4;
+ header->indexBuffer = nil; p += 4;
+ header->primType = *(uint32*)p; p += 4;
+ p += 16*2; // skip vertex streams, they're repeated with the vertex buffers
+ header->useOffsets = *(bool32*)p; p += 4;
+ header->vertexDeclaration = nil; p += 4;
+ header->totalNumIndex = *(uint32*)p; p += 4;
+ header->totalNumVertex = *(uint32*)p; p += 4;
+ header->inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ inst->numIndex = *(uint32*)p; p += 4;
+ inst->minVert = *(uint32*)p; p += 4;
+ uint32 matid = *(uint32*)p; p += 4;
+ inst->material = geometry->matList.materials[matid];
+ inst->vertexAlpha = *(bool32*)p; p += 4;
+ inst->vertexShader = nil; p += 4;
+ inst->baseIndex = 0; p += 4;
+ inst->numVertices = *(uint32*)p; p += 4;
+ inst->startIndex = *(uint32*)p; p += 4;
+ inst->numPrimitives = *(uint32*)p; p += 4;
+ inst++;
+ }
+
+ VertexElement elements[NUMDECLELT];
+ uint32 numDeclarations = stream->readU32();
+ stream->read8(elements, numDeclarations*8);
+ header->vertexDeclaration = createVertexDeclaration(elements);
+
+ assert(header->indexBuffer == nil);
+ header->indexBuffer = createIndexBuffer(header->totalNumIndex*2, false);
+ uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0);
+ stream->read8(indices, 2*header->totalNumIndex);
+ unlockIndices(header->indexBuffer);
+
+ VertexStream *s;
+ p = data;
+ for(int i = 0; i < 2; i++){
+ stream->read8(p, 16);
+ s = &header->vertexStream[i];
+ s->vertexBuffer = (void*)(uintptr)*(uint32*)p; p += 4;
+ s->offset = 0; p += 4;
+ s->stride = *(uint32*)p; p += 4;
+ s->geometryFlags = *(uint16*)p; p += 2;
+ s->managed = *p++;
+ s->dynamicLock = *p++;
+
+ if(s->vertexBuffer == nil)
+ continue;
+ // TODO: use dynamic VB when doing morphing
+ assert(s->vertexBuffer == nil);
+ s->vertexBuffer = createVertexBuffer(s->stride*header->totalNumVertex, 0, false);
+ uint8 *verts = lockVertices(s->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ stream->read8(verts, s->stride*header->totalNumVertex);
+ unlockVertices(s->vertexBuffer);
+ }
+
+ // TODO: somehow depends on number of streams used (baseIndex = minVert when more than one)
+ inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ inst->baseIndex = inst->minVert + header->vertexStream[0].offset / header->vertexStream[0].stride;
+ inst++;
+ }
+
+ rwFree(data);
+ return stream;
+}
+
+Stream*
+writeNativeData(Stream *stream, int32 len, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_D3D9)
+ return stream;
+ stream->writeU32(PLATFORM_D3D9);
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ int32 size = 64 + geometry->meshHeader->numMeshes*36;
+ uint8 *data = rwNewT(uint8, size, MEMDUR_FUNCTION | ID_GEOMETRY);
+ stream->writeI32(size);
+
+ uint8 *p = data;
+ *(uint32*)p = header->serialNumber; p += 4;
+ *(uint32*)p = header->numMeshes; p += 4;
+ p += 4; // skip index buffer
+ *(uint32*)p = header->primType; p += 4;
+ p += 16*2; // skip vertex streams, they're repeated with the vertex buffers
+ *(bool32*)p = header->useOffsets; p += 4;
+ p += 4; // skip vertex declaration
+ *(uint32*)p = header->totalNumIndex; p += 4;
+ *(uint32*)p = header->totalNumVertex; p += 4;
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ *(uint32*)p = inst->numIndex; p += 4;
+ *(uint32*)p = inst->minVert; p += 4;
+ int32 matid = geometry->matList.findIndex(inst->material);
+ *(int32*)p = matid; p += 4;
+ *(bool32*)p = inst->vertexAlpha; p += 4;
+ *(uint32*)p = 0; p += 4; // vertex shader
+ *(uint32*)p = inst->baseIndex; p += 4; // not used but meh...
+ *(uint32*)p = inst->numVertices; p += 4;
+ *(uint32*)p = inst->startIndex; p += 4;
+ *(uint32*)p = inst->numPrimitives; p += 4;
+ inst++;
+ }
+ stream->write8(data, size);
+
+ VertexElement elements[NUMDECLELT];
+ uint32 numElt = getDeclaration(header->vertexDeclaration, elements);
+ stream->writeU32(numElt);
+ stream->write8(elements, 8*numElt);
+
+ uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0);
+ stream->write8(indices, 2*header->totalNumIndex);
+ unlockIndices(header->indexBuffer);
+
+ VertexStream *s;
+ for(int i = 0; i < 2; i++){
+ s = &header->vertexStream[i];
+ p = data;
+ *(uint32*)p = s->vertexBuffer ? 0xbadeaffe : 0; p += 4;
+ *(uint32*)p = s->offset; p += 4;
+ *(uint32*)p = s->stride; p += 4;
+ *(uint16*)p = s->geometryFlags; p += 2;
+ *p++ = s->managed;
+ *p++ = s->dynamicLock;
+ stream->write8(data, 16);
+
+ if(s->vertexBuffer == nil)
+ continue;
+ uint8 *verts = lockVertices(s->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ stream->write8(verts, s->stride*header->totalNumVertex);
+ unlockVertices(s->vertexBuffer);
+ }
+
+ rwFree(data);
+ return stream;
+}
+
+int32
+getSizeNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_D3D9)
+ return 0;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ int32 size = 12 + 4 + 4 + 64 + header->numMeshes*36;
+ uint32 numElt = getDeclaration(header->vertexDeclaration, nil);
+ size += 4 + numElt*8;
+ size += 2*header->totalNumIndex;
+ size += 0x10 + header->vertexStream[0].stride*header->totalNumVertex;
+ size += 0x10 + header->vertexStream[1].stride*header->totalNumVertex;
+ return size;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+static InstanceDataHeader*
+instanceMesh(rw::ObjPipeline *rwpipe, Geometry *geo)
+{
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ MeshHeader *meshh = geo->meshHeader;
+ header->platform = PLATFORM_D3D9;
+
+ header->serialNumber = meshh->serialNum;
+ header->numMeshes = meshh->numMeshes;
+ header->primType = meshh->flags == 1 ? D3DPT_TRIANGLESTRIP : D3DPT_TRIANGLELIST;
+ header->useOffsets = 0;
+ header->vertexDeclaration = nil;
+ header->totalNumVertex = geo->numVertices;
+ header->totalNumIndex = meshh->totalIndices;
+ header->inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+
+ header->indexBuffer = createIndexBuffer(header->totalNumIndex*2, false);
+
+ uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0);
+ InstanceData *inst = header->inst;
+ Mesh *mesh = meshh->getMeshes();
+ uint32 startindex = 0;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ findMinVertAndNumVertices(mesh->indices, mesh->numIndices,
+ &inst->minVert, (int32*)&inst->numVertices);
+ inst->numIndex = mesh->numIndices;
+ inst->material = mesh->material;
+ inst->vertexAlpha = 0;
+ inst->vertexShader = nil;
+ inst->baseIndex = inst->minVert;
+ inst->startIndex = startindex;
+ inst->numPrimitives = header->primType == D3DPT_TRIANGLESTRIP ? inst->numIndex-2 : inst->numIndex/3;
+ if(inst->minVert == 0)
+ memcpy(&indices[inst->startIndex], mesh->indices, inst->numIndex*2);
+ else
+ for(uint32 j = 0; j < inst->numIndex; j++)
+ indices[inst->startIndex+j] = mesh->indices[j] - inst->minVert;
+ startindex += inst->numIndex;
+ mesh++;
+ inst++;
+ }
+ unlockIndices(header->indexBuffer);
+
+ memset(&header->vertexStream, 0, 2*sizeof(VertexStream));
+
+ return header;
+}
+
+static void
+instance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // don't try to (re)instance native data
+ if(geo->flags & Geometry::NATIVE)
+ return;
+
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ if(geo->instData){
+ // Already have instanced data, so check if we have to reinstance
+ assert(header->platform == PLATFORM_D3D9);
+ if(header->serialNumber != geo->meshHeader->serialNum){
+ // Mesh changed, so reinstance everything
+ freeInstanceData(geo);
+ }
+ }
+
+ // no instance or complete reinstance
+ if(geo->instData == nil){
+ geo->instData = instanceMesh(rwpipe, geo);
+ pipe->instanceCB(geo, (InstanceDataHeader*)geo->instData, 0);
+ }else if(geo->lockedSinceInst)
+ pipe->instanceCB(geo, (InstanceDataHeader*)geo->instData, 1);
+
+ geo->lockedSinceInst = 0;
+}
+
+static void
+uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ if((geo->flags & Geometry::NATIVE) == 0)
+ return;
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_D3D9);
+ geo->numTriangles = geo->meshHeader->guessNumTriangles();
+ geo->allocateData();
+ geo->allocateMeshes(geo->meshHeader->numMeshes, geo->meshHeader->totalIndices, 0);
+
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ uint16 *indices = lockIndices(header->indexBuffer, 0, 0, 0);
+ InstanceData *inst = header->inst;
+ Mesh *mesh = geo->meshHeader->getMeshes();
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ if(inst->minVert == 0)
+ memcpy(mesh->indices, &indices[inst->startIndex], inst->numIndex*2);
+ else
+ for(uint32 j = 0; j < inst->numIndex; j++)
+ mesh->indices[j] = indices[inst->startIndex+j] + inst->minVert;
+ mesh++;
+ inst++;
+ }
+ unlockIndices(header->indexBuffer);
+
+ pipe->uninstanceCB(geo, header);
+ geo->generateTriangles();
+ geo->flags &= ~Geometry::NATIVE;
+ destroyNativeData(geo, 0, 0);
+}
+
+static void
+render(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ pipe->instance(atomic);
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_D3D9);
+ if(pipe->renderCB)
+ pipe->renderCB(atomic, (InstanceDataHeader*)geo->instData);
+}
+
+void
+ObjPipeline::init(void)
+{
+ this->rw::ObjPipeline::init(PLATFORM_D3D9);
+ this->impl.instance = d3d9::instance;
+ this->impl.uninstance = d3d9::uninstance;
+ this->impl.render = d3d9::render;
+ this->instanceCB = nil;
+ this->uninstanceCB = nil;
+ this->renderCB = nil;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+void
+defaultInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance)
+{
+ int i = 0;
+ VertexElement dcl[NUMDECLELT];
+ VertexStream *s = &header->vertexStream[0];
+
+ bool isPrelit = (geo->flags & Geometry::PRELIT) != 0;
+ bool hasNormals = (geo->flags & Geometry::NORMALS) != 0;
+
+ // TODO: support both vertex buffers
+
+ if(!reinstance){
+ // Create declarations and buffers only the first time
+
+ assert(s->vertexBuffer == nil);
+ s->offset = 0;
+ s->managed = 1;
+ s->geometryFlags = 0;
+ s->dynamicLock = 0;
+
+ dcl[i].stream = 0;
+ dcl[i].offset = 0;
+ dcl[i].type = D3DDECLTYPE_FLOAT3;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_POSITION;
+ dcl[i].usageIndex = 0;
+ i++;
+ uint16 stride = 12;
+ s->geometryFlags |= 0x2;
+
+ if(isPrelit){
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_D3DCOLOR;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_COLOR;
+ dcl[i].usageIndex = 0;
+ i++;
+ s->geometryFlags |= 0x8;
+ stride += 4;
+ }
+
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_FLOAT2;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_TEXCOORD;
+ dcl[i].usageIndex = (uint8)n;
+ i++;
+ s->geometryFlags |= 0x10 << n;
+ stride += 8;
+ }
+
+ if(hasNormals){
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_FLOAT3;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_NORMAL;
+ dcl[i].usageIndex = 0;
+ i++;
+ s->geometryFlags |= 0x4;
+ stride += 12;
+ }
+
+ // We expect some attributes to always be there, use the constant buffer as fallback
+ if(!isPrelit){
+ dcl[i].stream = 2;
+ dcl[i].offset = offsetof(VertexConstantData, color);
+ dcl[i].type = D3DDECLTYPE_D3DCOLOR;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_COLOR;
+ dcl[i].usageIndex = 0;
+ i++;
+ }
+ if(geo->numTexCoordSets == 0){
+ dcl[i].stream = 2;
+ dcl[i].offset = offsetof(VertexConstantData, texCoors[0]);
+ dcl[i].type = D3DDECLTYPE_FLOAT2;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_TEXCOORD;
+ dcl[i].usageIndex = 0;
+ i++;
+ }
+
+ dcl[i] = D3DDECL_END();
+ s->stride = stride;
+
+ assert(header->vertexDeclaration == nil);
+ header->vertexDeclaration = createVertexDeclaration((VertexElement*)dcl);
+
+ assert(s->vertexBuffer == nil);
+ s->vertexBuffer = createVertexBuffer(header->totalNumVertex*s->stride, 0, false);
+ }else
+ getDeclaration(header->vertexDeclaration, dcl);
+
+ uint8 *verts = lockVertices(s->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+
+ // Instance vertices
+ if(!reinstance || geo->lockedSinceInst&Geometry::LOCKVERTICES){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_POSITION || dcl[i].usageIndex != 0; i++)
+ ;
+ instV3d(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ geo->morphTargets[0].vertices,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ // Instance prelight colors
+ if(isPrelit && (!reinstance || geo->lockedSinceInst&Geometry::LOCKPRELIGHT)){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_COLOR || dcl[i].usageIndex != 0; i++)
+ ;
+ InstanceData *inst = header->inst;
+ uint32 n = header->numMeshes;
+ while(n--){
+ uint32 stride = header->vertexStream[dcl[i].stream].stride;
+ inst->vertexAlpha = instColor(vertFormatMap[dcl[i].type],
+ verts + dcl[i].offset + stride*inst->minVert,
+ geo->colors + inst->minVert,
+ inst->numVertices,
+ stride);
+ inst++;
+ }
+ }
+
+ // Instance tex coords
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ if(!reinstance || geo->lockedSinceInst&(Geometry::LOCKTEXCOORDS<<n)){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_TEXCOORD || dcl[i].usageIndex != n; i++)
+ ;
+ instTexCoords(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ geo->texCoords[n],
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+ }
+
+ // Instance normals
+ if(hasNormals && (!reinstance || geo->lockedSinceInst&Geometry::LOCKNORMALS)){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_NORMAL || dcl[i].usageIndex != 0; i++)
+ ;
+ instV3d(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ geo->morphTargets[0].normals,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+ unlockVertices(s->vertexBuffer);
+}
+
+void
+defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header)
+{
+ VertexElement dcl[NUMDECLELT];
+
+ uint8 *verts[2];
+ verts[0] = lockVertices(header->vertexStream[0].vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ verts[1] = lockVertices(header->vertexStream[1].vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+ getDeclaration(header->vertexDeclaration, dcl);
+
+ int i;
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_POSITION || dcl[i].usageIndex != 0; i++)
+ ;
+ uninstV3d(vertFormatMap[dcl[i].type],
+ geo->morphTargets[0].vertices,
+ verts[dcl[i].stream] + dcl[i].offset,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+
+ if(geo->flags & Geometry::PRELIT){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_COLOR || dcl[i].usageIndex != 0; i++)
+ ;
+ uninstColor(vertFormatMap[dcl[i].type],
+ geo->colors,
+ verts[dcl[i].stream] + dcl[i].offset,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_TEXCOORD || dcl[i].usageIndex != n; i++)
+ ;
+ uninstTexCoords(vertFormatMap[dcl[i].type],
+ geo->texCoords[n],
+ verts[dcl[i].stream] + dcl[i].offset,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ if(geo->flags & Geometry::NORMALS){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_NORMAL || dcl[i].usageIndex != 0; i++)
+ ;
+ uninstV3d(vertFormatMap[dcl[i].type],
+ geo->morphTargets[0].normals,
+ verts[dcl[i].stream] + dcl[i].offset,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ unlockVertices(verts[0]);
+ unlockVertices(verts[1]);
+}
+
+ObjPipeline*
+makeDefaultPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->renderCB = defaultRenderCB_Shader;
+ return pipe;
+}
+
+// Native Texture and Raster
+
+Texture*
+readNativeTexture(Stream *stream)
+{
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_D3D9){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ Texture *tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+
+ // Texture
+ tex->filterAddressing = stream->readU32();
+ stream->read8(tex->name, 32);
+ stream->read8(tex->mask, 32);
+
+ // Raster
+ int32 format = stream->readI32();
+ int32 d3dformat = stream->readI32();
+ int32 width = stream->readU16();
+ int32 height = stream->readU16();
+ int32 depth = stream->readU8();
+ int32 numLevels = stream->readU8();
+ int32 type = stream->readU8();
+/*
+#define HAS_ALPHA (1<<0)
+#define IS_CUBE (1<<1)
+#define USE_AUTOMIPMAPGEN (1<<2)
+#define IS_COMPRESSED (1<<3)
+*/
+ int32 flags = stream->readU8();
+
+ Raster *raster;
+ D3dRaster *ext;
+
+ if(flags & 8){
+ // is compressed
+ assert((flags & 2) == 0 && "Can't have cube maps yet");
+ raster = Raster::create(width, height, depth, format | type | Raster::DONTALLOCATE, PLATFORM_D3D9);
+ assert(raster);
+ ext = GETD3DRASTEREXT(raster);
+ ext->format = d3dformat;
+ ext->hasAlpha = flags & 1;
+ ext->texture = createTexture(raster->width, raster->height,
+ raster->format & Raster::MIPMAP ? numLevels : 1,
+ 0,
+ ext->format);
+ assert(ext->texture);
+ raster->flags &= ~Raster::DONTALLOCATE;
+ ext->customFormat = 1;
+ }else if(flags & 2){
+ assert(0 && "Can't have cube maps yet");
+ }else{
+ raster = Raster::create(width, height, depth, format | type, PLATFORM_D3D9);
+ assert(raster);
+ ext = GETD3DRASTEREXT(raster);
+ }
+ tex->raster = raster;
+
+ // TODO: check if format supported and convert if necessary
+
+ if(raster->format & Raster::PAL4)
+ stream->read8(ext->palette, 4*32);
+ else if(raster->format & Raster::PAL8)
+ stream->read8(ext->palette, 4*256);
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = stream->readU32();
+ if(i < raster->getNumLevels()){
+ data = raster->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ stream->read8(data, size);
+ raster->unlock(i);
+ }else
+ stream->seek(size);
+ }
+ return tex;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+ int32 chunksize = getSizeNativeTexture(tex);
+ writeChunkHeader(stream, ID_STRUCT, chunksize-12);
+ stream->writeU32(PLATFORM_D3D9);
+
+ // Texture
+ stream->writeU32(tex->filterAddressing);
+ stream->write8(tex->name, 32);
+ stream->write8(tex->mask, 32);
+
+ // Raster
+ Raster *raster = tex->raster;
+ D3dRaster *ext = GETD3DRASTEREXT(raster);
+ int32 numLevels = raster->getNumLevels();
+ stream->writeI32(raster->format);
+ stream->writeU32(ext->format);
+ stream->writeU16(raster->width);
+ stream->writeU16(raster->height);
+ stream->writeU8(raster->depth);
+ stream->writeU8(numLevels);
+ stream->writeU8(raster->type);
+ uint8 flags = 0;
+ if(ext->hasAlpha)
+ flags |= 1;
+ // no cube supported yet
+ if(ext->autogenMipmap)
+ flags |= 4;
+ if(ext->customFormat)
+ flags |= 8;
+ stream->writeU8(flags);
+
+ if(raster->format & Raster::PAL4)
+ stream->write8(ext->palette, 4*32);
+ else if(raster->format & Raster::PAL8)
+ stream->write8(ext->palette, 4*256);
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = getLevelSize(raster, i);
+ stream->writeU32(size);
+ data = raster->lock(i, Raster::LOCKREAD);
+ stream->write8(data, size);
+ raster->unlock(i);
+ }
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+ uint32 size = 12 + 72 + 16;
+ int32 levels = tex->raster->getNumLevels();
+ if(tex->raster->format & Raster::PAL4)
+ size += 4*32;
+ else if(tex->raster->format & Raster::PAL8)
+ size += 4*256;
+ for(int32 i = 0; i < levels; i++)
+ size += 4 + getLevelSize(tex->raster, i);
+ return size;
+}
+
+}
+}
diff --git a/src/d3d/d3d9matfx.cpp b/src/d3d/d3d9matfx.cpp
new file mode 100644
index 0000000..d544e65
--- /dev/null
+++ b/src/d3d/d3d9matfx.cpp
@@ -0,0 +1,313 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwrender.h"
+#include "../rwplugins.h"
+#include "rwd3d.h"
+#include "rwd3d9.h"
+
+namespace rw {
+namespace d3d9 {
+using namespace d3d;
+
+#ifndef RW_D3D9
+void matfxRenderCB_Shader(Atomic *atomic, InstanceDataHeader *header) {}
+#else
+
+static void *matfx_env_amb_VS;
+static void *matfx_env_amb_dir_VS;
+static void *matfx_env_all_VS;
+static void *matfx_env_PS;
+static void *matfx_env_tex_PS;
+
+enum
+{
+ VSLOC_texMat = VSLOC_afterLights,
+ VSLOC_colorClamp = VSLOC_texMat + 4,
+ VSLOC_envColor,
+
+ PSLOC_shininess = 1,
+};
+
+void
+matfxRender_Default(InstanceDataHeader *header, InstanceData *inst, int32 lightBits)
+{
+ Material *m = inst->material;
+
+ // Pick a shader
+ if((lightBits & VSLIGHT_MASK) == 0)
+ setVertexShader(default_amb_VS);
+ else if((lightBits & VSLIGHT_MASK) == VSLIGHT_DIRECT)
+ setVertexShader(default_amb_dir_VS);
+ else
+ setVertexShader(default_all_VS);
+
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255);
+
+ if(inst->material->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(default_tex_PS);
+ }else
+ setPixelShader(default_PS);
+
+ drawInst(header, inst);
+}
+
+static Frame *lastEnvFrame;
+
+static RawMatrix normal2texcoord = {
+ { 0.5f, 0.0f, 0.0f }, 0.0f,
+ { 0.0f, -0.5f, 0.0f }, 0.0f,
+ { 0.0f, 0.0f, 1.0f }, 0.0f,
+ { 0.5f, 0.5f, 0.0f }, 1.0f
+};
+
+void
+uploadEnvMatrix(Frame *frame)
+{
+ Matrix invMat;
+ if(frame == nil)
+ frame = engine->currentCamera->getFrame();
+
+ // cache the matrix across multiple meshes
+// can't do it, frame matrix may change
+// if(frame == lastEnvFrame)
+// return;
+// lastEnvFrame = frame;
+
+ RawMatrix envMtx, invMtx;
+ Matrix::invert(&invMat, frame->getLTM());
+ convMatrix(&invMtx, &invMat);
+ invMtx.pos.set(0.0f, 0.0f, 0.0f);
+ float uscale = fabs(normal2texcoord.right.x);
+ normal2texcoord.right.x = MatFX::envMapFlipU ? -uscale : uscale;
+ RawMatrix::mult(&envMtx, &invMtx, &normal2texcoord);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_texMat, (float*)&envMtx, 4);
+}
+
+void
+matfxRender_EnvMap(InstanceDataHeader *header, InstanceData *inst, int32 lightBits, MatFX::Env *env)
+{
+ Material *m = inst->material;
+
+ if(env->tex == nil || env->coefficient == 0.0f){
+ matfxRender_Default(header, inst, lightBits);
+ return;
+ }
+
+ d3d::setTexture(1, env->tex);
+ uploadEnvMatrix(env->frame);
+
+ SetRenderState(SRCBLEND, BLENDONE);
+
+ static float zero[4];
+ static float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ struct {
+ float shininess;
+ float disableFBA;
+ float unused[2];
+ } fxparams;
+ fxparams.shininess = env->coefficient;
+ fxparams.disableFBA = env->fbAlpha ? 0.0f : 1.0f;
+ d3ddevice->SetPixelShaderConstantF(PSLOC_shininess, (float*)&fxparams, 1);
+ // This clamps the vertex color below. With it we can achieve both PC and PS2 style matfx
+ if(MatFX::envMapApplyLight)
+ d3ddevice->SetVertexShaderConstantF(VSLOC_colorClamp, zero, 1);
+ else
+ d3ddevice->SetVertexShaderConstantF(VSLOC_colorClamp, one, 1);
+ RGBAf envcol[4];
+ if(MatFX::envMapUseMatColor)
+ convColor(envcol, &m->color);
+ else
+ convColor(envcol, &MatFX::envMapColor);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_envColor, (float*)&envcol, 1);
+
+ // Pick a shader
+ if((lightBits & VSLIGHT_MASK) == 0)
+ setVertexShader(matfx_env_amb_VS);
+ else if((lightBits & VSLIGHT_MASK) == VSLIGHT_DIRECT)
+ setVertexShader(matfx_env_amb_dir_VS);
+ else
+ setVertexShader(matfx_env_all_VS);
+
+ bool32 texAlpha = GETD3DRASTEREXT(env->tex->raster)->hasAlpha;
+
+ if(inst->material->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(matfx_env_tex_PS);
+ }else
+ setPixelShader(matfx_env_PS);
+
+ SetRenderState(VERTEXALPHA, texAlpha || inst->vertexAlpha || m->color.alpha != 255);
+
+ drawInst(header, inst);
+
+ SetRenderState(SRCBLEND, BLENDSRCALPHA);
+}
+
+void
+matfxRenderCB_Shader(Atomic *atomic, InstanceDataHeader *header)
+{
+ int vsBits;
+ uint32 flags = atomic->geometry->flags;
+ setStreamSource(0, (IDirect3DVertexBuffer9*)header->vertexStream[0].vertexBuffer,
+ 0, header->vertexStream[0].stride);
+ setIndices((IDirect3DIndexBuffer9*)header->indexBuffer);
+ setVertexDeclaration((IDirect3DVertexDeclaration9*)header->vertexDeclaration);
+
+ lastEnvFrame = nil;
+
+ vsBits = lightingCB_Shader(atomic);
+ uploadMatrices(atomic->getFrame()->getLTM());
+
+ bool normals = !!(atomic->geometry->flags & Geometry::NORMALS);
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ MatFX *matfx = MatFX::get(m);
+ if(matfx == nil)
+ matfxRender_Default(header, inst, vsBits);
+ else switch(matfx->type){
+ case MatFX::ENVMAP:
+ if(normals)
+ matfxRender_EnvMap(header, inst, vsBits, &matfx->fx[0].env);
+ else
+ matfxRender_Default(header, inst, vsBits);
+ break;
+ case MatFX::NOTHING:
+ case MatFX::BUMPMAP:
+ case MatFX::BUMPENVMAP:
+ case MatFX::DUAL:
+ case MatFX::UVTRANSFORM:
+ case MatFX::DUALUVTRANSFORM:
+ // not supported yet
+ matfxRender_Default(header, inst, vsBits);
+ break;
+ }
+
+ inst++;
+ }
+ d3d::setTexture(1, nil);
+}
+
+#define VS_NAME g_vs20_main
+#define PS_NAME g_ps20_main
+
+void
+createMatFXShaders(void)
+{
+ {
+ static
+#include "shaders/matfx_env_amb_VS.h"
+ matfx_env_amb_VS = createVertexShader((void*)VS_NAME);
+ assert(matfx_env_amb_VS);
+ }
+ {
+ static
+#include "shaders/matfx_env_amb_dir_VS.h"
+ matfx_env_amb_dir_VS = createVertexShader((void*)VS_NAME);
+ assert(matfx_env_amb_dir_VS);
+ }
+ {
+ static
+#include "shaders/matfx_env_all_VS.h"
+ matfx_env_all_VS = createVertexShader((void*)VS_NAME);
+ assert(matfx_env_all_VS);
+ }
+
+
+ {
+ static
+#include "shaders/matfx_env_PS.h"
+ matfx_env_PS = createPixelShader((void*)PS_NAME);
+ assert(matfx_env_PS);
+ }
+ {
+ static
+#include "shaders/matfx_env_tex_PS.h"
+ matfx_env_tex_PS = createPixelShader((void*)PS_NAME);
+ assert(matfx_env_tex_PS);
+ }
+
+}
+
+void
+destroyMatFXShaders(void)
+{
+ destroyVertexShader(matfx_env_amb_VS);
+ matfx_env_amb_VS = nil;
+
+ destroyVertexShader(matfx_env_amb_dir_VS);
+ matfx_env_amb_dir_VS = nil;
+
+ destroyVertexShader(matfx_env_all_VS);
+ matfx_env_all_VS = nil;
+
+
+ destroyPixelShader(matfx_env_PS);
+ matfx_env_PS = nil;
+
+ destroyPixelShader(matfx_env_tex_PS);
+ matfx_env_tex_PS = nil;
+}
+
+#endif
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+#ifdef RW_D3D9
+ createMatFXShaders();
+#endif
+
+ matFXGlobals.pipelines[PLATFORM_D3D9] = makeMatFXPipeline();
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+#ifdef RW_D3D9
+ destroyMatFXShaders();
+#endif
+
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_D3D9])->destroy();
+ matFXGlobals.pipelines[PLATFORM_D3D9] = nil;
+ return o;
+}
+
+void
+initMatFX(void)
+{
+ Driver::registerPlugin(PLATFORM_D3D9, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+}
+
+ObjPipeline*
+makeMatFXPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->renderCB = matfxRenderCB_Shader;
+ pipe->pluginID = ID_MATFX;
+ pipe->pluginData = 0;
+ return pipe;
+}
+
+}
+}
diff --git a/src/d3d/d3d9render.cpp b/src/d3d/d3d9render.cpp
new file mode 100644
index 0000000..89d937f
--- /dev/null
+++ b/src/d3d/d3d9render.cpp
@@ -0,0 +1,185 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "../rwrender.h"
+#include "rwd3d.h"
+#include "rwd3d9.h"
+
+namespace rw {
+namespace d3d9 {
+using namespace d3d;
+
+#ifndef RW_D3D9
+void defaultRenderCB(Atomic*, InstanceDataHeader*) {}
+void defaultRenderCB_Shader(Atomic *atomic, InstanceDataHeader *header) {}
+#else
+
+void
+drawInst_simple(d3d9::InstanceDataHeader *header, d3d9::InstanceData *inst)
+{
+ d3d::flushCache();
+ d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)header->primType, inst->baseIndex,
+ 0, inst->numVertices,
+ inst->startIndex, inst->numPrimitives);
+}
+
+// Emulate PS2 GS alpha test FB_ONLY case: failed alpha writes to frame- but not to depth buffer
+void
+drawInst_GSemu(d3d9::InstanceDataHeader *header, InstanceData *inst)
+{
+ uint32 hasAlpha;
+ int alphafunc, alpharef, gsalpharef;
+ int zwrite;
+ d3d::getRenderState(D3DRS_ALPHABLENDENABLE, &hasAlpha);
+ if(hasAlpha){
+ zwrite = rw::GetRenderState(rw::ZWRITEENABLE);
+ alphafunc = rw::GetRenderState(rw::ALPHATESTFUNC);
+ if(zwrite){
+ alpharef = rw::GetRenderState(rw::ALPHATESTREF);
+ gsalpharef = rw::GetRenderState(rw::GSALPHATESTREF);
+
+ SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL);
+ SetRenderState(rw::ALPHATESTREF, gsalpharef);
+ drawInst_simple(header, inst);
+ SetRenderState(rw::ALPHATESTFUNC, rw::ALPHALESS);
+ SetRenderState(rw::ZWRITEENABLE, 0);
+ drawInst_simple(header, inst);
+ SetRenderState(rw::ZWRITEENABLE, 1);
+ SetRenderState(rw::ALPHATESTFUNC, alphafunc);
+ SetRenderState(rw::ALPHATESTREF, alpharef);
+ }else{
+ SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAALWAYS);
+ drawInst_simple(header, inst);
+ SetRenderState(rw::ALPHATESTFUNC, alphafunc);
+ }
+ }else
+ drawInst_simple(header, inst);
+}
+
+void
+drawInst(d3d9::InstanceDataHeader *header, d3d9::InstanceData *inst)
+{
+ if(rw::GetRenderState(rw::GSALPHATEST))
+ drawInst_GSemu(header, inst);
+ else
+ drawInst_simple(header, inst);
+}
+
+/*
+void
+defaultRenderCB_Fix(Atomic *atomic, InstanceDataHeader *header)
+{
+ RawMatrix world;
+ Geometry *geo = atomic->geometry;
+
+ int lighting = !!(geo->flags & rw::Geometry::LIGHT);
+ if(lighting)
+ d3d::lightingCB_Fix(atomic);
+
+ d3d::setRenderState(D3DRS_LIGHTING, lighting);
+
+ Frame *f = atomic->getFrame();
+ convMatrix(&world, f->getLTM());
+ d3ddevice->SetTransform(D3DTS_WORLD, (D3DMATRIX*)&world);
+
+ setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride);
+ setIndices(header->indexBuffer);
+ setVertexDeclaration(header->vertexDeclaration);
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || inst->material->color.alpha != 255);
+ const static rw::RGBA white = { 255, 255, 255, 255 };
+ d3d::setMaterial(white, inst->material->surfaceProps);
+
+ d3d::setRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
+ if(geo->flags & Geometry::PRELIT)
+ d3d::setRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
+ else
+ d3d::setRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
+ d3d::setRenderState(D3DRS_DIFFUSEMATERIALSOURCE, inst->vertexAlpha ? D3DMCS_COLOR1 : D3DMCS_MATERIAL);
+
+ if(inst->material->texture){
+ // Texture
+ d3d::setTexture(0, inst->material->texture);
+ d3d::setTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3d::setTextureStageState(0, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3d::setTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
+ d3d::setTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3d::setTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3d::setTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
+ }else{
+ d3d::setTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3d::setTextureStageState(0, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3d::setTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3d::setTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ }
+
+ // Material colour
+ const rw::RGBA *col = &inst->material->color;
+ d3d::setTextureStageState(1, D3DTSS_CONSTANT, D3DCOLOR_ARGB(col->alpha,col->red,col->green,col->blue));
+ d3d::setTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3d::setTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3d::setTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CONSTANT);
+ d3d::setTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3d::setTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3d::setTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CONSTANT);
+
+ drawInst(header, inst);
+ inst++;
+ }
+ d3d::setTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ d3d::setTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
+}
+*/
+
+void
+defaultRenderCB_Shader(Atomic *atomic, InstanceDataHeader *header)
+{
+ int vsBits;
+ uint32 flags = atomic->geometry->flags;
+ setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride);
+ setIndices(header->indexBuffer);
+ setVertexDeclaration(header->vertexDeclaration);
+
+ vsBits = lightingCB_Shader(atomic);
+ uploadMatrices(atomic->getFrame()->getLTM());
+
+ // Pick a shader
+ if((vsBits & VSLIGHT_MASK) == 0)
+ setVertexShader(default_amb_VS);
+ else if((vsBits & VSLIGHT_MASK) == VSLIGHT_DIRECT)
+ setVertexShader(default_amb_dir_VS);
+ else
+ setVertexShader(default_all_VS);
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255);
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ if(m->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(default_tex_PS);
+ }else
+ setPixelShader(default_PS);
+
+ drawInst(header, inst);
+ inst++;
+ }
+}
+
+#endif
+}
+}
diff --git a/src/d3d/d3d9skin.cpp b/src/d3d/d3d9skin.cpp
new file mode 100644
index 0000000..86c79ab
--- /dev/null
+++ b/src/d3d/d3d9skin.cpp
@@ -0,0 +1,418 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwrender.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwd3d.h"
+#include "rwd3d9.h"
+
+namespace rw {
+namespace d3d9 {
+using namespace d3d;
+
+#ifndef RW_D3D9
+void skinInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance) {}
+void skinRenderCB(Atomic *atomic, InstanceDataHeader *header) {}
+#else
+
+
+static void *skin_amb_VS;
+static void *skin_amb_dir_VS;
+static void *skin_all_VS;
+
+#define NUMDECLELT 14
+
+void
+skinInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance)
+{
+ int i = 0;
+ VertexElement dcl[NUMDECLELT];
+ VertexStream *s = &header->vertexStream[0];
+
+ bool isPrelit = (geo->flags & Geometry::PRELIT) != 0;
+ bool hasNormals = (geo->flags & Geometry::NORMALS) != 0;
+
+ // TODO: support both vertex buffers
+
+ if(!reinstance){
+ // Create declarations and buffers only the first time
+
+ assert(s->vertexBuffer == nil);
+ s->offset = 0;
+ s->managed = 1;
+ s->geometryFlags = 0;
+ s->dynamicLock = 0;
+
+ dcl[i].stream = 0;
+ dcl[i].offset = 0;
+ dcl[i].type = D3DDECLTYPE_FLOAT3;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_POSITION;
+ dcl[i].usageIndex = 0;
+ i++;
+ uint16 stride = 12;
+ s->geometryFlags |= 0x2;
+
+ if(isPrelit){
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_D3DCOLOR;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_COLOR;
+ dcl[i].usageIndex = 0;
+ i++;
+ s->geometryFlags |= 0x8;
+ stride += 4;
+ }
+
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_FLOAT2;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_TEXCOORD;
+ dcl[i].usageIndex = (uint8)n;
+ i++;
+ s->geometryFlags |= 0x10 << n;
+ stride += 8;
+ }
+
+ if(hasNormals){
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_FLOAT3;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_NORMAL;
+ dcl[i].usageIndex = 0;
+ i++;
+ s->geometryFlags |= 0x4;
+ stride += 12;
+ }
+
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_FLOAT4;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_BLENDWEIGHT;
+ dcl[i].usageIndex = 0;
+ i++;
+ stride += 16;
+
+ dcl[i].stream = 0;
+ dcl[i].offset = stride;
+ dcl[i].type = D3DDECLTYPE_UBYTE4;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_BLENDINDICES;
+ dcl[i].usageIndex = 0;
+ i++;
+ stride += 4;
+
+ // We expect some attributes to always be there, use the constant buffer as fallback
+ if(!isPrelit){
+ dcl[i].stream = 2;
+ dcl[i].offset = offsetof(VertexConstantData, color);
+ dcl[i].type = D3DDECLTYPE_D3DCOLOR;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_COLOR;
+ dcl[i].usageIndex = 0;
+ i++;
+ }
+ if(geo->numTexCoordSets == 0){
+ dcl[i].stream = 2;
+ dcl[i].offset = offsetof(VertexConstantData, texCoors[0]);
+ dcl[i].type = D3DDECLTYPE_FLOAT2;
+ dcl[i].method = D3DDECLMETHOD_DEFAULT;
+ dcl[i].usage = D3DDECLUSAGE_TEXCOORD;
+ dcl[i].usageIndex = 0;
+ i++;
+ }
+
+ dcl[i] = D3DDECL_END();
+ s->stride = stride;
+
+ assert(header->vertexDeclaration == nil);
+ header->vertexDeclaration = createVertexDeclaration((VertexElement*)dcl);
+
+ assert(s->vertexBuffer == nil);
+ s->vertexBuffer = createVertexBuffer(header->totalNumVertex*s->stride, 0, false);
+ }else
+ getDeclaration(header->vertexDeclaration, dcl);
+
+ Skin *skin = Skin::get(geo);
+ uint8 *verts = lockVertices(s->vertexBuffer, 0, 0, D3DLOCK_NOSYSLOCK);
+
+ // Instance vertices
+ if(!reinstance || geo->lockedSinceInst&Geometry::LOCKVERTICES){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_POSITION || dcl[i].usageIndex != 0; i++)
+ ;
+ instV3d(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ geo->morphTargets[0].vertices,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ // Instance prelight colors
+ if(isPrelit && (!reinstance || geo->lockedSinceInst&Geometry::LOCKPRELIGHT)){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_COLOR || dcl[i].usageIndex != 0; i++)
+ ;
+ InstanceData *inst = header->inst;
+ uint32 n = header->numMeshes;
+ while(n--){
+ uint32 stride = header->vertexStream[dcl[i].stream].stride;
+ inst->vertexAlpha = instColor(vertFormatMap[dcl[i].type],
+ verts + dcl[i].offset + stride*inst->minVert,
+ geo->colors + inst->minVert,
+ inst->numVertices,
+ stride);
+ inst++;
+ }
+ }
+
+ // Instance tex coords
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ if(!reinstance || geo->lockedSinceInst&(Geometry::LOCKTEXCOORDS<<n)){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_TEXCOORD || dcl[i].usageIndex != n; i++)
+ ;
+ instTexCoords(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ geo->texCoords[n],
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+ }
+
+ // Instance normals
+ if(hasNormals && (!reinstance || geo->lockedSinceInst&Geometry::LOCKNORMALS)){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_NORMAL || dcl[i].usageIndex != 0; i++)
+ ;
+ instV3d(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ geo->morphTargets[0].normals,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ // Instance skin weights
+ if(!reinstance){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_BLENDWEIGHT || dcl[i].usageIndex != 0; i++)
+ ;
+ instV4d(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ (V4d*)skin->weights,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ // Instance skin indices
+ if(!reinstance){
+ for(i = 0; dcl[i].usage != D3DDECLUSAGE_BLENDINDICES || dcl[i].usageIndex != 0; i++)
+ ;
+ // not really colors of course but what the heck
+ instColor(vertFormatMap[dcl[i].type], verts + dcl[i].offset,
+ (RGBA*)skin->indices,
+ header->totalNumVertex,
+ header->vertexStream[dcl[i].stream].stride);
+ }
+
+ unlockVertices(s->vertexBuffer);
+}
+
+enum
+{
+ VSLOC_boneMatrices = VSLOC_afterLights
+};
+
+static float skinMatrices[64*16];
+
+void
+uploadSkinMatrices(Atomic *a)
+{
+ int i;
+ Skin *skin = Skin::get(a->geometry);
+ float *m = skinMatrices;
+ HAnimHierarchy *hier = Skin::getHierarchy(a);
+
+ if(hier){
+ Matrix *invMats = (Matrix*)skin->inverseMatrices;
+ Matrix tmp, tmp2;
+
+ assert(skin->numBones == hier->numNodes);
+ if(hier->flags & HAnimHierarchy::LOCALSPACEMATRICES){
+ for(i = 0; i < hier->numNodes; i++){
+ invMats[i].flags = 0;
+ Matrix::mult(&tmp, &invMats[i], &hier->matrices[i]);
+ RawMatrix::transpose((RawMatrix*)m, (RawMatrix*)&tmp);
+ m += 12;
+ }
+ }else{
+ Matrix invAtmMat;
+ Matrix::invert(&invAtmMat, a->getFrame()->getLTM());
+ for(i = 0; i < hier->numNodes; i++){
+ invMats[i].flags = 0;
+ Matrix::mult(&tmp, &hier->matrices[i], &invAtmMat);
+ Matrix::mult(&tmp2, &invMats[i], &tmp);
+ RawMatrix::transpose((RawMatrix*)m, (RawMatrix*)&tmp2);
+ m += 12;
+ }
+ }
+ }else{
+ for(i = 0; i < skin->numBones; i++){
+ m[0] = 1.0f;
+ m[1] = 0.0f;
+ m[2] = 0.0f;
+ m[3] = 0.0f;
+
+ m[4] = 0.0f;
+ m[5] = 1.0f;
+ m[6] = 0.0f;
+ m[7] = 0.0f;
+
+ m[8] = 0.0f;
+ m[9] = 0.0f;
+ m[10] = 1.0f;
+ m[11] = 0.0f;
+
+ m += 12;
+ }
+ }
+ d3ddevice->SetVertexShaderConstantF(VSLOC_boneMatrices, skinMatrices, skin->numBones*3);
+}
+
+void
+skinRenderCB(Atomic *atomic, InstanceDataHeader *header)
+{
+ int vsBits;
+ uint32 flags = atomic->geometry->flags;
+ setStreamSource(0, (IDirect3DVertexBuffer9*)header->vertexStream[0].vertexBuffer,
+ 0, header->vertexStream[0].stride);
+ setIndices((IDirect3DIndexBuffer9*)header->indexBuffer);
+ setVertexDeclaration((IDirect3DVertexDeclaration9*)header->vertexDeclaration);
+
+ vsBits = lightingCB_Shader(atomic);
+ uploadMatrices(atomic->getFrame()->getLTM());
+
+ uploadSkinMatrices(atomic);
+
+ // Pick a shader
+ if((vsBits & VSLIGHT_MASK) == 0)
+ setVertexShader(skin_amb_VS);
+ else if((vsBits & VSLIGHT_MASK) == VSLIGHT_DIRECT)
+ setVertexShader(skin_amb_dir_VS);
+ else
+ setVertexShader(skin_all_VS);
+
+ InstanceData *inst = header->inst;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Material *m = inst->material;
+
+ SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255);
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ if(inst->material->texture){
+ d3d::setTexture(0, m->texture);
+ setPixelShader(default_tex_PS);
+ }else
+ setPixelShader(default_PS);
+
+ drawInst(header, inst);
+ inst++;
+ }
+}
+
+#define VS_NAME g_vs20_main
+#define PS_NAME g_ps20_main
+
+void
+createSkinShaders(void)
+{
+ {
+ static
+#include "shaders/skin_amb_VS.h"
+ skin_amb_VS = createVertexShader((void*)VS_NAME);
+ assert(skin_amb_VS);
+ }
+ {
+ static
+#include "shaders/skin_amb_dir_VS.h"
+ skin_amb_dir_VS = createVertexShader((void*)VS_NAME);
+ assert(skin_amb_dir_VS);
+ }
+ // Skinning takes a lot of instructions....lighting may be not possible
+ // TODO: should do something about this
+ {
+ static
+#include "shaders/skin_all_VS.h"
+ skin_all_VS = createVertexShader((void*)VS_NAME);
+// assert(skin_all_VS);
+ }
+}
+
+void
+destroySkinShaders(void)
+{
+ destroyVertexShader(skin_amb_VS);
+ skin_amb_VS = nil;
+
+ destroyVertexShader(skin_amb_dir_VS);
+ skin_amb_dir_VS = nil;
+
+ if(skin_all_VS){
+ destroyVertexShader(skin_all_VS);
+ skin_all_VS = nil;
+ }
+}
+
+#endif
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+#ifdef RW_D3D9
+ createSkinShaders();
+#endif
+
+ skinGlobals.pipelines[PLATFORM_D3D9] = makeSkinPipeline();
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+#ifdef RW_D3D9
+ destroySkinShaders();
+#endif
+
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_D3D9])->destroy();
+ skinGlobals.pipelines[PLATFORM_D3D9] = nil;
+ return o;
+}
+
+void
+initSkin(void)
+{
+ Driver::registerPlugin(PLATFORM_D3D9, 0, ID_SKIN,
+ skinOpen, skinClose);
+}
+
+ObjPipeline*
+makeSkinPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = nil;
+ pipe->renderCB = skinRenderCB;
+ pipe->pluginID = ID_SKIN;
+ pipe->pluginData = 1;
+ return pipe;
+}
+
+}
+}
diff --git a/src/d3d/d3ddevice.cpp b/src/d3d/d3ddevice.cpp
new file mode 100644
index 0000000..62c46b8
--- /dev/null
+++ b/src/d3d/d3ddevice.cpp
@@ -0,0 +1,2022 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwplg.h"
+#include "../rwerror.h"
+#include "../rwrender.h"
+#include "../rwengine.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "rwd3d.h"
+#include "rwd3dimpl.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+namespace d3d {
+
+#ifdef RW_D3D9
+
+D3d9Globals d3d9Globals;
+
+// Keep track of rasters exclusively in video memory
+// as they need special treatment sometimes
+struct VidmemRaster
+{
+ Raster *raster;
+ VidmemRaster *next;
+};
+static VidmemRaster *vidmemRasters;
+void addVidmemRaster(Raster *raster);
+void removeVidmemRaster(Raster *raster);
+
+// Same thing for dynamic vertex buffers
+struct DynamicVB
+{
+ uint32 length;
+ uint32 fvf;
+ IDirect3DVertexBuffer9 **buf;
+ DynamicVB *next;
+};
+static DynamicVB *dynamicVBs;
+void addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf);
+void removeDynamicVB(IDirect3DVertexBuffer9 **buf);
+
+// Same thing for dynamic index buffers
+struct DynamicIB
+{
+ uint32 length;
+ IDirect3DIndexBuffer9 **buf;
+ DynamicIB *next;
+};
+static DynamicIB *dynamicIBs;
+void addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf);
+void removeDynamicIB(IDirect3DIndexBuffer9 **buf);
+
+
+struct RwRasterStateCache {
+ Raster *raster;
+ Texture::Addressing addressingU;
+ Texture::Addressing addressingV;
+ Texture::FilterMode filter;
+ int32 maxAniso;
+};
+
+#define MAXNUMSTAGES 8
+
+// cached RW render states
+struct RwStateCache {
+ bool32 vertexAlpha;
+ bool32 textureAlpha;
+ uint32 srcblend, destblend;
+ uint32 zwrite;
+ uint32 ztest;
+ uint32 fogenable;
+ RGBA fogcolor;
+ uint32 cullmode;
+ uint32 stencilenable;
+ uint32 stencilpass;
+ uint32 stencilfail;
+ uint32 stencilzfail;
+ uint32 stencilfunc;
+ uint32 stencilref;
+ uint32 stencilmask;
+ uint32 stencilwritemask;
+ uint32 alphafunc;
+ uint32 alpharef;
+
+ // emulation of PS2 GS
+ bool32 gsalpha;
+ uint32 gsalpharef;
+
+ RwRasterStateCache texstage[MAXNUMSTAGES];
+};
+static RwStateCache rwStateCache;
+
+void *constantVertexStream;
+static IDirect3DTexture9 *whiteTex;
+
+D3dShaderState d3dShaderState;
+
+#define MAXNUMSTATES (D3DRS_BLENDOPALPHA+1)
+#define MAXNUMTEXSTATES (D3DTSS_CONSTANT+1)
+#define MAXNUMSAMPLERSTATES (D3DSAMP_DMAPOFFSET+1)
+#define MAXNUMSTREAMS (3)
+#define MAXNUMRENDERTARGETS (4)
+
+static int32 numDirtyStates;
+static uint32 dirtyStates[MAXNUMSTATES];
+static struct {
+ uint32 value;
+ bool32 dirty;
+} stateCache[MAXNUMSTATES];
+static uint32 d3dStates[MAXNUMSTATES];
+
+// Things that aren't just integers
+struct D3dDeviceCache {
+ IDirect3DVertexShader9 *vertexShader;
+ IDirect3DPixelShader9 *pixelShader;
+ IDirect3DVertexDeclaration9 *vertexDeclaration;
+ IDirect3DIndexBuffer9 *indices;
+ struct {
+ IDirect3DVertexBuffer9 *buffer;
+ uint32 offset;
+ uint32 stride;
+ } vertexStreams[MAXNUMSTREAMS];
+ IDirect3DSurface9 *renderTargets[MAXNUMRENDERTARGETS];
+ IDirect3DSurface9 *depthSurface;
+};
+static D3dDeviceCache deviceCache;
+
+static int32 numDirtyTextureStageStates;
+static struct {
+ uint32 stage;
+ uint32 type;
+} dirtyTextureStageStates[MAXNUMTEXSTATES*MAXNUMSTAGES];
+static struct {
+ uint32 value;
+ bool32 dirty;
+} textureStageStateCache[MAXNUMTEXSTATES][MAXNUMSTAGES];
+static uint32 d3dTextureStageStates[MAXNUMTEXSTATES][MAXNUMSTAGES];
+
+static uint32 d3dSamplerStates[MAXNUMSAMPLERSTATES][MAXNUMSTAGES];
+
+
+static bool validStates[MAXNUMSTATES];
+static bool validTexStates[MAXNUMTEXSTATES];
+
+static D3DMATERIAL9 d3dmaterial;
+
+
+static uint32 blendMap[] = {
+ D3DBLEND_ZERO, // actually invalid
+ D3DBLEND_ZERO,
+ D3DBLEND_ONE,
+ D3DBLEND_SRCCOLOR,
+ D3DBLEND_INVSRCCOLOR,
+ D3DBLEND_SRCALPHA,
+ D3DBLEND_INVSRCALPHA,
+ D3DBLEND_DESTALPHA,
+ D3DBLEND_INVDESTALPHA,
+ D3DBLEND_DESTCOLOR,
+ D3DBLEND_INVDESTCOLOR,
+ D3DBLEND_SRCALPHASAT
+};
+
+static uint32 stencilOpMap[] = {
+ D3DSTENCILOP_KEEP, // actually invalid
+ D3DSTENCILOP_KEEP,
+ D3DSTENCILOP_ZERO,
+ D3DSTENCILOP_REPLACE,
+ D3DSTENCILOP_INCRSAT,
+ D3DSTENCILOP_DECRSAT,
+ D3DSTENCILOP_INVERT,
+ D3DSTENCILOP_INCR,
+ D3DSTENCILOP_DECR
+};
+
+static uint32 stencilFuncMap[] = {
+ D3DCMP_NEVER, // actually invalid
+ D3DCMP_NEVER,
+ D3DCMP_LESS,
+ D3DCMP_EQUAL,
+ D3DCMP_LESSEQUAL,
+ D3DCMP_GREATER,
+ D3DCMP_NOTEQUAL,
+ D3DCMP_GREATEREQUAL,
+ D3DCMP_ALWAYS
+};
+
+static uint32 alphafuncMap[] = {
+ D3DCMP_ALWAYS,
+ D3DCMP_GREATEREQUAL,
+ D3DCMP_LESS
+};
+
+static uint32 cullmodeMap[] = {
+ D3DCULL_NONE, // actually invalid
+ D3DCULL_NONE,
+ D3DCULL_CW,
+ D3DCULL_CCW
+};
+
+static uint32 filterConvMap[] = {
+ 0, D3DTEXF_POINT, D3DTEXF_LINEAR,
+ D3DTEXF_POINT, D3DTEXF_LINEAR,
+ D3DTEXF_POINT, D3DTEXF_LINEAR
+};
+static uint32 filterConvMap_MIP[] = {
+ 0, D3DTEXF_NONE, D3DTEXF_NONE,
+ D3DTEXF_POINT, D3DTEXF_POINT,
+ D3DTEXF_LINEAR, D3DTEXF_LINEAR
+};
+static uint32 addressConvMap[] = {
+ 0, D3DTADDRESS_WRAP, D3DTADDRESS_MIRROR,
+ D3DTADDRESS_CLAMP, D3DTADDRESS_BORDER
+};
+
+// D3D render state
+
+void
+setRenderState(uint32 state, uint32 value)
+{
+ if(stateCache[state].value != value){
+ stateCache[state].value = value;
+ if(!stateCache[state].dirty){
+ stateCache[state].dirty = 1;
+ dirtyStates[numDirtyStates++] = state;
+ }
+ }
+}
+
+void
+getRenderState(uint32 state, uint32 *value)
+{
+ *value = stateCache[state].value;
+}
+
+void
+setRenderTarget(int n, void *surf)
+{
+ if(surf != deviceCache.renderTargets[n]){
+ deviceCache.renderTargets[n] = (IDirect3DSurface9*)surf;
+ d3ddevice->SetRenderTarget(n, deviceCache.renderTargets[n]);
+ }
+}
+
+void
+setDepthSurface(void *surf)
+{
+ if(surf != deviceCache.depthSurface){
+ deviceCache.depthSurface = (IDirect3DSurface9*)surf;
+ d3ddevice->SetDepthStencilSurface(deviceCache.depthSurface);
+ }
+}
+
+void
+setTextureStageState(uint32 stage, uint32 type, uint32 value)
+{
+ if(textureStageStateCache[type][stage].value != value){
+ textureStageStateCache[type][stage].value = value;
+ if(!textureStageStateCache[type][stage].dirty){
+ textureStageStateCache[type][stage].dirty = 1;
+ dirtyTextureStageStates[numDirtyTextureStageStates].stage = stage;
+ dirtyTextureStageStates[numDirtyTextureStageStates].type = type;
+ numDirtyTextureStageStates++;
+ }
+ }
+}
+
+void
+getTextureStageState(uint32 stage, uint32 type, uint32 *value)
+{
+ *value = textureStageStateCache[type][stage].value;
+}
+
+void
+flushCache(void)
+{
+ uint32 s, t;
+ uint32 v;
+ for(int32 i = 0; i < numDirtyStates; i++){
+ s = dirtyStates[i];
+ v = stateCache[s].value;
+ stateCache[s].dirty = 0;
+ if(d3dStates[s] != v){
+ d3ddevice->SetRenderState((D3DRENDERSTATETYPE)s, v);
+ d3dStates[s] = v;
+ }
+ }
+ numDirtyStates = 0;
+ for(int32 i = 0; i < numDirtyTextureStageStates; i++){
+ s = dirtyTextureStageStates[i].stage;
+ t = dirtyTextureStageStates[i].type;
+ v = textureStageStateCache[t][s].value;
+ textureStageStateCache[t][s].dirty = 0;
+ if(d3dTextureStageStates[t][s] != v){
+ d3ddevice->SetTextureStageState(s, (D3DTEXTURESTAGESTATETYPE)t, v);
+ d3dTextureStageStates[t][s] = v;
+ }
+ }
+ numDirtyTextureStageStates = 0;
+
+ if(d3dShaderState.fogDirty){
+ d3ddevice->SetVertexShaderConstantF(VSLOC_fogData, (float*)&d3dShaderState.fogData, 1);
+ d3ddevice->SetPixelShaderConstantF(PSLOC_fogColor, (float*)&d3dShaderState.fogColor, 1);
+ d3dShaderState.fogDirty = false;
+ }
+}
+
+void
+setSamplerState(uint32 stage, uint32 type, uint32 value)
+{
+ if(d3dSamplerStates[type][stage] != value){
+ d3ddevice->SetSamplerState(stage, (D3DSAMPLERSTATETYPE)type, value);
+ d3dSamplerStates[type][stage] = value;
+ }
+}
+
+void
+getSamplerState(uint32 stage, uint32 type, uint32 *value)
+{
+ *value = d3dSamplerStates[type][stage];
+}
+
+// Bring D3D device in accordance with saved render states (after a reset)
+static void
+restoreD3d9Device(void)
+{
+ int32 i;
+ uint32 s, t;
+
+ for(i = 0; i < MAXNUMSTAGES; i++){
+ Raster *raster = rwStateCache.texstage[i].raster;
+ if(raster){
+ D3dRaster *d3draster = GETD3DRASTEREXT(raster);
+ d3ddevice->SetTexture(i, (IDirect3DTexture9*)d3draster->texture);
+ }else
+ d3ddevice->SetTexture(i, nil);
+ setSamplerState(i, D3DSAMP_ADDRESSU, addressConvMap[rwStateCache.texstage[i].addressingU]);
+ setSamplerState(i, D3DSAMP_ADDRESSV, addressConvMap[rwStateCache.texstage[i].addressingV]);
+ setSamplerState(i, D3DSAMP_MAGFILTER, filterConvMap[rwStateCache.texstage[i].filter]);
+ if(rwStateCache.texstage[i].maxAniso == 1)
+ setSamplerState(i, D3DSAMP_MINFILTER, filterConvMap[rwStateCache.texstage[i].filter]);
+ else
+ setSamplerState(i, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
+ setSamplerState(i, D3DSAMP_MIPFILTER, filterConvMap_MIP[rwStateCache.texstage[i].filter]);
+ }
+ for(s = 0; s < MAXNUMSTATES; s++)
+ if(validStates[s])
+ d3ddevice->SetRenderState((D3DRENDERSTATETYPE)s, d3dStates[s]);
+ for(t = 0; t < MAXNUMTEXSTATES; t++)
+ if(validTexStates[t])
+ for(s = 0; s < MAXNUMSTAGES; s++)
+ d3ddevice->SetTextureStageState(s, (D3DTEXTURESTAGESTATETYPE)t, d3dTextureStageStates[t][s]);
+ for(t = 1; t < MAXNUMSAMPLERSTATES; t++)
+ for(s = 0; s < MAXNUMSTAGES; s++)
+ d3ddevice->SetSamplerState(s, (D3DSAMPLERSTATETYPE)t, d3dSamplerStates[t][s]);
+ d3ddevice->SetMaterial(&d3dmaterial);
+
+ d3ddevice->SetVertexShader(deviceCache.vertexShader);
+ d3ddevice->SetPixelShader(deviceCache.pixelShader);
+ d3ddevice->SetVertexDeclaration(deviceCache.vertexDeclaration);
+ d3ddevice->SetIndices(deviceCache.indices);
+ for(i = 0; i < MAXNUMSTREAMS; i++)
+ d3ddevice->SetStreamSource(i, deviceCache.vertexStreams[i].buffer, deviceCache.vertexStreams[i].offset, deviceCache.vertexStreams[i].stride);
+
+ // shader constants are zero now
+ d3dShaderState.fogDirty = true;
+ d3dShaderState.matColor.red = 0;
+ d3dShaderState.matColor.green = 0;
+ d3dShaderState.matColor.blue = 0;
+ d3dShaderState.matColor.alpha = 0;
+ d3dShaderState.surfProps.ambient = 0.0f;
+ d3dShaderState.surfProps.specular = 0.0f;
+ d3dShaderState.surfProps.diffuse = 0.0f;
+ d3dShaderState.extraSurfProp = 0.0f;
+ d3dShaderState.numDir = 0;
+ d3dShaderState.numPoint = 0;
+ d3dShaderState.numSpot = 0;
+ d3dShaderState.ambient.red = 0.0f;
+ d3dShaderState.ambient.green = 0.0f;
+ d3dShaderState.ambient.blue = 0.0f;
+ d3dShaderState.ambient.alpha = 0.0f;
+}
+
+void
+evictD3D9Raster(Raster *raster)
+{
+ int i;
+ // Make sure we're not still referencing this raster
+ D3dRaster *natras = GETD3DRASTEREXT(raster);
+ switch(raster->type){
+ case Raster::CAMERATEXTURE:
+ for(i = 0; i < MAXNUMRENDERTARGETS; i++)
+ if(deviceCache.renderTargets[i] == natras->texture)
+ setRenderTarget(i, i == 0 ? d3d9Globals.defaultRenderTarget : nil);
+ // fall through
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ for(i = 0; i < MAXNUMSTAGES; i++)
+ if(rwStateCache.texstage[i].raster == raster)
+ rwStateCache.texstage[i].raster = nil;
+ break;
+ case Raster::ZBUFFER:
+ if(natras->texture == deviceCache.depthSurface)
+ setDepthSurface(d3d9Globals.defaultDepthSurf);
+ break;
+ }
+}
+
+// RW render state
+
+static void
+setDepthTest(bool32 enable)
+{
+ if(rwStateCache.ztest != enable){
+ rwStateCache.ztest = enable;
+ if(rwStateCache.zwrite && !enable){
+ // If we still want to write, enable but set mode to always
+ setRenderState(D3DRS_ZENABLE, TRUE);
+ setRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS);
+ }else{
+ setRenderState(D3DRS_ZENABLE, rwStateCache.ztest);
+ setRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ }
+ }
+}
+
+static void
+setDepthWrite(bool32 enable)
+{
+ if(rwStateCache.zwrite != enable){
+ rwStateCache.zwrite = enable;
+ if(enable && !rwStateCache.ztest){
+ // Have to switch on ztest so writing can work
+ setRenderState(D3DRS_ZENABLE, TRUE);
+ setRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS);
+ }
+ setRenderState(D3DRS_ZWRITEENABLE, rwStateCache.zwrite);
+ }
+}
+
+static void
+setVertexAlpha(bool32 enable)
+{
+ if(rwStateCache.vertexAlpha != enable){
+ if(!rwStateCache.textureAlpha){
+ setRenderState(D3DRS_ALPHABLENDENABLE, enable);
+ setRenderState(D3DRS_ALPHATESTENABLE, enable);
+ }
+ rwStateCache.vertexAlpha = enable;
+ }
+}
+
+void
+setRasterStage(uint32 stage, Raster *raster)
+{
+ bool32 alpha;
+ D3dRaster *d3draster = nil;
+ if(raster != rwStateCache.texstage[stage].raster){
+ rwStateCache.texstage[stage].raster = raster;
+ if(raster){
+ assert(raster->platform == PLATFORM_D3D8 ||
+ raster->platform == PLATFORM_D3D9);
+ d3draster = GETD3DRASTEREXT(raster);
+ d3ddevice->SetTexture(stage, (IDirect3DTexture9*)d3draster->texture);
+ alpha = d3draster->hasAlpha;
+ }else{
+ d3ddevice->SetTexture(stage, whiteTex);
+ alpha = 0;
+ }
+ if(stage == 0){
+ if(rwStateCache.textureAlpha != alpha){
+ rwStateCache.textureAlpha = alpha;
+ if(!rwStateCache.vertexAlpha){
+ setRenderState(D3DRS_ALPHABLENDENABLE, alpha);
+ setRenderState(D3DRS_ALPHATESTENABLE, alpha);
+ }
+ }
+ }
+ }
+}
+
+static void
+setFilterMode(uint32 stage, int32 filter, int32 maxAniso = 1)
+{
+ if(rwStateCache.texstage[stage].filter != (Texture::FilterMode)filter){
+ rwStateCache.texstage[stage].filter = (Texture::FilterMode)filter;
+ setSamplerState(stage, D3DSAMP_MAGFILTER, filterConvMap[filter]);
+ if(maxAniso == 1)
+ setSamplerState(stage, D3DSAMP_MINFILTER, filterConvMap[filter]);
+ else
+ setSamplerState(stage, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
+ setSamplerState(stage, D3DSAMP_MIPFILTER, filterConvMap_MIP[filter]);
+ }
+ if(rwStateCache.texstage[stage].maxAniso != maxAniso){
+ rwStateCache.texstage[stage].maxAniso = maxAniso;
+ if(maxAniso == 1)
+ setSamplerState(stage, D3DSAMP_MINFILTER, filterConvMap[filter]);
+ else
+ setSamplerState(stage, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
+ setSamplerState(stage, D3DSAMP_MAXANISOTROPY, maxAniso);
+ }
+}
+
+static void
+setAddressU(uint32 stage, int32 addressing)
+{
+ if(rwStateCache.texstage[stage].addressingU != (Texture::Addressing)addressing){
+ rwStateCache.texstage[stage].addressingU = (Texture::Addressing)addressing;
+ setSamplerState(stage, D3DSAMP_ADDRESSU, addressConvMap[addressing]);
+ }
+}
+
+static void
+setAddressV(uint32 stage, int32 addressing)
+{
+ if(rwStateCache.texstage[stage].addressingV != (Texture::Addressing)addressing){
+ rwStateCache.texstage[stage].addressingV = (Texture::Addressing)addressing;
+ setSamplerState(stage, D3DSAMP_ADDRESSV, addressConvMap[addressing]);
+ }
+}
+
+void
+setTexture(uint32 stage, Texture *tex)
+{
+ if(tex == nil){
+ setRasterStage(stage, nil);
+ return;
+ }
+ if(tex->raster){
+ setFilterMode(stage, tex->getFilter(), tex->getMaxAnisotropy());
+ setAddressU(stage, tex->getAddressU());
+ setAddressV(stage, tex->getAddressV());
+ }
+ setRasterStage(stage, tex->raster);
+}
+
+void
+setD3dMaterial(D3DMATERIAL9 *mat9)
+{
+ if(d3dmaterial.Diffuse.r != mat9->Diffuse.r ||
+ d3dmaterial.Diffuse.g != mat9->Diffuse.g ||
+ d3dmaterial.Diffuse.b != mat9->Diffuse.b ||
+ d3dmaterial.Diffuse.a != mat9->Diffuse.a ||
+ d3dmaterial.Ambient.r != mat9->Ambient.r ||
+ d3dmaterial.Ambient.g != mat9->Ambient.g ||
+ d3dmaterial.Ambient.b != mat9->Ambient.b ||
+ d3dmaterial.Ambient.a != mat9->Ambient.a ||
+ d3dmaterial.Specular.r != mat9->Specular.r ||
+ d3dmaterial.Specular.g != mat9->Specular.g ||
+ d3dmaterial.Specular.b != mat9->Specular.b ||
+ d3dmaterial.Specular.a != mat9->Specular.a ||
+ d3dmaterial.Emissive.r != mat9->Emissive.r ||
+ d3dmaterial.Emissive.g != mat9->Emissive.g ||
+ d3dmaterial.Emissive.b != mat9->Emissive.b ||
+ d3dmaterial.Emissive.a != mat9->Emissive.a ||
+ d3dmaterial.Power != mat9->Power){
+ d3ddevice->SetMaterial(mat9);
+ d3dmaterial = *mat9;
+ }
+}
+
+void
+setMaterial_fix(const RGBA &color, const SurfaceProperties &surfProps)
+{
+ D3DMATERIAL9 mat9;
+ D3DCOLORVALUE black = { 0, 0, 0, 0 };
+ float ambmult = surfProps.ambient/255.0f;
+ float diffmult = surfProps.diffuse/255.0f;
+ mat9.Ambient.r = color.red*ambmult;
+ mat9.Ambient.g = color.green*ambmult;
+ mat9.Ambient.b = color.blue*ambmult;
+ mat9.Ambient.a = color.alpha;
+ mat9.Diffuse.r = color.red*diffmult;
+ mat9.Diffuse.g = color.green*diffmult;
+ mat9.Diffuse.b = color.blue*diffmult;
+ mat9.Diffuse.a = color.alpha;
+ mat9.Power = 0.0f;
+ mat9.Emissive = black;
+ mat9.Specular = black;
+ setD3dMaterial(&mat9);
+}
+
+
+void
+setMaterial(const RGBA &color, const SurfaceProperties &surfaceprops, float extraSurfProp)
+{
+ if(!equal(d3dShaderState.matColor, color)){
+ rw::RGBAf col;
+ convColor(&col, &color);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_matColor, (float*)&col, 1);
+ d3dShaderState.matColor = color;
+ }
+
+ if(d3dShaderState.surfProps.ambient != surfaceprops.ambient ||
+ d3dShaderState.surfProps.specular != surfaceprops.specular ||
+ d3dShaderState.surfProps.diffuse != surfaceprops.diffuse ||
+ d3dShaderState.extraSurfProp != extraSurfProp){
+ float surfProps[4];
+ surfProps[0] = surfaceprops.ambient;
+ surfProps[1] = surfaceprops.specular;
+ surfProps[2] = surfaceprops.diffuse;
+ surfProps[3] = extraSurfProp;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_surfProps, surfProps, 1);
+ d3dShaderState.surfProps = surfaceprops;
+ d3dShaderState.extraSurfProp = extraSurfProp;
+ }
+}
+
+static void
+setRwRenderState(int32 state, void *pvalue)
+{
+ uint32 value = (uint32)(uintptr)pvalue;
+ uint32 bval = value ? TRUE : FALSE;
+ switch(state){
+ case TEXTURERASTER:
+ setRasterStage(0, (Raster*)pvalue);
+ break;
+ case TEXTUREADDRESS:
+ setAddressU(0, value);
+ setAddressV(0, value);
+ break;
+ case TEXTUREADDRESSU:
+ setAddressU(0, value);
+ break;
+ case TEXTUREADDRESSV:
+ setAddressV(0, value);
+ break;
+ case TEXTUREFILTER:
+ setFilterMode(0, value);
+ break;
+ case VERTEXALPHA:
+ setVertexAlpha(bval);
+ break;
+ case SRCBLEND:
+ if(rwStateCache.srcblend != value){
+ rwStateCache.srcblend = value;
+ setRenderState(D3DRS_SRCBLEND, blendMap[value]);
+ }
+ break;
+ case DESTBLEND:
+ if(rwStateCache.destblend != value){
+ rwStateCache.destblend = value;
+ setRenderState(D3DRS_DESTBLEND, blendMap[value]);
+ }
+ break;
+ case ZTESTENABLE:
+ setDepthTest(bval);
+ break;
+ case ZWRITEENABLE:
+ setDepthWrite(bval);
+ break;
+ case FOGENABLE:
+ if(rwStateCache.fogenable != bval){
+ rwStateCache.fogenable = bval;
+// setRenderState(D3DRS_FOGENABLE, rwStateCache.fogenable);
+ d3dShaderState.fogData.disable = bval ? 0.0f : 1.0f;
+ d3dShaderState.fogDirty = true;
+ };
+ break;
+ case FOGCOLOR:{
+ RGBA c;
+ c.red = value;
+ c.green = value>>8;
+ c.blue = value>>16;
+ c.alpha = value>>24;
+ if(!equal(rwStateCache.fogcolor, c)){
+ rwStateCache.fogcolor = c;
+ setRenderState(D3DRS_FOGCOLOR, D3DCOLOR_RGBA(c.red, c.green, c.blue, c.alpha));
+ convColor(&d3dShaderState.fogColor, &c);
+ d3dShaderState.fogDirty = true;
+ }} break;
+ case CULLMODE:
+ if(rwStateCache.cullmode != value){
+ rwStateCache.cullmode = value;
+ setRenderState(D3DRS_CULLMODE, cullmodeMap[value]);
+ }
+ break;
+
+ case STENCILENABLE:
+ if(rwStateCache.stencilenable != bval){
+ rwStateCache.stencilenable = bval;
+ setRenderState(D3DRS_STENCILENABLE, bval);
+ }
+ break;
+ case STENCILFAIL:
+ if(rwStateCache.stencilfail != value){
+ rwStateCache.stencilfail = value;
+ setRenderState(D3DRS_STENCILFAIL, stencilOpMap[value]);
+ }
+ break;
+ case STENCILZFAIL:
+ if(rwStateCache.stencilzfail != value){
+ rwStateCache.stencilzfail = value;
+ setRenderState(D3DRS_STENCILZFAIL, stencilOpMap[value]);
+ }
+ break;
+ case STENCILPASS:
+ if(rwStateCache.stencilpass != value){
+ rwStateCache.stencilpass = value;
+ setRenderState(D3DRS_STENCILPASS, stencilOpMap[value]);
+ }
+ break;
+ case STENCILFUNCTION:
+ if(rwStateCache.stencilfunc != value){
+ rwStateCache.stencilfunc = value;
+ setRenderState(D3DRS_STENCILFUNC, stencilFuncMap[value]);
+ }
+ break;
+ case STENCILFUNCTIONREF:
+ if(rwStateCache.stencilref != value){
+ rwStateCache.stencilref = value;
+ setRenderState(D3DRS_STENCILREF, value);
+ }
+ break;
+ case STENCILFUNCTIONMASK:
+ if(rwStateCache.stencilmask != value){
+ rwStateCache.stencilmask = value;
+ setRenderState(D3DRS_STENCILMASK, value);
+ }
+ break;
+ case STENCILFUNCTIONWRITEMASK:
+ if(rwStateCache.stencilwritemask != value){
+ rwStateCache.stencilwritemask = value;
+ setRenderState(D3DRS_STENCILWRITEMASK, value);
+ }
+ break;
+
+ case ALPHATESTFUNC:
+ if(rwStateCache.alphafunc != value){
+ rwStateCache.alphafunc = value;
+ setRenderState(D3DRS_ALPHAFUNC, alphafuncMap[rwStateCache.alphafunc]);
+ }
+ break;
+ case ALPHATESTREF:
+ if(rwStateCache.alpharef != value){
+ rwStateCache.alpharef = value;
+ setRenderState(D3DRS_ALPHAREF, rwStateCache.alpharef);
+ }
+ break;
+ case GSALPHATEST:
+ rwStateCache.gsalpha = value;
+ break;
+ case GSALPHATESTREF:
+ rwStateCache.gsalpharef = value;
+ break;
+ }
+}
+
+static void*
+getRwRenderState(int32 state)
+{
+ uint32 val;
+ switch(state){
+ case TEXTURERASTER:
+ return rwStateCache.texstage[0].raster;
+ case TEXTUREADDRESS:
+ if(rwStateCache.texstage[0].addressingU == rwStateCache.texstage[0].addressingV)
+ val = rwStateCache.texstage[0].addressingU;
+ else
+ val = 0; // invalid
+ break;
+ case TEXTUREADDRESSU:
+ val = rwStateCache.texstage[0].addressingU;
+ break;
+ case TEXTUREADDRESSV:
+ val = rwStateCache.texstage[0].addressingV;
+ break;
+ case TEXTUREFILTER:
+ val = rwStateCache.texstage[0].filter;
+ break;
+
+ case VERTEXALPHA:
+ val = rwStateCache.vertexAlpha;
+ break;
+ case SRCBLEND:
+ val = rwStateCache.srcblend;
+ break;
+ case DESTBLEND:
+ val = rwStateCache.destblend;
+ break;
+ case ZTESTENABLE:
+ val = rwStateCache.ztest;
+ break;
+ case ZWRITEENABLE:
+ val = rwStateCache.zwrite;
+ break;
+ case FOGENABLE:
+ val = rwStateCache.fogenable;
+ break;
+ case FOGCOLOR:
+ val = RWRGBAINT(rwStateCache.fogcolor.red, rwStateCache.fogcolor.green,
+ rwStateCache.fogcolor.blue, rwStateCache.fogcolor.alpha);
+ break;
+ case CULLMODE:
+ val = rwStateCache.cullmode;
+ break;
+
+ case STENCILENABLE:
+ val = rwStateCache.stencilenable;
+ break;
+ case STENCILFAIL:
+ val = rwStateCache.stencilfail;
+ break;
+ case STENCILZFAIL:
+ val = rwStateCache.stencilzfail;
+ break;
+ case STENCILPASS:
+ val = rwStateCache.stencilpass;
+ break;
+ case STENCILFUNCTION:
+ val = rwStateCache.stencilfunc;
+ break;
+ case STENCILFUNCTIONREF:
+ val = rwStateCache.stencilref;
+ break;
+ case STENCILFUNCTIONMASK:
+ val = rwStateCache.stencilmask;
+ break;
+ case STENCILFUNCTIONWRITEMASK:
+ val = rwStateCache.stencilwritemask;
+ break;
+
+ case ALPHATESTFUNC:
+ val = rwStateCache.alphafunc;
+ break;
+ case ALPHATESTREF:
+ val = rwStateCache.alpharef;
+ break;
+ case GSALPHATEST:
+ val = rwStateCache.gsalpha;
+ break;
+ case GSALPHATESTREF:
+ val = rwStateCache.gsalpharef;
+ break;
+ default:
+ val = 0;
+ }
+ return (void*)(uintptr)val;
+}
+
+// Shaders
+
+void
+setVertexShader(void *vs)
+{
+ if(deviceCache.vertexShader != vs){
+ deviceCache.vertexShader = (IDirect3DVertexShader9*)vs;
+ d3ddevice->SetVertexShader(deviceCache.vertexShader);
+ }
+}
+
+void
+setPixelShader(void *ps)
+{
+ if(deviceCache.pixelShader != ps){
+ deviceCache.pixelShader = (IDirect3DPixelShader9*)ps;
+ d3ddevice->SetPixelShader(deviceCache.pixelShader);
+ }
+}
+
+void
+setIndices(void *indexBuffer)
+{
+ if(deviceCache.indices != indexBuffer){
+ deviceCache.indices = (IDirect3DIndexBuffer9*)indexBuffer;
+ d3ddevice->SetIndices(deviceCache.indices);
+ }
+}
+
+void
+setStreamSource(int n, void *buffer, uint32 offset, uint32 stride)
+{
+ if(deviceCache.vertexStreams[n].buffer != buffer ||
+ deviceCache.vertexStreams[n].offset != offset ||
+ deviceCache.vertexStreams[n].stride != stride){
+ deviceCache.vertexStreams[n].buffer = (IDirect3DVertexBuffer9*)buffer;
+ deviceCache.vertexStreams[n].offset = offset;
+ deviceCache.vertexStreams[n].stride = stride;
+ d3ddevice->SetStreamSource(n, deviceCache.vertexStreams[n].buffer, offset, stride);
+ }
+}
+
+void
+setVertexDeclaration(void *declaration)
+{
+ if(deviceCache.vertexDeclaration != declaration){
+ deviceCache.vertexDeclaration = (IDirect3DVertexDeclaration9*)declaration;
+ d3ddevice->SetVertexDeclaration(deviceCache.vertexDeclaration);
+ }
+}
+
+void*
+createVertexShader(void *csosrc)
+{
+ void *shdr;
+ if(d3ddevice->CreateVertexShader((DWORD*)csosrc, (IDirect3DVertexShader9**)&shdr) == D3D_OK){
+ d3d9Globals.numVertexShaders++;
+ return shdr;
+ }
+ return nil;
+}
+
+void*
+createPixelShader(void *csosrc)
+{
+ void *shdr;
+ if(d3ddevice->CreatePixelShader((DWORD*)csosrc, (IDirect3DPixelShader9**)&shdr) == D3D_OK){
+ d3d9Globals.numPixelShaders++;
+ return shdr;
+ }
+ return nil;
+}
+
+void
+destroyVertexShader(void *shader)
+{
+ if(shader){
+ ((IDirect3DVertexShader9*)shader)->Release();
+ d3d9Globals.numVertexShaders--;
+ }
+}
+
+void
+destroyPixelShader(void *shader)
+{
+ if(shader){
+ ((IDirect3DPixelShader9*)shader)->Release();
+ d3d9Globals.numPixelShaders--;
+ }
+}
+
+
+// Camera
+
+static void
+setRenderSurfaces(Camera *cam)
+{
+ Raster *fbuf = cam->frameBuffer;
+ assert(fbuf);
+ {
+ if(fbuf->parent)
+ fbuf = fbuf->parent;
+
+ D3dRaster *natras = GETD3DRASTEREXT(fbuf);
+ assert(fbuf->type == Raster::CAMERA || fbuf->type == Raster::CAMERATEXTURE);
+ if(natras->texture == nil)
+ setRenderTarget(0, d3d9Globals.defaultRenderTarget);
+ else{
+ assert(fbuf->type == Raster::CAMERATEXTURE);
+ IDirect3DSurface9 *surf;
+ ((IDirect3DTexture9*)natras->texture)->GetSurfaceLevel(0, &surf);
+ setRenderTarget(0, surf);
+ surf->Release();
+ }
+ }
+
+ Raster *zbuf = cam->zBuffer;
+ if(zbuf){
+ if(zbuf->parent)
+ zbuf = zbuf->parent;
+
+ D3dRaster *natras = GETD3DRASTEREXT(zbuf);
+ assert(zbuf->type == Raster::ZBUFFER);
+ setDepthSurface(natras->texture);
+ }else
+ setDepthSurface(nil);
+
+}
+
+static void
+setViewport(Raster *fb)
+{
+ D3DVIEWPORT9 vp;
+ vp.MinZ = 0.0f;
+ vp.MaxZ = 1.0f;
+ vp.X = fb->offsetX;
+ vp.Y = fb->offsetY;
+ vp.Width = fb->width;
+ vp.Height = fb->height;
+ d3ddevice->SetViewport(&vp);
+}
+// Manage video memory
+
+void
+addVidmemRaster(Raster *raster)
+{
+ VidmemRaster *vmr = rwNewT(VidmemRaster, 1, ID_DRIVER | MEMDUR_EVENT);
+ vmr->raster = raster;
+ vmr->next = vidmemRasters;
+ vidmemRasters = vmr;
+}
+
+void
+removeVidmemRaster(Raster *raster)
+{
+ VidmemRaster **p, *vmr;
+ for(p = &vidmemRasters; *p; p = &(*p)->next)
+ if((*p)->raster == raster)
+ goto found;
+ return;
+found:
+ vmr = *p;
+ *p = vmr->next;
+ rwFree(vmr);
+}
+
+static void
+releaseVidmemRasters(void)
+{
+ VidmemRaster *vmr;
+ Raster *raster;
+ D3dRaster *natras;
+ for(vmr = vidmemRasters; vmr; vmr = vmr->next){
+ raster = vmr->raster;
+ natras = GETD3DRASTEREXT(raster);
+ switch(raster->type){
+ case Raster::CAMERATEXTURE:
+ destroyTexture(natras->texture);
+ natras->texture = nil;
+ break;
+
+ case Raster::ZBUFFER:
+ // we'll leave the default surface dangling so we can tell the difference
+ if(natras->texture != d3d9Globals.defaultDepthSurf){
+ ((IDirect3DSurface9*)natras->texture)->Release();
+ natras->texture = nil;
+ }
+ break;
+ }
+ }
+}
+
+static void
+recreateVidmemRasters(void)
+{
+ VidmemRaster *vmr;
+ Raster *raster;
+ D3dRaster *natras;
+ for(vmr = vidmemRasters; vmr; vmr = vmr->next){
+ raster = vmr->raster;
+ natras = GETD3DRASTEREXT(raster);
+ switch(raster->type){
+ case Raster::CAMERATEXTURE: {
+ int32 levels = Raster::calculateNumLevels(raster->width, raster->height);
+ IDirect3DTexture9 *tex = nil;
+ d3ddevice->CreateTexture(raster->width, raster->height,
+ raster->format & Raster::MIPMAP ? levels : 1,
+ D3DUSAGE_RENDERTARGET,
+ (D3DFORMAT)natras->format, D3DPOOL_DEFAULT, &tex, nil);
+ natras->texture = tex;
+ if(natras->texture)
+ d3d9Globals.numTextures++;
+ break;
+ }
+
+ case Raster::ZBUFFER:
+ if(natras->texture){
+ RECT rect;
+ GetClientRect(d3d9Globals.window, &rect);
+ raster->width = rect.right;
+ raster->height = rect.bottom;
+ natras->texture = d3d9Globals.defaultDepthSurf;
+ natras->format = d3d9Globals.present.AutoDepthStencilFormat;
+ raster->depth = findFormatDepth(natras->format);
+ }else{
+ IDirect3DSurface9 *surf = nil;
+ d3ddevice->CreateDepthStencilSurface(raster->width, raster->height, (D3DFORMAT)natras->format,
+ d3d9Globals.present.MultiSampleType, d3d9Globals.present.MultiSampleQuality,
+ FALSE, &surf, nil);
+ natras->texture = surf;
+ }
+ break;
+ }
+ }
+}
+
+void
+addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf)
+{
+ DynamicVB *dvb = rwNewT(DynamicVB, 1, ID_DRIVER | MEMDUR_EVENT);
+ dvb->length = length;
+ dvb->fvf = fvf;
+ dvb->buf = buf;
+ dvb->next = dynamicVBs;
+ dynamicVBs = dvb;
+}
+
+void
+removeDynamicVB(IDirect3DVertexBuffer9 **buf)
+{
+ DynamicVB **p, *dvb;
+ for(p = &dynamicVBs; *p; p = &(*p)->next)
+ if((*p)->buf == buf)
+ goto found;
+ return;
+found:
+ dvb = *p;
+ *p = dvb->next;
+ rwFree(dvb);
+}
+
+static void
+releaseDynamicVBs(void)
+{
+ DynamicVB *dvb;
+ int i;
+ for(dvb = dynamicVBs; dvb; dvb = dvb->next){
+ for(i = 0; i < MAXNUMSTREAMS; i++)
+ if(deviceCache.vertexStreams[i].buffer == *dvb->buf)
+ deviceCache.vertexStreams[i].buffer = nil;
+ destroyVertexBuffer(*dvb->buf);
+ *dvb->buf = nil;
+ }
+}
+
+static void
+recreateDynamicVBs(void)
+{
+ DynamicVB *dvb;
+ for(dvb = dynamicVBs; dvb; dvb = dvb->next){
+ assert(*dvb->buf == nil);
+ *dvb->buf = (IDirect3DVertexBuffer9*)createVertexBuffer(dvb->length, dvb->fvf, true);
+ }
+}
+
+
+void
+addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf)
+{
+ DynamicIB *ivb = rwNewT(DynamicIB, 1, ID_DRIVER | MEMDUR_EVENT);
+ ivb->length = length;
+ ivb->buf = buf;
+ ivb->next = dynamicIBs;
+ dynamicIBs = ivb;
+}
+
+void
+removeDynamicIB(IDirect3DIndexBuffer9 **buf)
+{
+ DynamicIB **p, *ivb;
+ for(p = &dynamicIBs; *p; p = &(*p)->next)
+ if((*p)->buf == buf)
+ goto found;
+ return;
+found:
+ ivb = *p;
+ *p = ivb->next;
+ rwFree(ivb);
+}
+
+static void
+releaseDynamicIBs(void)
+{
+ DynamicIB *ivb;
+ for(ivb = dynamicIBs; ivb; ivb = ivb->next){
+ if(deviceCache.indices == *ivb->buf)
+ deviceCache.indices = nil;
+ destroyIndexBuffer(*ivb->buf);
+ *ivb->buf = nil;
+ }
+}
+
+static void
+recreateDynamicIBs(void)
+{
+ DynamicIB *ivb;
+ for(ivb = dynamicIBs; ivb; ivb = ivb->next){
+ assert(*ivb->buf == nil);
+ *ivb->buf = (IDirect3DIndexBuffer9*)createIndexBuffer(ivb->length, true);
+ }
+}
+
+static void
+releaseVideoMemory(void)
+{
+ int32 i;
+ for(i = 0; i < MAXNUMSTAGES; i++)
+ d3ddevice->SetTexture(i, nil);
+ d3ddevice->SetVertexDeclaration(nil);
+ d3ddevice->SetVertexShader(nil);
+ d3ddevice->SetPixelShader(nil);
+ d3ddevice->SetIndices(nil);
+ for(i = 0; i < MAXNUMSTREAMS; i++)
+ d3ddevice->SetStreamSource(0, nil, 0, 0);
+
+ setRenderTarget(0, d3d9Globals.defaultRenderTarget);
+ for(i = 1; i < MAXNUMRENDERTARGETS; i++)
+ setRenderTarget(i, nil);
+ setDepthSurface(d3d9Globals.defaultDepthSurf);
+
+ releaseVidmemRasters();
+ releaseDynamicVBs();
+ releaseDynamicIBs();
+}
+
+static void
+restoreVideoMemory(void)
+{
+ // Have to get these back before recreating rasters
+ d3ddevice->GetRenderTarget(0, &d3d9Globals.defaultRenderTarget);
+ d3d9Globals.defaultRenderTarget->Release(); // refcount increased by Get
+ deviceCache.renderTargets[0] = d3d9Globals.defaultRenderTarget;
+ d3ddevice->GetDepthStencilSurface(&d3d9Globals.defaultDepthSurf);
+ d3d9Globals.defaultDepthSurf->Release(); // refcount increased by Get
+ deviceCache.depthSurface = d3d9Globals.defaultDepthSurf;
+
+ recreateDynamicIBs();
+ recreateDynamicVBs();
+ // important that we get all raster back before restoring state
+ recreateVidmemRasters();
+
+ restoreD3d9Device();
+}
+
+static void
+beginUpdate(Camera *cam)
+{
+ float view[16], proj[16];
+
+ // View Matrix
+ Matrix inv;
+ Matrix::invert(&inv, cam->getFrame()->getLTM());
+ // Since we're looking into positive Z,
+ // flip X to ge a left handed view space.
+ view[0] = -inv.right.x;
+ view[1] = inv.right.y;
+ view[2] = inv.right.z;
+ view[3] = 0.0f;
+ view[4] = -inv.up.x;
+ view[5] = inv.up.y;
+ view[6] = inv.up.z;
+ view[7] = 0.0f;
+ view[8] = -inv.at.x;
+ view[9] = inv.at.y;
+ view[10] = inv.at.z;
+ view[11] = 0.0f;
+ view[12] = -inv.pos.x;
+ view[13] = inv.pos.y;
+ view[14] = inv.pos.z;
+ view[15] = 1.0f;
+ memcpy(&cam->devView, view, sizeof(RawMatrix));
+// d3ddevice->SetTransform(D3DTS_VIEW, (D3DMATRIX*)view);
+
+ // Projection Matrix
+ float32 invwx = 1.0f/cam->viewWindow.x;
+ float32 invwy = 1.0f/cam->viewWindow.y;
+ float32 invz = 1.0f/(cam->farPlane-cam->nearPlane);
+
+ proj[0] = invwx;
+ proj[1] = 0.0f;
+ proj[2] = 0.0f;
+ proj[3] = 0.0f;
+
+ proj[4] = 0.0f;
+ proj[5] = invwy;
+ proj[6] = 0.0f;
+ proj[7] = 0.0f;
+
+ proj[8] = cam->viewOffset.x*invwx;
+ proj[9] = cam->viewOffset.y*invwy;
+ proj[12] = -proj[8];
+ proj[13] = -proj[9];
+ if(cam->projection == Camera::PERSPECTIVE){
+ proj[10] = cam->farPlane*invz;
+ proj[11] = 1.0f;
+
+ proj[15] = 0.0f;
+ }else{
+ proj[10] = invz;
+ proj[11] = 0.0f;
+
+ proj[15] = 1.0f;
+ }
+ proj[14] = -cam->nearPlane*proj[10];
+ memcpy(&cam->devProj, proj, sizeof(RawMatrix));
+// d3ddevice->SetTransform(D3DTS_PROJECTION, (D3DMATRIX*)proj);
+
+ // TODO: figure out where this is really done
+// setRenderState(D3DRS_FOGSTART, *(uint32*)&cam->fogPlane);
+// setRenderState(D3DRS_FOGEND, *(uint32*)&cam->farPlane);
+ d3dShaderState.fogData.start = cam->fogPlane;
+ d3dShaderState.fogData.end = cam->farPlane;
+ d3dShaderState.fogData.range = 1.0f/(cam->fogPlane - cam->farPlane);
+ // TODO: not quite sure this is the right place to do this...
+ d3dShaderState.fogData.disable = rwStateCache.fogenable ? 0.0f : 1.0f;
+ d3dShaderState.fogDisable.start = 0.0f;
+ d3dShaderState.fogDisable.end = 0.0f;
+ d3dShaderState.fogDisable.range = 0.0f;
+ d3dShaderState.fogDisable.disable = 1.0f;
+ d3dShaderState.fogDirty = true;
+
+ RECT r;
+ GetClientRect(d3d9Globals.window, &r);
+ BOOL icon = IsIconic(d3d9Globals.window);
+ if(!icon &&
+ (r.right != d3d9Globals.present.BackBufferWidth || r.bottom != d3d9Globals.present.BackBufferHeight)){
+
+ d3d9Globals.present.BackBufferWidth = r.right;
+ d3d9Globals.present.BackBufferHeight = r.bottom;
+
+ releaseVideoMemory();
+ d3d::d3ddevice->Reset(&d3d9Globals.present);
+ restoreVideoMemory();
+ }
+
+ setRenderSurfaces(cam);
+
+ setViewport(cam->frameBuffer);
+
+ d3ddevice->BeginScene();
+}
+
+static void
+endUpdate(Camera *cam)
+{
+ d3ddevice->EndScene();
+}
+
+static void
+clearCamera(Camera *cam, RGBA *col, uint32 mode)
+{
+ int flags = 0;
+ if(mode & Camera::CLEARIMAGE)
+ mode |= D3DCLEAR_TARGET;
+ if(mode & Camera::CLEARZ)
+ mode |= D3DCLEAR_ZBUFFER;
+ if(mode & Camera::CLEARSTENCIL)
+ mode |= D3DCLEAR_STENCIL;
+ D3DCOLOR c = D3DCOLOR_RGBA(col->red, col->green, col->blue, col->alpha);
+
+ setRenderSurfaces(cam);
+
+ setViewport(cam->frameBuffer); // need to set this for the clear to work correctly
+ d3ddevice->Clear(0, nil, mode, c, 1.0f, 0);
+}
+
+static void
+showRaster(Raster *raster, uint32 flag)
+{
+ UINT interval = flag & Raster::FLIPWAITVSYNCH ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
+ if(d3d9Globals.present.PresentationInterval != interval){
+ d3d9Globals.present.PresentationInterval = interval;
+ releaseVideoMemory();
+ d3d::d3ddevice->Reset(&d3d9Globals.present);
+ restoreVideoMemory();
+ }
+
+ // not used but we want cameras to have rasters
+ assert(raster);
+ HRESULT res = d3ddevice->Present(nil, nil, 0, nil);
+
+ if(res == D3DERR_DEVICELOST){
+ res = d3ddevice->TestCooperativeLevel();
+ // lost while being minimized, not reset once we're back
+ if(res == D3DERR_DEVICENOTRESET){
+ releaseVideoMemory();
+ d3d::d3ddevice->Reset(&d3d9Globals.present);
+ restoreVideoMemory();
+ }
+ }
+}
+
+static bool32
+rasterRenderFast(Raster *raster, int32 x, int32 y)
+{
+ IDirect3DTexture9 *dsttex;
+ IDirect3DSurface9 *srcsurf, *dstsurf;
+ D3DSURFACE_DESC srcdesc, dstdesc;
+ RECT rect = { x, y, x, y };
+
+ Raster *src = raster;
+ Raster *dst = Raster::getCurrentContext();
+ D3dRaster *natdst = GETD3DRASTEREXT(dst);
+ D3dRaster *natsrc = GETD3DRASTEREXT(src);
+
+ switch(dst->type){
+ case Raster::CAMERATEXTURE:
+ switch(src->type){
+ case Raster::CAMERA:
+ dsttex = (IDirect3DTexture9*)natdst->texture;
+ dsttex->GetSurfaceLevel(0, &dstsurf);
+ assert(dstsurf);
+ dstsurf->GetDesc(&dstdesc);
+
+ d3ddevice->GetRenderTarget(0, &srcsurf);
+ assert(srcsurf);
+ srcsurf->GetDesc(&srcdesc);
+
+ rect.right += srcdesc.Width;
+ rect.bottom += srcdesc.Height;
+
+ d3ddevice->StretchRect(srcsurf, &rect, dstsurf, &rect, D3DTEXF_NONE);
+ dstsurf->Release();
+ srcsurf->Release();
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
+//
+// Device
+//
+
+int
+findFormatDepth(uint32 format)
+{
+ // not all formats actually
+ switch(format){
+ case D3DFMT_R8G8B8: return 24;
+ case D3DFMT_A8R8G8B8: return 32;
+ case D3DFMT_X8R8G8B8: return 32;
+ case D3DFMT_R5G6B5: return 16;
+ case D3DFMT_X1R5G5B5: return 16;
+ case D3DFMT_A1R5G5B5: return 16;
+ case D3DFMT_A4R4G4B4: return 16;
+ case D3DFMT_R3G3B2: return 8;
+ case D3DFMT_A8: return 8;
+ case D3DFMT_A8R3G3B2: return 16;
+ case D3DFMT_X4R4G4B4: return 16;
+ case D3DFMT_A2B10G10R10: return 32;
+ case D3DFMT_A8B8G8R8: return 32;
+ case D3DFMT_X8B8G8R8: return 32;
+ case D3DFMT_G16R16: return 32;
+ case D3DFMT_A2R10G10B10: return 32;
+ case D3DFMT_A16B16G16R16: return 64;
+
+ case D3DFMT_L8: return 8;
+ case D3DFMT_D16: return 16;
+ case D3DFMT_D24S8: return 32;
+ case D3DFMT_D24X8: return 32;
+ case D3DFMT_D24X4S4: return 32;
+ case D3DFMT_D32: return 32;
+
+ default: return 0;
+ }
+}
+
+// the commented ones don't "work"
+static D3DFORMAT fbFormats[] = {
+// D3DFMT_A1R5G5B5,
+/// D3DFMT_A2R10G10B10, // works but let's not use it...
+// D3DFMT_A8R8G8B8,
+ D3DFMT_X8R8G8B8,
+// D3DFMT_X1R5G5B5,
+ D3DFMT_R5G6B5
+};
+
+static void
+addVideoMode(D3DDISPLAYMODE *mode)
+{
+ int i;
+
+ for(i = 1; i < d3d9Globals.numModes; i++){
+ if(d3d9Globals.modes[i].mode.Width == mode->Width &&
+ d3d9Globals.modes[i].mode.Height == mode->Height &&
+ d3d9Globals.modes[i].mode.Format == mode->Format){
+ // had this format already, remember highest refresh rate
+ if(mode->RefreshRate > d3d9Globals.modes[i].mode.RefreshRate)
+ d3d9Globals.modes[i].mode.RefreshRate = mode->RefreshRate;
+ return;
+ }
+ }
+
+ // none found, add
+ d3d9Globals.modes[d3d9Globals.numModes].mode = *mode;
+ d3d9Globals.modes[d3d9Globals.numModes].flags = VIDEOMODEEXCLUSIVE;
+ d3d9Globals.numModes++;
+}
+
+static void
+makeVideoModeList(void)
+{
+ int i, j;
+ D3DDISPLAYMODE mode;
+
+ d3d9Globals.numModes = 1;
+ for(i = 0; i < nelem(fbFormats); i++)
+ d3d9Globals.numModes += d3d9Globals.d3d9->GetAdapterModeCount(d3d9Globals.adapter, fbFormats[i]);
+
+ rwFree(d3d9Globals.modes);
+ d3d9Globals.modes = rwNewT(DisplayMode, d3d9Globals.numModes, ID_DRIVER | MEMDUR_EVENT);
+
+ // first mode is current mode as windowed
+ d3d9Globals.d3d9->GetAdapterDisplayMode(d3d9Globals.adapter, &d3d9Globals.modes[0].mode);
+ d3d9Globals.modes[0].flags = 0;
+ d3d9Globals.numModes = 1;
+
+ for(i = 0; i < nelem(fbFormats); i++){
+ int n = d3d9Globals.d3d9->GetAdapterModeCount(d3d9Globals.adapter, fbFormats[i]);
+ for(j = 0; j < n; j++){
+ d3d9Globals.d3d9->EnumAdapterModes(d3d9Globals.adapter, fbFormats[i], j, &mode);
+ addVideoMode(&mode);
+ }
+ }
+}
+
+static int
+openD3D(EngineOpenParams *params)
+{
+ HWND win = params->window;
+
+ d3d9Globals.window = win;
+ d3d9Globals.numAdapters = 0;
+ d3d9Globals.modes = nil;
+ d3d9Globals.numModes = 0;
+ d3d9Globals.currentMode = 0;
+
+ d3d9Globals.d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
+ if(d3d9Globals.d3d9 == nil){
+ RWERROR((ERR_GENERAL, "Direct3DCreate9() failed"));
+ return 0;
+ }
+
+ d3d9Globals.numAdapters = d3d9Globals.d3d9->GetAdapterCount();
+ d3d9Globals.adapter = 0;
+
+ for(d3d9Globals.adapter = 0; d3d9Globals.adapter < d3d9Globals.numAdapters; d3d9Globals.adapter++)
+ if(d3d9Globals.d3d9->GetDeviceCaps(d3d9Globals.adapter, D3DDEVTYPE_HAL, &d3d9Globals.caps) == D3D_OK)
+ goto found;
+ // no adapter
+ d3d9Globals.d3d9->Release();
+ d3d9Globals.d3d9 = nil;
+ RWERROR((ERR_GENERAL, "Direct3DCreate9() failed"));
+ return 0;
+
+found:
+ makeVideoModeList();
+ return 1;
+}
+
+static int
+closeD3D(void)
+{
+ ULONG ref = d3d9Globals.d3d9->Release();
+ if(ref != 0)
+ printf("IDirect3D9_Release did not destroy\n");
+ d3d9Globals.d3d9 = nil;
+ rwFree(d3d9Globals.modes);
+ d3d9Globals.modes = nil;
+ d3d9Globals.numModes = 0;
+ d3d9Globals.currentMode = 0;
+ return 1;
+}
+
+static int
+startD3D(void)
+{
+ HRESULT hr;
+ int vp;
+ if(d3d9Globals.caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
+ vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
+ else
+ vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
+
+ uint32 width, height, depth;
+ D3DFORMAT format, zformat;
+ d3d9Globals.startMode = d3d9Globals.modes[d3d9Globals.currentMode];
+ format = d3d9Globals.startMode.mode.Format;
+
+ bool windowed = !(d3d9Globals.startMode.flags & VIDEOMODEEXCLUSIVE);
+
+ // Use window size in windowed mode, otherwise get size from video mode
+ if(windowed){
+ RECT rect;
+ GetClientRect(d3d9Globals.window, &rect);
+ width = rect.right - rect.left;
+ height = rect.bottom - rect.top;
+ }else{
+ // this will be much better for restoring after iconification
+ SetWindowLong(d3d9Globals.window, GWL_STYLE, WS_POPUP);
+ width = d3d9Globals.startMode.mode.Width;
+ height = d3d9Globals.startMode.mode.Height;
+ }
+
+ // See if we can get an alpha channel
+ if(format == D3DFMT_X8R8G8B8){
+ if(d3d9Globals.d3d9->CheckDeviceType(d3d9Globals.adapter, D3DDEVTYPE_HAL, format, D3DFMT_A8R8G8B8, windowed) == D3D_OK)
+ format = D3DFMT_A8R8G8B8;
+ }
+
+ depth = findFormatDepth(format);
+
+ // TOOD: use something more sophisticated maybe?
+ if(depth == 32)
+ zformat = D3DFMT_D24S8;
+ else
+ zformat = D3DFMT_D16;
+
+ d3d9Globals.present.BackBufferWidth = width;
+ d3d9Globals.present.BackBufferHeight = height;
+ d3d9Globals.present.BackBufferFormat = format;
+ d3d9Globals.present.BackBufferCount = 1;
+ d3d9Globals.present.MultiSampleType = d3d9Globals.msLevel == 1 ?
+ D3DMULTISAMPLE_NONE : (D3DMULTISAMPLE_TYPE)d3d9Globals.msLevel;
+ d3d9Globals.present.MultiSampleQuality = 0;
+ d3d9Globals.present.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3d9Globals.present.hDeviceWindow = d3d9Globals.window;
+ d3d9Globals.present.Windowed = windowed;
+ d3d9Globals.present.EnableAutoDepthStencil = true;
+ d3d9Globals.present.AutoDepthStencilFormat = zformat;
+ d3d9Globals.present.Flags = 0;
+ d3d9Globals.present.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
+// d3d9Globals.present.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
+ d3d9Globals.present.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+
+ rw::d3d::isP8supported = 0;
+
+ assert(d3d::d3ddevice == nil);
+
+ BOOL icon = IsIconic(d3d9Globals.window);
+ IDirect3DDevice9 *dev;
+ hr = d3d9Globals.d3d9->CreateDevice(d3d9Globals.adapter, D3DDEVTYPE_HAL,
+ d3d9Globals.window, vp, &d3d9Globals.present, &dev);
+ if(FAILED(hr)){
+ RWERROR((ERR_GENERAL, "CreateDevice() failed"));
+ return 0;
+ }
+ d3d::d3ddevice = dev;
+ return 1;
+}
+
+static int
+initD3D(void)
+{
+ int32 s, t;
+
+ memset(&deviceCache, 0, sizeof(deviceCache));
+ d3ddevice->GetRenderTarget(0, &d3d9Globals.defaultRenderTarget);
+ d3d9Globals.defaultRenderTarget->Release(); // refcount increased by Get
+ deviceCache.renderTargets[0] = d3d9Globals.defaultRenderTarget;
+ d3ddevice->GetDepthStencilSurface(&d3d9Globals.defaultDepthSurf);
+ d3d9Globals.defaultDepthSurf->Release(); // refcount increased by Get
+ deviceCache.depthSurface = d3d9Globals.defaultDepthSurf;
+
+ d3d9Globals.numTextures = 0;
+ d3d9Globals.numVertexShaders = 0;
+ d3d9Globals.numPixelShaders = 0;
+ d3d9Globals.numVertexBuffers = 0;
+ d3d9Globals.numIndexBuffers = 0;
+ d3d9Globals.numVertexDeclarations = 0;
+
+ VertexConstantData constants;
+ constants.normal.x = 0.0f;
+ constants.normal.y = 0.0f;
+ constants.normal.z = 0.0f;
+ constants.color.red = 0;
+ constants.color.green = 0;
+ constants.color.blue = 0;
+ constants.color.alpha = 255;
+ for(s = 0; s < 8; s++){
+ constants.texCoors[s].u = 0.0f;
+ constants.texCoors[s].v = 0.0f;
+ }
+ assert(constantVertexStream == nil);
+ constantVertexStream = createVertexBuffer(sizeof(constants)*10000, 0, false);
+ assert(constantVertexStream);
+ uint8 *lockedvertices = lockVertices(constantVertexStream, 0, sizeof(constants), D3DLOCK_NOSYSLOCK);
+ assert(lockedvertices);
+ memcpy(lockedvertices, &constants, sizeof(constants));
+ unlockVertices(constantVertexStream);
+ setStreamSource(2, constantVertexStream, 0, 0);
+
+ d3ddevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
+ rwStateCache.alphafunc = ALPHAGREATEREQUAL;
+ d3ddevice->SetRenderState(D3DRS_ALPHAREF, 10);
+ rwStateCache.alpharef = 10;
+
+ rwStateCache.gsalpha = 0;
+ rwStateCache.gsalpharef = 128;
+
+ d3ddevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
+ rwStateCache.fogenable = 0;
+ d3ddevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_LINEAR);
+
+ d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+ rwStateCache.cullmode = CULLNONE;
+
+ d3ddevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ rwStateCache.srcblend = BLENDSRCALPHA;
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ rwStateCache.destblend = BLENDINVSRCALPHA;
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, 0);
+ rwStateCache.vertexAlpha = 0;
+ rwStateCache.textureAlpha = 0;
+
+ rwStateCache.stencilenable = 0;
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+ rwStateCache.stencilfail = STENCILKEEP;
+ d3ddevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
+ rwStateCache.stencilzfail = STENCILKEEP;
+ d3ddevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
+ rwStateCache.stencilpass = STENCILKEEP;
+ d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP);
+ rwStateCache.stencilfunc = STENCILALWAYS;
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS);
+ rwStateCache.stencilref = 0;
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0);
+ rwStateCache.stencilmask = 0xFFFFFFFF;
+ d3ddevice->SetRenderState(D3DRS_STENCILMASK, 0xFFFFFFFF);
+ rwStateCache.stencilwritemask = 0xFFFFFFFF;
+ d3ddevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xFFFFFFFF);
+
+ setTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+// setTextureStageState(0, D3DTSS_CONSTANT, 0xFFFFFFFF);
+// setTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+// setTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_CONSTANT);
+// setTextureStageState(0, D3DTSS_COLOROP, D3DTA_CONSTANT);
+
+ // These states exist, not all do
+ validStates[D3DRS_ZENABLE] = 1;
+ validStates[D3DRS_FILLMODE] = 1;
+ validStates[D3DRS_SHADEMODE] = 1;
+ validStates[D3DRS_ZWRITEENABLE] = 1;
+ validStates[D3DRS_ALPHATESTENABLE] = 1;
+ validStates[D3DRS_LASTPIXEL] = 1;
+ validStates[D3DRS_SRCBLEND] = 1;
+ validStates[D3DRS_DESTBLEND] = 1;
+ validStates[D3DRS_CULLMODE] = 1;
+ validStates[D3DRS_ZFUNC] = 1;
+ validStates[D3DRS_ALPHAREF] = 1;
+ validStates[D3DRS_ALPHAFUNC] = 1;
+ validStates[D3DRS_DITHERENABLE] = 1;
+ validStates[D3DRS_ALPHABLENDENABLE] = 1;
+ validStates[D3DRS_FOGENABLE] = 1;
+ validStates[D3DRS_SPECULARENABLE] = 1;
+ validStates[D3DRS_FOGCOLOR] = 1;
+ validStates[D3DRS_FOGTABLEMODE] = 1;
+ validStates[D3DRS_FOGSTART] = 1;
+ validStates[D3DRS_FOGEND] = 1;
+ validStates[D3DRS_FOGDENSITY] = 1;
+ validStates[D3DRS_RANGEFOGENABLE] = 1;
+ validStates[D3DRS_STENCILENABLE] = 1;
+ validStates[D3DRS_STENCILFAIL] = 1;
+ validStates[D3DRS_STENCILZFAIL] = 1;
+ validStates[D3DRS_STENCILPASS] = 1;
+ validStates[D3DRS_STENCILFUNC] = 1;
+ validStates[D3DRS_STENCILREF] = 1;
+ validStates[D3DRS_STENCILMASK] = 1;
+ validStates[D3DRS_STENCILWRITEMASK] = 1;
+ validStates[D3DRS_TEXTUREFACTOR] = 1;
+ validStates[D3DRS_WRAP0] = 1;
+ validStates[D3DRS_WRAP1] = 1;
+ validStates[D3DRS_WRAP2] = 1;
+ validStates[D3DRS_WRAP3] = 1;
+ validStates[D3DRS_WRAP4] = 1;
+ validStates[D3DRS_WRAP5] = 1;
+ validStates[D3DRS_WRAP6] = 1;
+ validStates[D3DRS_WRAP7] = 1;
+ validStates[D3DRS_CLIPPING] = 1;
+ validStates[D3DRS_LIGHTING] = 1;
+ validStates[D3DRS_AMBIENT] = 1;
+ validStates[D3DRS_FOGVERTEXMODE] = 1;
+ validStates[D3DRS_COLORVERTEX] = 1;
+ validStates[D3DRS_LOCALVIEWER] = 1;
+ validStates[D3DRS_NORMALIZENORMALS] = 1;
+ validStates[D3DRS_DIFFUSEMATERIALSOURCE] = 1;
+ validStates[D3DRS_SPECULARMATERIALSOURCE] = 1;
+ validStates[D3DRS_AMBIENTMATERIALSOURCE] = 1;
+ validStates[D3DRS_EMISSIVEMATERIALSOURCE] = 1;
+ validStates[D3DRS_VERTEXBLEND] = 1;
+ validStates[D3DRS_CLIPPLANEENABLE] = 1;
+ validStates[D3DRS_POINTSIZE] = 1;
+ validStates[D3DRS_POINTSIZE_MIN] = 1;
+ validStates[D3DRS_POINTSPRITEENABLE] = 1;
+ validStates[D3DRS_POINTSCALEENABLE] = 1;
+ validStates[D3DRS_POINTSCALE_A] = 1;
+ validStates[D3DRS_POINTSCALE_B] = 1;
+ validStates[D3DRS_POINTSCALE_C] = 1;
+ validStates[D3DRS_MULTISAMPLEANTIALIAS] = 1;
+ validStates[D3DRS_MULTISAMPLEMASK] = 1;
+ validStates[D3DRS_PATCHEDGESTYLE] = 1;
+ validStates[D3DRS_DEBUGMONITORTOKEN] = 1;
+ validStates[D3DRS_POINTSIZE_MAX] = 1;
+ validStates[D3DRS_INDEXEDVERTEXBLENDENABLE] = 1;
+ validStates[D3DRS_COLORWRITEENABLE] = 1;
+ validStates[D3DRS_TWEENFACTOR] = 1;
+ validStates[D3DRS_BLENDOP] = 1;
+ validStates[D3DRS_POSITIONDEGREE] = 1;
+ validStates[D3DRS_NORMALDEGREE] = 1;
+ validStates[D3DRS_SCISSORTESTENABLE] = 1;
+ validStates[D3DRS_SLOPESCALEDEPTHBIAS] = 1;
+ validStates[D3DRS_ANTIALIASEDLINEENABLE] = 1;
+ validStates[D3DRS_MINTESSELLATIONLEVEL] = 1;
+ validStates[D3DRS_MAXTESSELLATIONLEVEL] = 1;
+ validStates[D3DRS_ADAPTIVETESS_X] = 1;
+ validStates[D3DRS_ADAPTIVETESS_Y] = 1;
+ validStates[D3DRS_ADAPTIVETESS_Z] = 1;
+ validStates[D3DRS_ADAPTIVETESS_W] = 1;
+ validStates[D3DRS_ENABLEADAPTIVETESSELLATION] = 1;
+ validStates[D3DRS_TWOSIDEDSTENCILMODE] = 1;
+ validStates[D3DRS_CCW_STENCILFAIL] = 1;
+ validStates[D3DRS_CCW_STENCILZFAIL] = 1;
+ validStates[D3DRS_CCW_STENCILPASS] = 1;
+ validStates[D3DRS_CCW_STENCILFUNC] = 1;
+ validStates[D3DRS_COLORWRITEENABLE1] = 1;
+ validStates[D3DRS_COLORWRITEENABLE2] = 1;
+ validStates[D3DRS_COLORWRITEENABLE3] = 1;
+ validStates[D3DRS_BLENDFACTOR] = 1;
+ validStates[D3DRS_SRGBWRITEENABLE] = 1;
+ validStates[D3DRS_DEPTHBIAS] = 1;
+ validStates[D3DRS_WRAP8] = 1;
+ validStates[D3DRS_WRAP9] = 1;
+ validStates[D3DRS_WRAP10] = 1;
+ validStates[D3DRS_WRAP11] = 1;
+ validStates[D3DRS_WRAP12] = 1;
+ validStates[D3DRS_WRAP13] = 1;
+ validStates[D3DRS_WRAP14] = 1;
+ validStates[D3DRS_WRAP15] = 1;
+ validStates[D3DRS_SEPARATEALPHABLENDENABLE] = 1;
+ validStates[D3DRS_SRCBLENDALPHA] = 1;
+ validStates[D3DRS_DESTBLENDALPHA] = 1;
+ validStates[D3DRS_BLENDOPALPHA] = 1;
+
+ validTexStates[D3DTSS_COLOROP] = 1;
+ validTexStates[D3DTSS_COLORARG1] = 1;
+ validTexStates[D3DTSS_COLORARG2] = 1;
+ validTexStates[D3DTSS_ALPHAOP] = 1;
+ validTexStates[D3DTSS_ALPHAARG1] = 1;
+ validTexStates[D3DTSS_ALPHAARG2] = 1;
+ validTexStates[D3DTSS_BUMPENVMAT00] = 1;
+ validTexStates[D3DTSS_BUMPENVMAT01] = 1;
+ validTexStates[D3DTSS_BUMPENVMAT10] = 1;
+ validTexStates[D3DTSS_BUMPENVMAT11] = 1;
+ validTexStates[D3DTSS_TEXCOORDINDEX] = 1;
+ validTexStates[D3DTSS_BUMPENVLSCALE] = 1;
+ validTexStates[D3DTSS_BUMPENVLOFFSET] = 1;
+ validTexStates[D3DTSS_TEXTURETRANSFORMFLAGS] = 1;
+ validTexStates[D3DTSS_COLORARG0] = 1;
+ validTexStates[D3DTSS_ALPHAARG0] = 1;
+ validTexStates[D3DTSS_RESULTARG] = 1;
+ validTexStates[D3DTSS_CONSTANT] = 1;
+
+ // Save the current states
+ for(s = 0; s < MAXNUMSTATES; s++)
+ if(validStates[s]){
+ d3ddevice->GetRenderState((D3DRENDERSTATETYPE)s, (DWORD*)&d3dStates[s]);
+ stateCache[s].value = d3dStates[s];
+ }
+ for(t = 0; t < MAXNUMTEXSTATES; t++)
+ if(validTexStates[t])
+ for(s = 0; s < MAXNUMSTAGES; s++){
+ d3ddevice->GetTextureStageState(s, (D3DTEXTURESTAGESTATETYPE)t, (DWORD*)&d3dTextureStageStates[t][s]);
+ textureStageStateCache[t][s].value = d3dTextureStageStates[t][s];
+ }
+ for(t = 1; t < MAXNUMSAMPLERSTATES; t++)
+ for(s = 0; s < MAXNUMSTAGES; s++){
+ d3ddevice->GetSamplerState(s, (D3DSAMPLERSTATETYPE)t, (DWORD*)&d3dSamplerStates[t][s]);
+ d3dSamplerStates[t][s] = d3dSamplerStates[t][s];
+ }
+ // init rw cache
+ for(t = 0; t < MAXNUMSTAGES; t++){
+ setFilterMode(t, Texture::NEAREST);
+ setAddressU(t, Texture::WRAP);
+ setAddressV(t, Texture::WRAP);
+ }
+
+ IDirect3DSurface9 *surf;
+ D3DLOCKED_RECT lr;
+ uint8 whitepixel[4] = {0xFF, 0xFF, 0xFF, 0xFF};
+ whiteTex = (IDirect3DTexture9*)createTexture(1, 1, 1, 0, D3DFMT_X8R8G8B8);
+ whiteTex->GetSurfaceLevel(0, &surf);
+ HRESULT res = surf->LockRect(&lr, 0, D3DLOCK_NOSYSLOCK);
+ assert(res == D3D_OK);
+ memcpy(lr.pBits, whitepixel, 4);
+ surf->UnlockRect();
+ surf->Release();
+
+ openIm2D();
+ openIm3D();
+
+ return 1;
+}
+
+static int
+termD3D(void)
+{
+ destroyVertexBuffer(constantVertexStream);
+ constantVertexStream = nil;
+
+ destroyTexture(whiteTex);
+ whiteTex = nil;
+
+ closeIm3D();
+ closeIm2D();
+
+ releaseVideoMemory();
+
+ ULONG ref = d3d::d3ddevice->Release();
+ if(ref != 0)
+ printf("IDirect3D9Device_Release did not destroy\n");
+ d3d::d3ddevice = nil;
+ return 1;
+}
+
+static int
+finalizeD3D(void)
+{
+ return 1;
+}
+
+static int
+deviceSystem(DeviceReq req, void *arg, int32 n)
+{
+ D3DADAPTER_IDENTIFIER9 adapter;
+ VideoMode *rwmode;
+
+ switch(req){
+ case DEVICEOPEN:
+ return openD3D((EngineOpenParams*)arg);
+ case DEVICECLOSE:
+ return closeD3D();
+
+ case DEVICEINIT:
+ return startD3D() && initD3D();
+ case DEVICETERM:
+ return termD3D();
+
+ case DEVICEFINALIZE:
+ return finalizeD3D();
+
+
+ case DEVICEGETNUMSUBSYSTEMS:
+ return d3d9Globals.numAdapters;
+
+ case DEVICEGETCURRENTSUBSYSTEM:
+ return d3d9Globals.adapter;
+
+ case DEVICESETSUBSYSTEM:
+ if(n >= d3d9Globals.numAdapters)
+ return 0;
+ d3d9Globals.adapter = n;
+ if(d3d9Globals.d3d9->GetDeviceCaps(d3d9Globals.adapter, D3DDEVTYPE_HAL, &d3d9Globals.caps) != D3D_OK)
+ return 0;
+ makeVideoModeList();
+ return 1;
+
+ case DEVICEGETSUBSSYSTEMINFO:
+ if(n >= d3d9Globals.numAdapters)
+ return 0;
+ if(d3d9Globals.d3d9->GetAdapterIdentifier(d3d9Globals.adapter, 0, &adapter) != D3D_OK)
+ return 0;
+ strncpy(((SubSystemInfo*)arg)->name, adapter.Description, sizeof(SubSystemInfo::name));
+ return 1;
+
+
+ case DEVICEGETNUMVIDEOMODES:
+ return d3d9Globals.numModes;
+
+ case DEVICEGETCURRENTVIDEOMODE:
+ return d3d9Globals.currentMode;
+
+ case DEVICESETVIDEOMODE:
+ if(n >= d3d9Globals.numModes)
+ return 0;
+ d3d9Globals.currentMode = n;
+ return 1;
+
+ case DEVICEGETVIDEOMODEINFO:
+ rwmode = (VideoMode*)arg;
+ rwmode->width = d3d9Globals.modes[n].mode.Width;
+ rwmode->height = d3d9Globals.modes[n].mode.Height;
+ rwmode->depth = findFormatDepth(d3d9Globals.modes[n].mode.Format);
+ rwmode->flags = d3d9Globals.modes[n].flags;
+ return 1;
+ case DEVICEGETMAXMULTISAMPLINGLEVELS:
+ {
+ assert(d3d9Globals.d3d9 != nil);
+ uint32 level;
+ DWORD quality;
+ for (level = D3DMULTISAMPLE_16_SAMPLES; level > D3DMULTISAMPLE_NONMASKABLE; level--) {
+ if (SUCCEEDED(d3d9Globals.d3d9->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3d9Globals.startMode.mode.Format,
+ !(d3d9Globals.startMode.flags & VIDEOMODEEXCLUSIVE), (D3DMULTISAMPLE_TYPE)level,
+ &quality)))
+ return level;
+ }
+ }
+ return 1;
+ case DEVICEGETMULTISAMPLINGLEVELS:
+ if(d3d9Globals.msLevel == 0)
+ return 1;
+ return d3d9Globals.msLevel;
+ case DEVICESETMULTISAMPLINGLEVELS:
+ d3d9Globals.msLevel = (uint32)n;
+ return 1;
+ }
+ return 1;
+}
+
+Device renderdevice = {
+ 0.0f, 1.0f,
+ d3d::beginUpdate,
+ d3d::endUpdate,
+ d3d::clearCamera,
+ d3d::showRaster,
+ d3d::rasterRenderFast,
+ d3d::setRwRenderState,
+ d3d::getRwRenderState,
+ d3d::im2DRenderLine,
+ d3d::im2DRenderTriangle,
+ d3d::im2DRenderPrimitive,
+ d3d::im2DRenderIndexedPrimitive,
+ d3d::im3DTransform,
+ d3d::im3DRenderPrimitive,
+ d3d::im3DRenderIndexedPrimitive,
+ d3d::im3DEnd,
+ d3d::deviceSystem,
+};
+
+#endif
+}
+}
diff --git a/src/d3d/d3dimmed.cpp b/src/d3d/d3dimmed.cpp
new file mode 100644
index 0000000..e0df9d9
--- /dev/null
+++ b/src/d3d/d3dimmed.cpp
@@ -0,0 +1,404 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwplg.h"
+#include "../rwrender.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwd3d.h"
+#include "rwd3d9.h"
+#include "rwd3dimpl.h"
+
+namespace rw {
+namespace d3d {
+
+#ifdef RW_D3D9
+
+// might want to tweak this
+#define NUMINDICES 10000
+#define NUMVERTICES 10000
+
+static int primTypeMap[] = {
+ D3DPT_POINTLIST, // invalid
+ D3DPT_LINELIST,
+ D3DPT_LINESTRIP,
+ D3DPT_TRIANGLELIST,
+ D3DPT_TRIANGLESTRIP,
+ D3DPT_TRIANGLEFAN,
+ D3DPT_POINTLIST, // actually not supported!
+};
+
+static IDirect3DVertexDeclaration9 *im2ddecl;
+static IDirect3DVertexBuffer9 *im2dvertbuf;
+static IDirect3DIndexBuffer9 *im2dindbuf;
+
+void *im2dOverridePS;
+
+void
+openIm2D(void)
+{
+ D3DVERTEXELEMENT9 elements[4] = {
+// can't get proper fog with this :(
+// { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 },
+ { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
+ { 0, offsetof(Im2DVertex, color), D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
+ { 0, offsetof(Im2DVertex, u), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ D3DDECL_END()
+ };
+ assert(im2ddecl == nil);
+ im2ddecl = (IDirect3DVertexDeclaration9*)d3d9::createVertexDeclaration((d3d9::VertexElement*)elements);
+ assert(im2ddecl);
+
+ assert(im2dvertbuf == nil);
+ im2dvertbuf = (IDirect3DVertexBuffer9*)createVertexBuffer(NUMVERTICES*sizeof(Im2DVertex), 0, true);
+ assert(im2dvertbuf);
+ addDynamicVB(NUMVERTICES*sizeof(Im2DVertex), 0, &im2dvertbuf);
+
+ assert(im2dindbuf == nil);
+ im2dindbuf = (IDirect3DIndexBuffer9*)createIndexBuffer(NUMINDICES*sizeof(uint16), true);
+ assert(im2dindbuf);
+ addDynamicIB(NUMINDICES*sizeof(uint16), &im2dindbuf);
+}
+
+void
+closeIm2D(void)
+{
+ d3d9::destroyVertexDeclaration(im2ddecl);
+ im2ddecl = nil;
+
+ removeDynamicVB(&im2dvertbuf);
+ destroyVertexBuffer(im2dvertbuf);
+ im2dvertbuf = nil;
+
+ removeDynamicIB(&im2dindbuf);
+ destroyIndexBuffer(im2dindbuf);
+ im2dindbuf = nil;
+}
+
+static Im2DVertex tmpprimbuf[3];
+
+void
+im2DRenderLine(void *vertices, int32 numVertices, int32 vert1, int32 vert2)
+{
+ Im2DVertex *verts = (Im2DVertex*)vertices;
+ tmpprimbuf[0] = verts[vert1];
+ tmpprimbuf[1] = verts[vert2];
+ im2DRenderPrimitive(PRIMTYPELINELIST, tmpprimbuf, 2);
+}
+
+void
+im2DRenderTriangle(void *vertices, int32 numVertices, int32 vert1, int32 vert2, int32 vert3)
+{
+ Im2DVertex *verts = (Im2DVertex*)vertices;
+ tmpprimbuf[0] = verts[vert1];
+ tmpprimbuf[1] = verts[vert2];
+ tmpprimbuf[2] = verts[vert3];
+ im2DRenderPrimitive(PRIMTYPETRILIST, tmpprimbuf, 3);
+}
+
+void
+im2DSetXform(void)
+{
+ float xform[4];
+ Camera *cam;
+ cam = (Camera*)engine->currentCamera;
+ xform[0] = 2.0f/cam->frameBuffer->width;
+ xform[1] = -2.0f/cam->frameBuffer->height;
+ xform[2] = -1.0f;
+ xform[3] = 1.0f;
+ // TODO: should cache this...
+ d3ddevice->SetVertexShaderConstantF(VSLOC_afterLights, xform, 1);
+}
+
+void
+im2DRenderPrimitive(PrimitiveType primType, void *vertices, int32 numVertices)
+{
+ if(numVertices > NUMVERTICES){
+ // TODO: error
+ return;
+ }
+ uint8 *lockedvertices = lockVertices(im2dvertbuf, 0, numVertices*sizeof(Im2DVertex), D3DLOCK_DISCARD);
+ memcpy(lockedvertices, vertices, numVertices*sizeof(Im2DVertex));
+ unlockVertices(im2dvertbuf);
+
+ setStreamSource(0, im2dvertbuf, 0, sizeof(Im2DVertex));
+ setVertexDeclaration(im2ddecl);
+
+ im2DSetXform();
+
+ setVertexShader(im2d_VS);
+ if(im2dOverridePS)
+ setPixelShader(im2dOverridePS);
+ else if(engine->device.getRenderState(TEXTURERASTER))
+ setPixelShader(im2d_tex_PS);
+ else
+ setPixelShader(im2d_PS);
+
+ d3d::flushCache();
+
+ uint32 primCount = 0;
+ switch(primType){
+ case PRIMTYPELINELIST:
+ primCount = numVertices/2;
+ break;
+ case PRIMTYPEPOLYLINE:
+ primCount = numVertices-1;
+ break;
+ case PRIMTYPETRILIST:
+ primCount = numVertices/3;
+ break;
+ case PRIMTYPETRISTRIP:
+ primCount = numVertices-2;
+ break;
+ case PRIMTYPETRIFAN:
+ primCount = numVertices-2;
+ break;
+ case PRIMTYPEPOINTLIST:
+ primCount = numVertices;
+ break;
+ }
+ d3ddevice->DrawPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0, primCount);
+}
+
+void
+im2DRenderIndexedPrimitive(PrimitiveType primType,
+ void *vertices, int32 numVertices, void *indices, int32 numIndices)
+{
+ if(numVertices > NUMVERTICES ||
+ numIndices > NUMINDICES){
+ // TODO: error
+ return;
+ }
+ uint16 *lockedindices = lockIndices(im2dindbuf, 0, numIndices*sizeof(uint16), D3DLOCK_DISCARD);
+ memcpy(lockedindices, indices, numIndices*sizeof(uint16));
+ unlockIndices(im2dindbuf);
+
+ uint8 *lockedvertices = lockVertices(im2dvertbuf, 0, numVertices*sizeof(Im2DVertex), D3DLOCK_DISCARD);
+ memcpy(lockedvertices, vertices, numVertices*sizeof(Im2DVertex));
+ unlockVertices(im2dvertbuf);
+
+ setStreamSource(0, im2dvertbuf, 0, sizeof(Im2DVertex));
+ setIndices(im2dindbuf);
+ setVertexDeclaration(im2ddecl);
+
+ im2DSetXform();
+
+ setVertexShader(im2d_VS);
+ if(im2dOverridePS)
+ setPixelShader(im2dOverridePS);
+ else if(engine->device.getRenderState(TEXTURERASTER))
+ setPixelShader(im2d_tex_PS);
+ else
+ setPixelShader(im2d_PS);
+
+ d3d::flushCache();
+
+ uint32 primCount = 0;
+ switch(primType){
+ case PRIMTYPELINELIST:
+ primCount = numIndices/2;
+ break;
+ case PRIMTYPEPOLYLINE:
+ primCount = numIndices-1;
+ break;
+ case PRIMTYPETRILIST:
+ primCount = numIndices/3;
+ break;
+ case PRIMTYPETRISTRIP:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPETRIFAN:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPEPOINTLIST:
+ primCount = numIndices;
+ break;
+ }
+ d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0,
+ 0, numVertices,
+ 0, primCount);
+}
+
+
+// Im3D
+
+
+static IDirect3DVertexDeclaration9 *im3ddecl;
+static IDirect3DVertexBuffer9 *im3dvertbuf;
+static IDirect3DIndexBuffer9 *im3dindbuf;
+static int32 num3DVertices;
+
+void
+openIm3D(void)
+{
+ D3DVERTEXELEMENT9 elements[5] = {
+ { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
+ { 0, offsetof(Im3DVertex, normal), D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
+ { 0, offsetof(Im3DVertex, color), D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
+ { 0, offsetof(Im3DVertex, u), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ D3DDECL_END()
+ };
+
+ assert(im3ddecl == nil);
+ im3ddecl = (IDirect3DVertexDeclaration9*)d3d9::createVertexDeclaration((d3d9::VertexElement*)elements);
+ assert(im3ddecl);
+
+ assert(im3dvertbuf == nil);
+ im3dvertbuf = (IDirect3DVertexBuffer9*)createVertexBuffer(NUMVERTICES*sizeof(Im3DVertex), 0, true);
+ assert(im3dvertbuf);
+ addDynamicVB(NUMVERTICES*sizeof(Im3DVertex), 0, &im3dvertbuf);
+
+ assert(im3dindbuf == nil);
+ im3dindbuf = (IDirect3DIndexBuffer9*)createIndexBuffer(NUMINDICES*sizeof(uint16), true);
+ assert(im3dindbuf);
+ addDynamicIB(NUMINDICES*sizeof(uint16), &im3dindbuf);
+}
+
+void
+closeIm3D(void)
+{
+ d3d9::destroyVertexDeclaration(im3ddecl);
+ im3ddecl = nil;
+
+ removeDynamicVB(&im3dvertbuf);
+ destroyVertexBuffer(im3dvertbuf);
+ im3dvertbuf = nil;
+
+ removeDynamicIB(&im3dindbuf);
+ destroyIndexBuffer(im3dindbuf);
+ im3dindbuf = nil;
+}
+
+// settable by user - TOOD: make this less shit
+RGBA im3dMaterialColor = { 255, 255, 255, 255 };
+SurfaceProperties im3dSurfaceProps = { 1.0f, 1.0f, 1.0f };
+
+void
+im3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags)
+{
+ if(world == nil)
+ uploadMatrices();
+ else
+ uploadMatrices(world);
+
+ if((flags & im3d::VERTEXUV) == 0)
+ SetRenderStatePtr(TEXTURERASTER, nil);
+
+ void *shader = default_amb_VS;
+ if(flags & im3d::LIGHTING){
+ setMaterial(im3dMaterialColor, im3dSurfaceProps);
+ int32 vsBits = lightingCB_Shader();
+ // Pick a shader
+ if((vsBits & VSLIGHT_MASK) == 0)
+ shader = default_amb_VS;
+ else if((vsBits & VSLIGHT_MASK) == VSLIGHT_DIRECT)
+ shader = default_amb_dir_VS;
+ else
+ shader = default_all_VS;
+ }else{
+ static RGBA white = { 255, 255, 255, 255 };
+ static SurfaceProperties surfprops = { 0.0f, 0.0f, 0.0f };
+ setMaterial(white, surfprops);
+ }
+
+ uint8 *lockedvertices = lockVertices(im3dvertbuf, 0, numVertices*sizeof(Im3DVertex), D3DLOCK_DISCARD);
+ memcpy(lockedvertices, vertices, numVertices*sizeof(Im3DVertex));
+ unlockVertices(im3dvertbuf);
+
+ setStreamSource(0, im3dvertbuf, 0, sizeof(Im3DVertex));
+ setVertexDeclaration(im3ddecl);
+
+ setVertexShader(shader);
+
+ num3DVertices = numVertices;
+}
+
+void
+im3DRenderPrimitive(PrimitiveType primType)
+{
+ if(engine->device.getRenderState(TEXTURERASTER))
+ setPixelShader(default_tex_PS);
+ else
+ setPixelShader(default_PS);
+
+ d3d::flushCache();
+
+ uint32 primCount = 0;
+ switch(primType){
+ case PRIMTYPELINELIST:
+ primCount = num3DVertices/2;
+ break;
+ case PRIMTYPEPOLYLINE:
+ primCount = num3DVertices-1;
+ break;
+ case PRIMTYPETRILIST:
+ primCount = num3DVertices/3;
+ break;
+ case PRIMTYPETRISTRIP:
+ primCount = num3DVertices-2;
+ break;
+ case PRIMTYPETRIFAN:
+ primCount = num3DVertices-2;
+ break;
+ case PRIMTYPEPOINTLIST:
+ primCount = num3DVertices;
+ break;
+ }
+ d3ddevice->DrawPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0, primCount);
+}
+
+void
+im3DRenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices)
+{
+ uint16 *lockedindices = lockIndices(im3dindbuf, 0, numIndices*sizeof(uint16), D3DLOCK_DISCARD);
+ memcpy(lockedindices, indices, numIndices*sizeof(uint16));
+ unlockIndices(im3dindbuf);
+
+ setIndices(im3dindbuf);
+
+ if(engine->device.getRenderState(TEXTURERASTER))
+ setPixelShader(default_tex_PS);
+ else
+ setPixelShader(default_PS);
+
+ d3d::flushCache();
+
+ uint32 primCount = 0;
+ switch(primType){
+ case PRIMTYPELINELIST:
+ primCount = numIndices/2;
+ break;
+ case PRIMTYPEPOLYLINE:
+ primCount = numIndices-1;
+ break;
+ case PRIMTYPETRILIST:
+ primCount = numIndices/3;
+ break;
+ case PRIMTYPETRISTRIP:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPETRIFAN:
+ primCount = numIndices-2;
+ break;
+ case PRIMTYPEPOINTLIST:
+ primCount = numIndices;
+ break;
+ }
+ d3ddevice->DrawIndexedPrimitive((D3DPRIMITIVETYPE)primTypeMap[primType], 0,
+ 0, num3DVertices,
+ 0, primCount);
+}
+
+void
+im3DEnd(void)
+{
+}
+
+#endif
+}
+}
diff --git a/src/d3d/d3drender.cpp b/src/d3d/d3drender.cpp
new file mode 100644
index 0000000..d135d87
--- /dev/null
+++ b/src/d3d/d3drender.cpp
@@ -0,0 +1,424 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "../rwbase.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwd3d.h"
+
+namespace rw {
+namespace d3d {
+
+#ifdef RW_D3D9
+IDirect3DDevice9 *d3ddevice = nil;
+
+#define MAX_LIGHTS 8
+
+
+#define VS_NAME g_vs20_main
+#define PS_NAME g_ps20_main
+void *default_amb_VS;
+void *default_amb_dir_VS;
+void *default_all_VS;
+void *default_PS;
+void *default_tex_PS;
+void *im2d_VS;
+void *im2d_PS;
+void *im2d_tex_PS;
+
+
+void
+createDefaultShaders(void)
+{
+ {
+ static
+#include "shaders/default_amb_VS.h"
+ default_amb_VS = createVertexShader((void*)VS_NAME);
+ assert(default_amb_VS);
+ }
+ {
+ static
+#include "shaders/default_amb_dir_VS.h"
+ default_amb_dir_VS = createVertexShader((void*)VS_NAME);
+ assert(default_amb_dir_VS);
+ }
+ {
+ static
+#include "shaders/default_all_VS.h"
+ default_all_VS = createVertexShader((void*)VS_NAME);
+ assert(default_all_VS);
+ }
+
+ {
+ static
+#include "shaders/default_PS.h"
+ default_PS = createPixelShader((void*)PS_NAME);
+ assert(default_PS);
+ }
+ {
+ static
+#include "shaders/default_tex_PS.h"
+ default_tex_PS = createPixelShader((void*)PS_NAME);
+ assert(default_tex_PS);
+ }
+
+ {
+ static
+#include "shaders/im2d_VS.h"
+ im2d_VS = createVertexShader((void*)VS_NAME);
+ assert(im2d_VS);
+ }
+ {
+ static
+#include "shaders/im2d_PS.h"
+ im2d_PS = createPixelShader((void*)PS_NAME);
+ assert(im2d_PS);
+ }
+ {
+ static
+#include "shaders/im2d_tex_PS.h"
+ im2d_tex_PS = createPixelShader((void*)PS_NAME);
+ assert(im2d_tex_PS);
+ }
+}
+
+void
+destroyDefaultShaders(void)
+{
+ destroyVertexShader(default_amb_VS);
+ default_amb_VS = nil;
+ destroyVertexShader(default_amb_dir_VS);
+ default_amb_dir_VS = nil;
+ destroyVertexShader(default_all_VS);
+ default_all_VS = nil;
+
+ destroyPixelShader(default_PS);
+ default_PS = nil;
+ destroyPixelShader(default_tex_PS);
+ default_tex_PS = nil;
+
+ destroyVertexShader(im2d_VS);
+ im2d_VS = nil;
+ destroyPixelShader(im2d_PS);
+ im2d_PS = nil;
+ destroyPixelShader(im2d_tex_PS);
+ im2d_tex_PS = nil;
+}
+
+
+void
+lightingCB_Fix(Atomic *atomic)
+{
+ WorldLights lightData;
+ Light *directionals[8];
+ Light *locals[8];
+ lightData.directionals = directionals;
+ lightData.numDirectionals = 8;
+ lightData.locals = locals;
+ lightData.numLocals = 8;
+
+ ((World*)engine->currentWorld)->enumerateLights(atomic, &lightData);
+
+ int i, n;
+ RGBA amb;
+ D3DLIGHT9 light;
+ light.Type = D3DLIGHT_DIRECTIONAL;
+ //light.Diffuse = { 0.8f, 0.8f, 0.8f, 1.0f };
+ light.Specular = { 0.0f, 0.0f, 0.0f, 0.0f };
+ light.Ambient = { 0.0f, 0.0f, 0.0f, 0.0f };
+ light.Position = { 0.0f, 0.0f, 0.0f };
+ //light.Direction = { 0.0f, 0.0f, -1.0f };
+ light.Range = 0.0f;
+ light.Falloff = 0.0f;
+ light.Attenuation0 = 0.0f;
+ light.Attenuation1 = 0.0f;
+ light.Attenuation2 = 0.0f;
+ light.Theta = 0.0f;
+ light.Phi = 0.0f;
+
+ convColor(&amb, &lightData.ambient);
+ d3d::setRenderState(D3DRS_AMBIENT, D3DCOLOR_RGBA(amb.red, amb.green, amb.blue, amb.alpha));
+
+ n = 0;
+ for(i = 0; i < lightData.numDirectionals; i++){
+ if(n >= MAX_LIGHTS)
+ return;
+ Light *l = lightData.directionals[i];
+ light.Type = D3DLIGHT_DIRECTIONAL;
+ light.Diffuse = *(D3DCOLORVALUE*)&l->color;
+ light.Direction = *(D3DVECTOR*)&l->getFrame()->getLTM()->at;
+ d3ddevice->SetLight(n, &light);
+ d3ddevice->LightEnable(n, TRUE);
+ n++;
+ }
+
+ for(i = 0; i < lightData.numLocals; i++){
+ if(n >= MAX_LIGHTS)
+ return;
+ Light *l = lightData.locals[i];
+ switch(l->getType()){
+ case Light::POINT:
+ light.Type = D3DLIGHT_POINT;
+ light.Diffuse = *(D3DCOLORVALUE*)&l->color;
+ light.Position = *(D3DVECTOR*)&l->getFrame()->getLTM()->pos;
+ light.Direction.x = 0.0f;
+ light.Direction.y = 0.0f;
+ light.Direction.z = 0.0f;
+ light.Range = l->radius;
+ light.Falloff = 1.0f;
+ light.Attenuation0 = 1.0f;
+ light.Attenuation1 = 0.0f/l->radius;
+ light.Attenuation2 = 5.0f/(l->radius*l->radius);
+ d3ddevice->SetLight(n, &light);
+ d3ddevice->LightEnable(n, TRUE);
+ n++;
+ break;
+
+ case Light::SPOT:
+ light.Type = D3DLIGHT_SPOT;
+ light.Diffuse = *(D3DCOLORVALUE*)&l->color;
+ light.Position = *(D3DVECTOR*)&l->getFrame()->getLTM()->pos;
+ light.Direction = *(D3DVECTOR*)&l->getFrame()->getLTM()->at;
+ light.Range = l->radius;
+ light.Falloff = 1.0f;
+ light.Attenuation0 = 1.0f;
+ light.Attenuation1 = 0.0f/l->radius;
+ light.Attenuation2 = 5.0f/(l->radius*l->radius);
+ light.Theta = l->getAngle()*2.0f;
+ light.Phi = light.Theta;
+ d3ddevice->SetLight(n, &light);
+ d3ddevice->LightEnable(n, TRUE);
+ n++;
+ break;
+
+ case Light::SOFTSPOT:
+ light.Type = D3DLIGHT_SPOT;
+ light.Diffuse = *(D3DCOLORVALUE*)&l->color;
+ light.Position = *(D3DVECTOR*)&l->getFrame()->getLTM()->pos;
+ light.Direction = *(D3DVECTOR*)&l->getFrame()->getLTM()->at;
+ light.Range = l->radius;
+ light.Falloff = 1.0f;
+ light.Attenuation0 = 1.0f;
+ light.Attenuation1 = 0.0f/l->radius;
+ light.Attenuation2 = 5.0f/(l->radius*l->radius);
+ light.Theta = 0.0f;
+ light.Phi = l->getAngle()*2.0f;
+ d3ddevice->SetLight(n, &light);
+ d3ddevice->LightEnable(n, TRUE);
+ n++;
+ break;
+ }
+ }
+
+ for(; n < MAX_LIGHTS; n++)
+ d3ddevice->LightEnable(n, FALSE);
+}
+
+
+struct LightVS
+{
+ V3d color; float param0;
+ V3d position; float param1;
+ V3d direction; float param2;
+};
+
+void
+setAmbient(const RGBAf &color)
+{
+ if(!equal(d3dShaderState.ambient, color)){
+ d3dShaderState.ambient = color;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_ambLight, (float*)&color, 1);
+ }
+}
+
+void
+setNumLights(int numDir, int numPoint, int numSpot)
+{
+ static int32 numLights[4*3];
+ if(d3dShaderState.numDir != numDir ||
+ d3dShaderState.numPoint != numPoint ||
+ d3dShaderState.numSpot != numSpot){
+ numLights[0] = d3dShaderState.numDir = numDir;
+ numLights[4] = d3dShaderState.numPoint = numPoint;
+ numLights[8] = d3dShaderState.numSpot = numSpot;
+ d3ddevice->SetVertexShaderConstantI(VSLOC_numLights, numLights, 3);
+ }
+}
+
+int32
+uploadLights(WorldLights *lightData)
+{
+ int i;
+ int bits = 0;
+ float32 firstLight[4];
+ firstLight[0] = 0; // directional
+ firstLight[1] = 0; // point
+ firstLight[2] = 0; // spot
+ firstLight[3] = 0;
+
+ if(lightData->numAmbients)
+ bits |= VSLIGHT_AMBIENT;
+
+ LightVS directionals[8];
+ LightVS points[8];
+ LightVS spots[8];
+ for(i = 0; i < lightData->numDirectionals; i++){
+ Light *l = lightData->directionals[i];
+ directionals[i].color.x = l->color.red;
+ directionals[i].color.y = l->color.green;
+ directionals[i].color.z = l->color.blue;
+ directionals[i].direction = l->getFrame()->getLTM()->at;
+ bits |= VSLIGHT_DIRECT;
+ }
+
+ int np = 0;
+ int ns = 0;
+ for(i = 0; i < lightData->numLocals; i++){
+ Light *l = lightData->locals[i];
+
+ switch(l->getType()){
+ case Light::POINT:
+ points[np].color.x = l->color.red;
+ points[np].color.y = l->color.green;
+ points[np].color.z = l->color.blue;
+ points[np].param0 = l->radius;
+ points[np].position = l->getFrame()->getLTM()->pos;
+ np++;
+ bits |= VSLIGHT_POINT;
+ break;
+ case Light::SPOT:
+ case Light::SOFTSPOT:
+ spots[ns].color.x = l->color.red;
+ spots[ns].color.y = l->color.green;
+ spots[ns].color.z = l->color.blue;
+ spots[ns].param0 = l->radius;
+ spots[ns].position = l->getFrame()->getLTM()->pos;
+ spots[ns].direction = l->getFrame()->getLTM()->at;
+ spots[ns].param1 = l->minusCosAngle;
+ // lower bound of falloff
+ if(l->getType() == Light::SOFTSPOT)
+ spots[ns].param2 = 0.0f;
+ else
+ spots[ns].param2 = 1.0f;
+ bits |= VSLIGHT_SPOT;
+ ns++;
+ break;
+ }
+ }
+
+ firstLight[0] = 0;
+ int numDir = lightData->numDirectionals;
+ firstLight[1] = numDir + firstLight[0];
+ int numPoint = np;
+ firstLight[2] = numPoint + firstLight[1];
+ int numSpot = ns;
+
+ setNumLights(numDir, numPoint, numSpot);
+ if(d3dShaderState.lightOffset[0] != firstLight[0] ||
+ d3dShaderState.lightOffset[1] != firstLight[1] ||
+ d3dShaderState.lightOffset[2] != firstLight[2]){
+ d3dShaderState.lightOffset[0] = firstLight[0];
+ d3dShaderState.lightOffset[1] = firstLight[1];
+ d3dShaderState.lightOffset[2] = firstLight[2];
+ d3ddevice->SetVertexShaderConstantF(VSLOC_lightOffset, firstLight, 1);
+ }
+
+ int32 off = VSLOC_lights;
+ if(numDir)
+ d3ddevice->SetVertexShaderConstantF(off, (float*)&directionals, numDir*3);
+ off += numDir*3;
+
+ if(numPoint)
+ d3ddevice->SetVertexShaderConstantF(off, (float*)&points, numPoint*3);
+ off += numPoint*3;
+
+ if(numSpot)
+ d3ddevice->SetVertexShaderConstantF(off, (float*)&spots, numSpot*3);
+
+ return bits;
+}
+
+int32
+lightingCB_Shader(Atomic *atomic)
+{
+ WorldLights lightData;
+ Light *directionals[8];
+ Light *locals[8];
+ lightData.directionals = directionals;
+ lightData.numDirectionals = 8;
+ lightData.locals = locals;
+ lightData.numLocals = 8;
+
+ if(atomic->geometry->flags & rw::Geometry::LIGHT){
+ ((World*)engine->currentWorld)->enumerateLights(atomic, &lightData);
+ setAmbient(lightData.ambient);
+ return uploadLights(&lightData);
+ }else{
+ static const RGBAf black = { 0.0f, 0.0f, 0.0f, 0.0f };
+ setAmbient(black);
+ setNumLights(0, 0, 0);
+ return 0;
+ }
+}
+
+int32
+lightingCB_Shader(void)
+{
+ WorldLights lightData;
+ Light *directionals[8];
+ Light *locals[8];
+ lightData.directionals = directionals;
+ lightData.numDirectionals = 8;
+ lightData.locals = locals;
+ lightData.numLocals = 8;
+
+ ((World*)engine->currentWorld)->enumerateLights(&lightData);
+ setAmbient(lightData.ambient);
+ return uploadLights(&lightData);
+}
+
+static RawMatrix identityXform = {
+ { 1.0f, 0.0f, 0.0f }, 0.0f,
+ { 0.0f, 1.0f, 0.0f }, 0.0f,
+ { 0.0f, 0.0f, 1.0f }, 0.0f,
+ { 0.0f, 0.0f, 0.0f }, 1.0f
+};
+
+void
+uploadMatrices(void)
+{
+ RawMatrix combined;
+ Camera *cam = engine->currentCamera;
+ d3ddevice->SetVertexShaderConstantF(VSLOC_world, (float*)&identityXform, 4);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_normal, (float*)&identityXform, 4);
+
+ RawMatrix::mult(&combined, &cam->devView, &cam->devProj);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&combined, 4);
+}
+
+void
+uploadMatrices(Matrix *worldMat)
+{
+ RawMatrix combined, world, worldview;
+ Camera *cam = engine->currentCamera;
+ convMatrix(&world, worldMat);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_world, (float*)&world, 4);
+ // TODO: inverse transpose
+ d3ddevice->SetVertexShaderConstantF(VSLOC_normal, (float*)&world, 4);
+
+ RawMatrix::mult(&worldview, &world, &cam->devView);
+ RawMatrix::mult(&combined, &worldview, &cam->devProj);
+ d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&combined, 4);
+}
+
+
+
+#endif
+
+}
+}
diff --git a/src/d3d/rwd3d.h b/src/d3d/rwd3d.h
new file mode 100644
index 0000000..cabfeff
--- /dev/null
+++ b/src/d3d/rwd3d.h
@@ -0,0 +1,421 @@
+#ifdef RW_D3D9
+#ifdef WITH_D3D
+#include <d3d9.h>
+#endif
+#endif
+
+namespace rw {
+
+#ifdef RW_D3D9
+
+#ifdef _WINDOWS_
+struct EngineOpenParams
+{
+ HWND window;
+};
+#else
+struct EngineOpenParams
+{
+ uint32 please_include_windows_h;
+};
+#endif
+#else
+#ifdef _D3D9_H_
+#error "please don't include d3d9.h for non-d3d9 platforms"
+#endif
+#endif
+
+namespace d3d {
+
+extern bool32 isP8supported;
+
+extern Device renderdevice;
+
+#ifdef RW_D3D9
+#ifdef _D3D9_H_
+extern IDirect3DDevice9 *d3ddevice;
+void setD3dMaterial(D3DMATERIAL9 *mat9);
+#endif
+
+#define COLOR_ARGB(a, r, g, b) ((rw::uint32)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
+
+struct Im3DVertex
+{
+ V3d position;
+ V3d normal; // librw extension
+ uint32 color;
+ float32 u, v;
+
+ void setX(float32 x) { this->position.x = x; }
+ void setY(float32 y) { this->position.y = y; }
+ void setZ(float32 z) { this->position.z = z; }
+ void setNormalX(float32 x) { this->normal.x = x; }
+ void setNormalY(float32 y) { this->normal.y = y; }
+ void setNormalZ(float32 z) { this->normal.z = z; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) { this->color = COLOR_ARGB(a, r, g, b); }
+ void setU(float32 u) { this->u = u; }
+ void setV(float32 v) { this->v = v; }
+
+ float getX(void) { return this->position.x; }
+ float getY(void) { return this->position.y; }
+ float getZ(void) { return this->position.z; }
+ float getNormalX(void) { return this->normal.x; }
+ float getNormalY(void) { return this->normal.y; }
+ float getNormalZ(void) { return this->normal.z; }
+ RGBA getColor(void) { return makeRGBA(this->color>>16 & 0xFF, this->color>>8 & 0xFF,
+ this->color & 0xFF, this->color>>24 & 0xFF); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+extern RGBA im3dMaterialColor;
+extern SurfaceProperties im3dSurfaceProps;
+
+struct Im2DVertex
+{
+ float32 x, y, z;
+ //float32 q; // recipz no longer used because we have a vertex stage now
+ float32 w;
+ uint32 color;
+ float32 u, v;
+
+ void setScreenX(float32 x) { this->x = x; }
+ void setScreenY(float32 y) { this->y = y; }
+ void setScreenZ(float32 z) { this->z = z; }
+ void setCameraZ(float32 z) { this->w = z; }
+// void setRecipCameraZ(float32 recipz) { this->q = recipz; }
+ void setRecipCameraZ(float32 recipz) { this->w = 1.0f/recipz; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) { this->color = COLOR_ARGB(a, r, g, b); }
+ void setU(float32 u, float recipZ) { this->u = u; }
+ void setV(float32 v, float recipZ) { this->v = v; }
+
+ float getScreenX(void) { return this->x; }
+ float getScreenY(void) { return this->y; }
+ float getScreenZ(void) { return this->z; }
+// float getCameraZ(void) { return 1.0f/this->q; }
+// float getRecipCameraZ(void) { return this->q; }
+ float getCameraZ(void) { return this->w; }
+ float getRecipCameraZ(void) { return 1.0f/this->w; }
+ RGBA getColor(void) { return makeRGBA(this->color>>16 & 0xFF, this->color>>8 & 0xFF,
+ this->color & 0xFF, this->color>>24 & 0xFF); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+
+#else
+#ifndef MAKEFOURCC
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ ((uint32)(uint8)(ch0) | ((uint32)(uint8)(ch1) << 8) | \
+ ((uint32)(uint8)(ch2) << 16) | ((uint32)(uint8)(ch3) << 24 ))
+#endif
+enum {
+ D3DFMT_UNKNOWN = 0,
+
+ D3DFMT_R8G8B8 = 20,
+ D3DFMT_A8R8G8B8 = 21,
+ D3DFMT_X8R8G8B8 = 22,
+ D3DFMT_R5G6B5 = 23,
+ D3DFMT_X1R5G5B5 = 24,
+ D3DFMT_A1R5G5B5 = 25,
+ D3DFMT_A4R4G4B4 = 26,
+ D3DFMT_R3G3B2 = 27,
+ D3DFMT_A8 = 28,
+ D3DFMT_A8R3G3B2 = 29,
+ D3DFMT_X4R4G4B4 = 30,
+ D3DFMT_A2B10G10R10 = 31,
+ D3DFMT_A8B8G8R8 = 32,
+ D3DFMT_X8B8G8R8 = 33,
+ D3DFMT_G16R16 = 34,
+ D3DFMT_A2R10G10B10 = 35,
+ D3DFMT_A16B16G16R16 = 36,
+
+ D3DFMT_A8P8 = 40,
+ D3DFMT_P8 = 41,
+
+ D3DFMT_L8 = 50,
+ D3DFMT_A8L8 = 51,
+ D3DFMT_A4L4 = 52,
+
+ D3DFMT_V8U8 = 60,
+ D3DFMT_L6V5U5 = 61,
+ D3DFMT_X8L8V8U8 = 62,
+ D3DFMT_Q8W8V8U8 = 63,
+ D3DFMT_V16U16 = 64,
+ D3DFMT_A2W10V10U10 = 67,
+
+ D3DFMT_UYVY = MAKEFOURCC('U', 'Y', 'V', 'Y'),
+ D3DFMT_R8G8_B8G8 = MAKEFOURCC('R', 'G', 'B', 'G'),
+ D3DFMT_YUY2 = MAKEFOURCC('Y', 'U', 'Y', '2'),
+ D3DFMT_G8R8_G8B8 = MAKEFOURCC('G', 'R', 'G', 'B'),
+ D3DFMT_DXT1 = MAKEFOURCC('D', 'X', 'T', '1'),
+ D3DFMT_DXT2 = MAKEFOURCC('D', 'X', 'T', '2'),
+ D3DFMT_DXT3 = MAKEFOURCC('D', 'X', 'T', '3'),
+ D3DFMT_DXT4 = MAKEFOURCC('D', 'X', 'T', '4'),
+ D3DFMT_DXT5 = MAKEFOURCC('D', 'X', 'T', '5'),
+
+ D3DFMT_D16_LOCKABLE = 70,
+ D3DFMT_D32 = 71,
+ D3DFMT_D15S1 = 73,
+ D3DFMT_D24S8 = 75,
+ D3DFMT_D24X8 = 77,
+ D3DFMT_D24X4S4 = 79,
+ D3DFMT_D16 = 80,
+
+ D3DFMT_D32F_LOCKABLE = 82,
+ D3DFMT_D24FS8 = 83,
+
+ // d3d9ex only
+ /* Z-Stencil formats valid for CPU access */
+ D3DFMT_D32_LOCKABLE = 84,
+ D3DFMT_S8_LOCKABLE = 85,
+
+ D3DFMT_L16 = 81,
+
+ D3DFMT_VERTEXDATA =100,
+ D3DFMT_INDEX16 =101,
+ D3DFMT_INDEX32 =102,
+
+ D3DFMT_Q16W16V16U16 =110,
+
+ D3DFMT_MULTI2_ARGB8 = MAKEFOURCC('M','E','T','1'),
+
+ // Floating point surface formats
+
+ // s10e5 formats (16-bits per channel)
+ D3DFMT_R16F = 111,
+ D3DFMT_G16R16F = 112,
+ D3DFMT_A16B16G16R16F = 113,
+
+ // IEEE s23e8 formats (32-bits per channel)
+ D3DFMT_R32F = 114,
+ D3DFMT_G32R32F = 115,
+ D3DFMT_A32B32G32R32F = 116,
+
+ D3DFMT_CxV8U8 = 117,
+
+ // d3d9ex only
+ // Monochrome 1 bit per pixel format
+ D3DFMT_A1 = 118,
+ // 2.8 biased fixed point
+ D3DFMT_A2B10G10R10_XR_BIAS = 119,
+ // Binary format indicating that the data has no inherent type
+ D3DFMT_BINARYBUFFER = 199
+};
+
+enum {
+ D3DLOCK_NOSYSLOCK = 0, // ignored
+ D3DPOOL_MANAGED = 0, // ignored
+ D3DPT_TRIANGLELIST = 4,
+ D3DPT_TRIANGLESTRIP = 5,
+
+
+ D3DDECLTYPE_FLOAT1 = 0, // 1D float expanded to (value, 0., 0., 1.)
+ D3DDECLTYPE_FLOAT2 = 1, // 2D float expanded to (value, value, 0., 1.)
+ D3DDECLTYPE_FLOAT3 = 2, // 3D float expanded to (value, value, value, 1.)
+ D3DDECLTYPE_FLOAT4 = 3, // 4D float
+ D3DDECLTYPE_D3DCOLOR = 4, // 4D packed unsigned bytes mapped to 0. to 1. range
+ // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A)
+ D3DDECLTYPE_UBYTE4 = 5, // 4D unsigned byte
+ D3DDECLTYPE_SHORT2 = 6, // 2D signed short expanded to (value, value, 0., 1.)
+ D3DDECLTYPE_SHORT4 = 7, // 4D signed short
+
+ D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0
+ D3DDECLTYPE_SHORT2N = 9, // 2D signed short normalized (v[0]/32767.0,v[1]/32767.0,0,1)
+ D3DDECLTYPE_SHORT4N = 10, // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0)
+ D3DDECLTYPE_USHORT2N = 11, // 2D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,0,1)
+ D3DDECLTYPE_USHORT4N = 12, // 4D unsigned short normalized (v[0]/65535.0,v[1]/65535.0,v[2]/65535.0,v[3]/65535.0)
+ D3DDECLTYPE_UDEC3 = 13, // 3D unsigned 10 10 10 format expanded to (value, value, value, 1)
+ D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1)
+ D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1)
+ D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values
+ D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused.
+
+
+ D3DDECLMETHOD_DEFAULT = 0,
+
+
+ D3DDECLUSAGE_POSITION = 0,
+ D3DDECLUSAGE_BLENDWEIGHT, // 1
+ D3DDECLUSAGE_BLENDINDICES, // 2
+ D3DDECLUSAGE_NORMAL, // 3
+ D3DDECLUSAGE_PSIZE, // 4
+ D3DDECLUSAGE_TEXCOORD, // 5
+ D3DDECLUSAGE_TANGENT, // 6
+ D3DDECLUSAGE_BINORMAL, // 7
+ D3DDECLUSAGE_TESSFACTOR, // 8
+ D3DDECLUSAGE_POSITIONT, // 9
+ D3DDECLUSAGE_COLOR, // 10
+ D3DDECLUSAGE_FOG, // 11
+ D3DDECLUSAGE_DEPTH, // 12
+ D3DDECLUSAGE_SAMPLE // 13
+ ,
+
+ D3DUSAGE_AUTOGENMIPMAP = 0x400
+};
+#endif
+
+extern int vertFormatMap[];
+
+void *createIndexBuffer(uint32 length, bool dynamic);
+void destroyIndexBuffer(void *indexBuffer);
+uint16 *lockIndices(void *indexBuffer, uint32 offset, uint32 size, uint32 flags);
+void unlockIndices(void *indexBuffer);
+
+void *createVertexBuffer(uint32 length, uint32 fvf, bool dynamic);
+void destroyVertexBuffer(void *vertexBuffer);
+uint8 *lockVertices(void *vertexBuffer, uint32 offset, uint32 size, uint32 flags);
+void unlockVertices(void *vertexBuffer);
+
+void *createTexture(int32 width, int32 height, int32 levels, uint32 usage, uint32 format);
+void destroyTexture(void *texture);
+uint8 *lockTexture(void *texture, int32 level);
+void unlockTexture(void *texture, int32 level);
+
+// Native Texture and Raster
+
+struct D3dRaster
+{
+ void *texture;
+ void *palette;
+ void *lockedSurf;
+ uint32 format;
+ uint32 bpp; // bytes per pixel
+ bool hasAlpha;
+ bool customFormat;
+ bool autogenMipmap;
+};
+
+int32 getLevelSize(Raster *raster, int32 level);
+void allocateDXT(Raster *raster, int32 dxt, int32 numLevels, bool32 hasAlpha);
+void setPalette(Raster *raster, void *palette, int32 size);
+void setTexels(Raster *raster, void *texels, int32 level);
+
+extern int32 nativeRasterOffset;
+void registerNativeRaster(void);
+#define GETD3DRASTEREXT(raster) PLUGINOFFSET(rw::d3d::D3dRaster, raster, rw::d3d::nativeRasterOffset)
+
+// Rendering
+
+void setRenderState(uint32 state, uint32 value);
+void getRenderState(uint32 state, uint32 *value);
+void setTextureStageState(uint32 stage, uint32 type, uint32 value);
+void getTextureStageState(uint32 stage, uint32 type, uint32 *value);
+void setSamplerState(uint32 stage, uint32 type, uint32 value);
+void getSamplerState(uint32 stage, uint32 type, uint32 *value);
+void flushCache(void);
+
+void setTexture(uint32 stage, Texture *tex);
+void setMaterial(const RGBA &color, const SurfaceProperties &surfaceprops, float extraSurfProp = 0.0f);
+inline void setMaterial(uint32 flags, const RGBA &color, const SurfaceProperties &surfaceprops, float extraSurfProp = 0.0f)
+{
+ static RGBA white = { 255, 255, 255, 255 };
+ if(flags & Geometry::MODULATE)
+ setMaterial(color, surfaceprops, extraSurfProp);
+ else
+ setMaterial(white, surfaceprops, extraSurfProp);
+}
+
+void setVertexShader(void *vs);
+void setPixelShader(void *ps);
+void setIndices(void *indexBuffer);
+void setStreamSource(int n, void *buffer, uint32 offset, uint32 stride);
+void setVertexDeclaration(void *declaration);
+
+void *createVertexShader(void *csosrc);
+void *createPixelShader(void *csosrc);
+void destroyVertexShader(void *shader);
+void destroyPixelShader(void *shader);
+
+
+/*
+ * Vertex shaders and common pipeline stuff
+ */
+
+// This data will be available in vertex stream 2
+struct VertexConstantData
+{
+ V3d normal;
+ RGBA color;
+ TexCoords texCoors[8];
+};
+extern void *constantVertexStream;
+
+// TODO: figure out why this even still exists...
+struct D3dShaderState
+{
+ // for VS
+ struct {
+ float32 start;
+ float32 end;
+ float32 range; // 1/(start-end)
+ float32 disable; // lower clamp
+ } fogData, fogDisable;
+ RGBA matColor;
+ SurfaceProperties surfProps;
+ float extraSurfProp;
+ float lightOffset[3];
+ int32 numDir, numPoint, numSpot;
+ RGBAf ambient;
+ // for PS
+ RGBAf fogColor;
+
+ bool fogDirty;
+};
+extern D3dShaderState d3dShaderState;
+
+// Standard Vertex shader locations
+enum
+{
+ VSLOC_combined = 0,
+ VSLOC_world = 4,
+ VSLOC_normal = 8,
+ VSLOC_matColor = 12,
+ VSLOC_surfProps = 13,
+ VSLOC_fogData = 14,
+ VSLOC_ambLight = 15,
+ VSLOC_lightOffset = 16,
+ VSLOC_lights = 17,
+ VSLOC_afterLights = VSLOC_lights + 8*3,
+
+ VSLOC_numLights = 0,
+
+ PSLOC_fogColor = 0
+};
+
+// Vertex shader bits
+enum
+{
+ // These should be low so they could be used as indices
+ VSLIGHT_DIRECT = 1,
+ VSLIGHT_POINT = 2,
+ VSLIGHT_SPOT = 4,
+ VSLIGHT_MASK = 7, // all the above
+ // less critical
+ VSLIGHT_AMBIENT = 8,
+};
+
+void lightingCB_Fix(Atomic *atomic);
+int32 lightingCB_Shader(Atomic *atomic);
+int32 lightingCB_Shader(void);
+// for VS
+void uploadMatrices(void); // no world transform
+void uploadMatrices(Matrix *worldMat);
+void setAmbient(const RGBAf &color);
+void setNumLights(int numDir, int numPoint, int numSpot);
+int32 uploadLights(WorldLights *lightData); // called by lightingCB_Shader
+
+extern void *im2dOverridePS;
+
+extern void *default_amb_VS;
+extern void *default_amb_dir_VS;
+extern void *default_all_VS;
+extern void *default_PS;
+extern void *default_tex_PS;
+extern void *im2d_VS;
+extern void *im2d_PS;
+extern void *im2d_tex_PS;
+void createDefaultShaders(void);
+void destroyDefaultShaders(void);
+
+
+}
+}
diff --git a/src/d3d/rwd3d8.h b/src/d3d/rwd3d8.h
new file mode 100644
index 0000000..422379d
--- /dev/null
+++ b/src/d3d/rwd3d8.h
@@ -0,0 +1,74 @@
+namespace rw {
+namespace d3d8 {
+
+void registerPlatformPlugins(void);
+
+struct InstanceData
+{
+ uint32 minVert;
+ int32 stride;
+ int32 numVertices;
+ int32 numIndices;
+ Material *material;
+ uint32 vertexShader;
+ uint32 primType;
+ void *indexBuffer;
+ void *vertexBuffer;
+ uint32 baseIndex;
+ uint8 vertexAlpha;
+ uint8 managed;
+ uint8 remapped;
+};
+
+struct InstanceDataHeader : rw::InstanceDataHeader
+{
+ uint16 serialNumber;
+ uint16 numMeshes;
+
+ InstanceData *inst;
+};
+
+uint32 makeFVFDeclaration(uint32 flags, int32 numTex);
+int32 getStride(uint32 flags, int32 numTex);
+
+void *destroyNativeData(void *object, int32, int32);
+Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32);
+Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32);
+int32 getSizeNativeData(void *object, int32, int32);
+void registerNativeDataPlugin(void);
+
+class ObjPipeline : public rw::ObjPipeline
+{
+public:
+ void init(void);
+ static ObjPipeline *create(void);
+
+ void (*instanceCB)(Geometry *geo, InstanceData *header);
+ void (*uninstanceCB)(Geometry *geo, InstanceData *header);
+ void (*renderCB)(Atomic *atomic, InstanceDataHeader *header);
+};
+
+void defaultInstanceCB(Geometry *geo, InstanceData *header);
+void defaultUninstanceCB(Geometry *geo, InstanceData *header);
+void defaultRenderCB(Atomic *atomic, InstanceDataHeader *header);
+
+ObjPipeline *makeDefaultPipeline(void);
+
+// Skin plugin
+
+void initSkin(void);
+ObjPipeline *makeSkinPipeline(void);
+
+// MatFX plugin
+
+void initMatFX(void);
+ObjPipeline *makeMatFXPipeline(void);
+
+// Native Texture and Raster
+
+Texture *readNativeTexture(Stream *stream);
+void writeNativeTexture(Texture *tex, Stream *stream);
+uint32 getSizeNativeTexture(Texture *tex);
+
+}
+}
diff --git a/src/d3d/rwd3d9.h b/src/d3d/rwd3d9.h
new file mode 100644
index 0000000..03eccd5
--- /dev/null
+++ b/src/d3d/rwd3d9.h
@@ -0,0 +1,112 @@
+namespace rw {
+namespace d3d9 {
+
+void registerPlatformPlugins(void);
+
+struct VertexElement
+{
+ uint16 stream;
+ uint16 offset;
+ uint8 type;
+ uint8 method;
+ uint8 usage;
+ uint8 usageIndex;
+};
+
+struct VertexStream
+{
+ void *vertexBuffer;
+ uint32 offset;
+ uint32 stride;
+ uint16 geometryFlags;
+ uint8 managed;
+ uint8 dynamicLock;
+};
+
+struct InstanceData
+{
+ uint32 numIndex;
+ uint32 minVert;
+ Material *material;
+ bool32 vertexAlpha;
+ void *vertexShader;
+ uint32 baseIndex;
+ uint32 numVertices;
+ uint32 startIndex;
+ uint32 numPrimitives;
+};
+
+struct InstanceDataHeader : rw::InstanceDataHeader
+{
+ uint32 serialNumber;
+ uint32 numMeshes;
+ void *indexBuffer;
+ uint32 primType;
+ VertexStream vertexStream[2];
+ bool32 useOffsets;
+ void *vertexDeclaration;
+ uint32 totalNumIndex;
+ uint32 totalNumVertex;
+
+ InstanceData *inst;
+};
+
+void *createVertexDeclaration(VertexElement *elements);
+void destroyVertexDeclaration(void *delaration);
+uint32 getDeclaration(void *declaration, VertexElement *elements);
+
+void drawInst_simple(d3d9::InstanceDataHeader *header, d3d9::InstanceData *inst);
+// Emulate PS2 GS alpha test FB_ONLY case: failed alpha writes to frame- but not to depth buffer
+void drawInst_GSemu(d3d9::InstanceDataHeader *header, InstanceData *inst);
+// This one switches between the above two depending on render state;
+void drawInst(d3d9::InstanceDataHeader *header, d3d9::InstanceData *inst);
+
+
+
+
+void *destroyNativeData(void *object, int32, int32);
+Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32);
+Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32);
+int32 getSizeNativeData(void *object, int32, int32);
+void registerNativeDataPlugin(void);
+
+class ObjPipeline : public rw::ObjPipeline
+{
+public:
+ void init(void);
+ static ObjPipeline *create(void);
+
+ void (*instanceCB)(Geometry *geo, InstanceDataHeader *header, bool32 reinstance);
+ void (*uninstanceCB)(Geometry *geo, InstanceDataHeader *header);
+ void (*renderCB)(Atomic *atomic, InstanceDataHeader *header);
+};
+
+void defaultInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance);
+void defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header);
+void defaultRenderCB_Fix(Atomic *atomic, InstanceDataHeader *header);
+void defaultRenderCB_Shader(Atomic *atomic, InstanceDataHeader *header);
+
+ObjPipeline *makeDefaultPipeline(void);
+
+
+// Skin plugin
+
+void initSkin(void);
+void uploadSkinMatrices(Atomic *atomic);
+void skinInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance);
+void skinRenderCB(Atomic *atomic, InstanceDataHeader *header);
+ObjPipeline *makeSkinPipeline(void);
+
+// MatFX plugin
+
+void initMatFX(void);
+ObjPipeline *makeMatFXPipeline(void);
+
+// Native Texture and Raster
+
+Texture *readNativeTexture(Stream *stream);
+void writeNativeTexture(Texture *tex, Stream *stream);
+uint32 getSizeNativeTexture(Texture *tex);
+
+}
+}
diff --git a/src/d3d/rwd3dimpl.h b/src/d3d/rwd3dimpl.h
new file mode 100644
index 0000000..82f8a13
--- /dev/null
+++ b/src/d3d/rwd3dimpl.h
@@ -0,0 +1,81 @@
+namespace rw {
+namespace d3d {
+
+#ifdef RW_D3D9
+void openIm2D(void);
+void closeIm2D(void);
+void im2DRenderLine(void *vertices, int32 numVertices, int32 vert1, int32 vert2);
+void im2DRenderTriangle(void *vertices, int32 numVertices, int32 vert1, int32 vert2, int32 vert3);
+void im2DRenderPrimitive(PrimitiveType primType, void *vertices, int32 numVertices);
+void im2DRenderIndexedPrimitive(PrimitiveType primType, void *vertices, int32 numVertices, void *indices, int32 numIndices);
+
+void openIm3D(void);
+void closeIm3D(void);
+void im3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags);
+void im3DRenderPrimitive(PrimitiveType primType);
+void im3DRenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices);
+void im3DEnd(void);
+
+
+struct DisplayMode
+{
+ D3DDISPLAYMODE mode;
+ uint32 flags;
+};
+
+struct D3d9Globals
+{
+ HWND window;
+
+ IDirect3D9 *d3d9;
+ int numAdapters;
+ int adapter;
+ D3DCAPS9 caps;
+ DisplayMode *modes;
+ int numModes;
+ int currentMode;
+ DisplayMode startMode;
+
+ uint32 msLevel;
+
+ D3DPRESENT_PARAMETERS present;
+
+ IDirect3DSurface9 *defaultRenderTarget;
+ IDirect3DSurface9 *defaultDepthSurf;
+
+ int numTextures;
+ int numVertexShaders;
+ int numPixelShaders;
+ int numVertexBuffers;
+ int numIndexBuffers;
+ int numVertexDeclarations;
+};
+
+extern D3d9Globals d3d9Globals;
+
+void addVidmemRaster(Raster *raster);
+void removeVidmemRaster(Raster *raster);
+
+void addDynamicVB(uint32 length, uint32 fvf, IDirect3DVertexBuffer9 **buf); // NB: don't share this pointer
+void removeDynamicVB(IDirect3DVertexBuffer9 **buf);
+
+void addDynamicIB(uint32 length, IDirect3DIndexBuffer9 **buf); // NB: don't share this pointer
+void removeDynamicIB(IDirect3DIndexBuffer9 **buf);
+
+
+int findFormatDepth(uint32 format);
+void evictD3D9Raster(Raster *raster);
+
+#endif
+
+Raster *rasterCreate(Raster *raster);
+uint8 *rasterLock(Raster *raster, int32 level, int32 lockMode);
+void rasterUnlock(Raster *raster, int32 level);
+int32 rasterNumLevels(Raster *raster);
+bool32 imageFindRasterFormat(Image *img, int32 type,
+ int32 *width, int32 *height, int32 *depth, int32 *format);
+bool32 rasterFromImage(Raster *raster, Image *image);
+Image *rasterToImage(Raster *raster);
+
+}
+}
diff --git a/src/d3d/rwxbox.h b/src/d3d/rwxbox.h
new file mode 100644
index 0000000..1abebe1
--- /dev/null
+++ b/src/d3d/rwxbox.h
@@ -0,0 +1,195 @@
+namespace rw {
+namespace xbox {
+
+void registerPlatformPlugins(void);
+
+extern int v3dFormatMap[6];
+extern int v2dFormatMap[6];
+
+struct InstanceData
+{
+ uint32 minVert;
+ int32 numVertices;
+ int32 numIndices;
+ void *indexBuffer;
+ Material *material;
+ uint32 vertexShader;
+};
+
+struct InstanceDataHeader : rw::InstanceDataHeader
+{
+ int32 size;
+ uint16 serialNumber;
+ uint16 numMeshes;
+ uint32 primType;
+ int32 numVertices;
+ int32 stride;
+ void *vertexBuffer;
+ bool32 vertexAlpha;
+ InstanceData *begin;
+ InstanceData *end;
+
+ uint8 *data;
+};
+
+void *destroyNativeData(void *object, int32, int32);
+Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32);
+Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32);
+int32 getSizeNativeData(void *object, int32, int32);
+void registerNativeDataPlugin(void);
+
+class ObjPipeline : public rw::ObjPipeline
+{
+public:
+ void init(void);
+ static ObjPipeline *create(void);
+
+ void (*instanceCB)(Geometry *geo, InstanceDataHeader *header);
+ void (*uninstanceCB)(Geometry *geo, InstanceDataHeader *header);
+};
+
+ObjPipeline *makeDefaultPipeline(void);
+
+void defaultInstanceCB(Geometry *geo, InstanceDataHeader *header);
+void defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header);
+
+// Skin plugin
+
+Stream *readNativeSkin(Stream *stream, int32, void *object, int32 offset);
+Stream *writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset);
+int32 getSizeNativeSkin(void *object, int32 offset);
+
+void initSkin(void);
+ObjPipeline *makeSkinPipeline(void);
+
+// MatFX plugin
+
+void initMatFX(void);
+ObjPipeline *makeMatFXPipeline(void);
+
+// Vertex Format plugin
+
+extern uint32 vertexFormatSizes[6];
+
+uint32 *getVertexFmt(Geometry *g);
+uint32 makeVertexFmt(int32 flags, uint32 numTexSets);
+uint32 getVertexFmtStride(uint32 fmt);
+
+void registerVertexFormatPlugin(void);
+
+// Native Texture and Raster
+
+struct XboxRaster
+{
+ void *texture;
+ void *palette;
+ uint32 format;
+ uint32 bpp; // bytes per pixel
+ bool hasAlpha;
+ bool customFormat;
+ bool32 unknownFlag;
+};
+
+int32 getLevelSize(Raster *raster, int32 level);
+
+extern int32 nativeRasterOffset;
+void registerNativeRaster(void);
+#define GETXBOXRASTEREXT(raster) PLUGINOFFSET(rw::xbox::XboxRaster, raster, rw::xbox::nativeRasterOffset)
+
+Texture *readNativeTexture(Stream *stream);
+void writeNativeTexture(Texture *tex, Stream *stream);
+uint32 getSizeNativeTexture(Texture *tex);
+
+enum {
+ D3DFMT_UNKNOWN = 0xFFFFFFFF,
+
+ /* Swizzled formats */
+
+ D3DFMT_A8R8G8B8 = 0x00000006,
+ D3DFMT_X8R8G8B8 = 0x00000007,
+ D3DFMT_R5G6B5 = 0x00000005,
+ D3DFMT_R6G5B5 = 0x00000027,
+ D3DFMT_X1R5G5B5 = 0x00000003,
+ D3DFMT_A1R5G5B5 = 0x00000002,
+ D3DFMT_A4R4G4B4 = 0x00000004,
+ D3DFMT_A8 = 0x00000019,
+ D3DFMT_A8B8G8R8 = 0x0000003A,
+ D3DFMT_B8G8R8A8 = 0x0000003B,
+ D3DFMT_R4G4B4A4 = 0x00000039,
+ D3DFMT_R5G5B5A1 = 0x00000038,
+ D3DFMT_R8G8B8A8 = 0x0000003C,
+ D3DFMT_R8B8 = 0x00000029,
+ D3DFMT_G8B8 = 0x00000028,
+
+ D3DFMT_P8 = 0x0000000B,
+
+ D3DFMT_L8 = 0x00000000,
+ D3DFMT_A8L8 = 0x0000001A,
+ D3DFMT_AL8 = 0x00000001,
+ D3DFMT_L16 = 0x00000032,
+
+ D3DFMT_V8U8 = 0x00000028,
+ D3DFMT_L6V5U5 = 0x00000027,
+ D3DFMT_X8L8V8U8 = 0x00000007,
+ D3DFMT_Q8W8V8U8 = 0x0000003A,
+ D3DFMT_V16U16 = 0x00000033,
+
+ D3DFMT_D16_LOCKABLE = 0x0000002C,
+ D3DFMT_D16 = 0x0000002C,
+ D3DFMT_D24S8 = 0x0000002A,
+ D3DFMT_F16 = 0x0000002D,
+ D3DFMT_F24S8 = 0x0000002B,
+
+ /* YUV formats */
+
+ D3DFMT_YUY2 = 0x00000024,
+ D3DFMT_UYVY = 0x00000025,
+
+ /* Compressed formats */
+
+ D3DFMT_DXT1 = 0x0000000C,
+ D3DFMT_DXT2 = 0x0000000E,
+ D3DFMT_DXT3 = 0x0000000E,
+ D3DFMT_DXT4 = 0x0000000F,
+ D3DFMT_DXT5 = 0x0000000F,
+
+ /* Linear formats */
+
+ D3DFMT_LIN_A1R5G5B5 = 0x00000010,
+ D3DFMT_LIN_A4R4G4B4 = 0x0000001D,
+ D3DFMT_LIN_A8 = 0x0000001F,
+ D3DFMT_LIN_A8B8G8R8 = 0x0000003F,
+ D3DFMT_LIN_A8R8G8B8 = 0x00000012,
+ D3DFMT_LIN_B8G8R8A8 = 0x00000040,
+ D3DFMT_LIN_G8B8 = 0x00000017,
+ D3DFMT_LIN_R4G4B4A4 = 0x0000003E,
+ D3DFMT_LIN_R5G5B5A1 = 0x0000003D,
+ D3DFMT_LIN_R5G6B5 = 0x00000011,
+ D3DFMT_LIN_R6G5B5 = 0x00000037,
+ D3DFMT_LIN_R8B8 = 0x00000016,
+ D3DFMT_LIN_R8G8B8A8 = 0x00000041,
+ D3DFMT_LIN_X1R5G5B5 = 0x0000001C,
+ D3DFMT_LIN_X8R8G8B8 = 0x0000001E,
+
+ D3DFMT_LIN_A8L8 = 0x00000020,
+ D3DFMT_LIN_AL8 = 0x0000001B,
+ D3DFMT_LIN_L16 = 0x00000035,
+ D3DFMT_LIN_L8 = 0x00000013,
+
+ D3DFMT_LIN_V16U16 = 0x00000036,
+ D3DFMT_LIN_V8U8 = 0x00000017,
+ D3DFMT_LIN_L6V5U5 = 0x00000037,
+ D3DFMT_LIN_X8L8V8U8 = 0x0000001E,
+ D3DFMT_LIN_Q8W8V8U8 = 0x00000012,
+
+ D3DFMT_LIN_D24S8 = 0x0000002E,
+ D3DFMT_LIN_F24S8 = 0x0000002F,
+ D3DFMT_LIN_D16 = 0x00000030,
+ D3DFMT_LIN_F16 = 0x00000031,
+
+ D3DFMT_VERTEXDATA = 100,
+ D3DFMT_INDEX16 = 101
+};
+
+}
+}
diff --git a/src/d3d/rwxboximpl.h b/src/d3d/rwxboximpl.h
new file mode 100644
index 0000000..524ec28
--- /dev/null
+++ b/src/d3d/rwxboximpl.h
@@ -0,0 +1,11 @@
+namespace rw {
+namespace xbox {
+
+Raster *rasterCreate(Raster *raster);
+uint8 *rasterLock(Raster *raster, int32 level, int32 lockMode);
+void rasterUnlock(Raster*, int32);
+int32 rasterNumLevels(Raster *raster);
+Image *rasterToImage(Raster *raster);
+
+}
+}
diff --git a/src/d3d/shaders/default_PS.h b/src/d3d/shaders/default_PS.h
new file mode 100644
index 0000000..9f2cb31
--- /dev/null
+++ b/src/d3d/shaders/default_PS.h
@@ -0,0 +1,72 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T ps_2_0 /Fh default_PS.h default_PS.hlsl
+//
+//
+// Parameters:
+//
+// float4 fogColor;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fogColor c0 1
+//
+
+ ps_2_0
+ dcl t0.xyz
+ dcl v0
+ add r0.xyz, v0, -c0
+ mad r0.xyz, t0.z, r0, c0
+ mov r0.w, v0.w
+ mov oC0, r0
+
+// approximately 4 instruction slots used
+#endif
+
+const BYTE g_ps20_main[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 34, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 83, 0,
+ 0, 0, 0, 2, 255, 255,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 76, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 111, 103, 67, 111, 108,
+ 111, 114, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 115,
+ 95, 50, 95, 48, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 57, 46,
+ 50, 57, 46, 57, 53, 50,
+ 46, 51, 49, 49, 49, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 144,
+ 2, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 144,
+ 0, 0, 228, 161, 4, 0,
+ 0, 4, 0, 0, 7, 128,
+ 0, 0, 170, 176, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 0, 0, 255, 144,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/default_PS.hlsl b/src/d3d/shaders/default_PS.hlsl
new file mode 100644
index 0000000..eaf279d
--- /dev/null
+++ b/src/d3d/shaders/default_PS.hlsl
@@ -0,0 +1,19 @@
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0;
+ float4 Color : COLOR0;
+};
+
+sampler2D tex0 : register(s0);
+
+float4 fogColor : register(c0);
+
+float4 main(VS_out input) : COLOR
+{
+ float4 color = input.Color;
+#ifdef TEX
+ color *= tex2D(tex0, input.TexCoord0.xy);
+#endif
+ color.rgb = lerp(fogColor.rgb, color.rgb, input.TexCoord0.z);
+ return color;
+}
diff --git a/src/d3d/shaders/default_VS.hlsl b/src/d3d/shaders/default_VS.hlsl
new file mode 100644
index 0000000..d2f5452
--- /dev/null
+++ b/src/d3d/shaders/default_VS.hlsl
@@ -0,0 +1,51 @@
+#include "standardConstants.h"
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 TexCoord : TEXCOORD0;
+ float4 Prelight : COLOR0;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0; // also fog
+ float4 Color : COLOR0;
+};
+
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ output.Position = mul(combinedMat, input.Position);
+ float3 Vertex = mul(worldMat, input.Position).xyz;
+ float3 Normal = mul(normalMat, input.Normal);
+
+ output.TexCoord0.xy = input.TexCoord;
+
+ output.Color = input.Prelight;
+ output.Color.rgb += ambientLight.rgb * surfAmbient;
+
+ int i;
+#ifdef DIRECTIONALS
+ for(i = 0; i < numDirLights; i++)
+ output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
+#endif
+#ifdef POINTLIGHTS
+ for(i = 0; i < numPointLights; i++)
+ output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
+#endif
+#ifdef SPOTLIGHTS
+ for(i = 0; i < numSpotLights; i++)
+ output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
+#endif
+ // PS2 clamps before material color
+ output.Color = clamp(output.Color, 0.0, 1.0);
+ output.Color *= matCol;
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/d3d/shaders/default_all_VS.h b/src/d3d/shaders/default_all_VS.h
new file mode 100644
index 0000000..e6afa5d
--- /dev/null
+++ b/src/d3d/shaders/default_all_VS.h
@@ -0,0 +1,491 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /DDIRECTIONALS /DPOINTLIGHTS /DSPOTLIGHTS /Fh
+// default_all_VS.h default_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4x4 combinedMat;
+// int4 firstLight;
+// float4 fogData;
+//
+// struct
+// {
+// float4 color;
+// float4 position;
+// float4 direction;
+//
+// } lights[8];
+//
+// float4 matCol;
+// float3x3 normalMat;
+// int numDirLights;
+// int numPointLights;
+// int numSpotLights;
+// float4 surfProps;
+// float4x4 worldMat;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// -------------- ----- ----
+// numDirLights i0 1
+// numPointLights i1 1
+// numSpotLights i2 1
+// combinedMat c0 4
+// worldMat c4 4
+// normalMat c8 3
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// firstLight c16 1
+// lights c17 24
+//
+
+ vs_2_0
+ def c11, 0, 3, 1, 0
+ dcl_position v0
+ dcl_normal v1
+ dcl_texcoord v2
+ dcl_color v3
+ mul r0, v0.y, c1
+ mad r0, c0, v0.x, r0
+ mad r0, c2, v0.z, r0
+ mad r0, c3, v0.w, r0
+ mov oPos, r0
+ mul r0.xyz, v0.y, c5
+ mad r0.xyz, c4, v0.x, r0
+ mad r0.xyz, c6, v0.z, r0
+ mad r0.xyz, c7, v0.w, r0
+ mul r1.xyz, v1.y, c9
+ mad r1.xyz, c8, v1.x, r1
+ mad r1.xyz, c10, v1.z, r1
+ mov r2.x, c13.x
+ mad r2.xyz, c15, r2.x, v3
+ mov r3.xyz, r2
+ mov r1.w, c11.x
+ rep i0
+ add r2.w, r1.w, c16.x
+ mul r2.w, r2.w, c11.y
+ mova a0.x, r2.w
+ dp3 r2.w, r1, -c19[a0.x]
+ max r2.w, r2.w, c11.x
+ mul r4.xyz, r2.w, c17[a0.x]
+ mad r3.xyz, r4, c13.z, r3
+ add r1.w, r1.w, c11.z
+ endrep
+ mov r2.xyz, r3
+ mov r1.w, c11.x
+ rep i1
+ add r2.w, r1.w, c16.y
+ mul r2.w, r2.w, c11.y
+ mova a0.x, r2.w
+ add r4.xyz, r0, -c18[a0.x]
+ dp3 r2.w, r4, r4
+ rsq r2.w, r2.w
+ mul r4.xyz, r2.w, r4
+ dp3 r3.w, r1, -r4
+ max r3.w, r3.w, c11.x
+ mul r4.xyz, r3.w, c17[a0.x]
+ rcp r2.w, r2.w
+ rcp r3.w, c17[a0.x].w
+ mad r2.w, r2.w, -r3.w, c11.z
+ max r2.w, r2.w, c11.x
+ mul r4.xyz, r2.w, r4
+ mad r2.xyz, r4, c13.z, r2
+ add r1.w, r1.w, c11.z
+ endrep
+ mov r3.xyz, r2
+ mov r1.w, c11.x
+ rep i2
+ add r2.w, r1.w, c16.z
+ mul r2.w, r2.w, c11.y
+ mova a0.x, r2.w
+ add r4.xyz, r0, -c18[a0.x]
+ dp3 r2.w, r4, r4
+ rsq r2.w, r2.w
+ mul r4.xyz, r2.w, r4
+ dp3 r4.w, r1, -r4
+ dp3 r4.x, r4, c19[a0.x]
+ max r4.y, r4.w, c11.x
+ mov r4.z, c11.z
+ add r4.xz, r4, c18[a0.x].w
+ rcp r4.z, r4.z
+ mul r4.x, r4.z, r4.x
+ slt r4.z, r4.x, c11.x
+ mad r4.y, r4.z, -r4.y, r4.y
+ max r4.x, r4.x, c19[a0.x].w
+ mul r4.x, r4.x, r4.y
+ mul r4.xyz, r4.x, c17[a0.x]
+ rcp r2.w, r2.w
+ rcp r4.w, c17[a0.x].w
+ mad r2.w, r2.w, -r4.w, c11.z
+ max r2.w, r2.w, c11.x
+ mul r4.xyz, r2.w, r4
+ mad r3.xyz, r4, c13.z, r3
+ add r1.w, r1.w, c11.z
+ endrep
+ mov r3.w, v3.w
+ max r1, r3, c11.x
+ min r1, r1, c11.z
+ mul oD0, r1, c12
+ add r0.x, r0.w, -c14.y
+ mul r0.x, r0.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c11.z
+ mov oT0.xy, v2
+
+// approximately 95 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 158, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 67, 2,
+ 0, 0, 0, 2, 254, 255,
+ 12, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 60, 2, 0, 0, 12, 1,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 28, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 1, 0, 0, 2, 0,
+ 0, 0, 4, 0, 2, 0,
+ 56, 1, 0, 0, 0, 0,
+ 0, 0, 72, 1, 0, 0,
+ 2, 0, 16, 0, 1, 0,
+ 66, 0, 84, 1, 0, 0,
+ 0, 0, 0, 0, 100, 1,
+ 0, 0, 2, 0, 14, 0,
+ 1, 0, 58, 0, 28, 1,
+ 0, 0, 0, 0, 0, 0,
+ 108, 1, 0, 0, 2, 0,
+ 17, 0, 24, 0, 70, 0,
+ 184, 1, 0, 0, 0, 0,
+ 0, 0, 200, 1, 0, 0,
+ 2, 0, 12, 0, 1, 0,
+ 50, 0, 28, 1, 0, 0,
+ 0, 0, 0, 0, 207, 1,
+ 0, 0, 2, 0, 8, 0,
+ 3, 0, 34, 0, 220, 1,
+ 0, 0, 0, 0, 0, 0,
+ 236, 1, 0, 0, 1, 0,
+ 0, 0, 1, 0, 2, 0,
+ 252, 1, 0, 0, 0, 0,
+ 0, 0, 12, 2, 0, 0,
+ 1, 0, 1, 0, 1, 0,
+ 6, 0, 252, 1, 0, 0,
+ 0, 0, 0, 0, 27, 2,
+ 0, 0, 1, 0, 2, 0,
+ 1, 0, 10, 0, 252, 1,
+ 0, 0, 0, 0, 0, 0,
+ 41, 2, 0, 0, 2, 0,
+ 13, 0, 1, 0, 54, 0,
+ 28, 1, 0, 0, 0, 0,
+ 0, 0, 51, 2, 0, 0,
+ 2, 0, 4, 0, 4, 0,
+ 18, 0, 56, 1, 0, 0,
+ 0, 0, 0, 0, 97, 109,
+ 98, 105, 101, 110, 116, 76,
+ 105, 103, 104, 116, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 109, 98, 105, 110,
+ 101, 100, 77, 97, 116, 0,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 102, 105,
+ 114, 115, 116, 76, 105, 103,
+ 104, 116, 0, 171, 1, 0,
+ 2, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 102, 111, 103, 68,
+ 97, 116, 97, 0, 108, 105,
+ 103, 104, 116, 115, 0, 99,
+ 111, 108, 111, 114, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 111, 115, 105, 116, 105,
+ 111, 110, 0, 100, 105, 114,
+ 101, 99, 116, 105, 111, 110,
+ 0, 171, 115, 1, 0, 0,
+ 124, 1, 0, 0, 140, 1,
+ 0, 0, 124, 1, 0, 0,
+ 149, 1, 0, 0, 124, 1,
+ 0, 0, 5, 0, 0, 0,
+ 1, 0, 12, 0, 8, 0,
+ 3, 0, 160, 1, 0, 0,
+ 109, 97, 116, 67, 111, 108,
+ 0, 110, 111, 114, 109, 97,
+ 108, 77, 97, 116, 0, 171,
+ 171, 171, 3, 0, 3, 0,
+ 3, 0, 3, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 110, 117, 109, 68, 105, 114,
+ 76, 105, 103, 104, 116, 115,
+ 0, 171, 171, 171, 0, 0,
+ 2, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 110, 117, 109, 80,
+ 111, 105, 110, 116, 76, 105,
+ 103, 104, 116, 115, 0, 110,
+ 117, 109, 83, 112, 111, 116,
+ 76, 105, 103, 104, 116, 115,
+ 0, 115, 117, 114, 102, 80,
+ 114, 111, 112, 115, 0, 119,
+ 111, 114, 108, 100, 77, 97,
+ 116, 0, 118, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 57, 46, 50, 57,
+ 46, 57, 53, 50, 46, 51,
+ 49, 49, 49, 0, 81, 0,
+ 0, 5, 11, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 64, 64, 0, 0, 128, 63,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 144, 31, 0,
+ 0, 2, 3, 0, 0, 128,
+ 1, 0, 15, 144, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 2, 0, 15, 144, 31, 0,
+ 0, 2, 10, 0, 0, 128,
+ 3, 0, 15, 144, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 85, 144, 1, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 228, 160, 0, 0, 0, 144,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 2, 0, 228, 160, 0, 0,
+ 170, 144, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 3, 0, 228, 160,
+ 0, 0, 255, 144, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 0, 15, 192, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 85, 144, 5, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 4, 0, 228, 160,
+ 0, 0, 0, 144, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 7, 128, 6, 0,
+ 228, 160, 0, 0, 170, 144,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 7, 128,
+ 7, 0, 228, 160, 0, 0,
+ 255, 144, 0, 0, 228, 128,
+ 5, 0, 0, 3, 1, 0,
+ 7, 128, 1, 0, 85, 144,
+ 9, 0, 228, 160, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 8, 0, 228, 160, 1, 0,
+ 0, 144, 1, 0, 228, 128,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 10, 0, 228, 160,
+ 1, 0, 170, 144, 1, 0,
+ 228, 128, 1, 0, 0, 2,
+ 2, 0, 1, 128, 13, 0,
+ 0, 160, 4, 0, 0, 4,
+ 2, 0, 7, 128, 15, 0,
+ 228, 160, 2, 0, 0, 128,
+ 3, 0, 228, 144, 1, 0,
+ 0, 2, 3, 0, 7, 128,
+ 2, 0, 228, 128, 1, 0,
+ 0, 2, 1, 0, 8, 128,
+ 11, 0, 0, 160, 38, 0,
+ 0, 1, 0, 0, 228, 240,
+ 2, 0, 0, 3, 2, 0,
+ 8, 128, 1, 0, 255, 128,
+ 16, 0, 0, 160, 5, 0,
+ 0, 3, 2, 0, 8, 128,
+ 2, 0, 255, 128, 11, 0,
+ 85, 160, 46, 0, 0, 2,
+ 0, 0, 1, 176, 2, 0,
+ 255, 128, 8, 0, 0, 4,
+ 2, 0, 8, 128, 1, 0,
+ 228, 128, 19, 32, 228, 161,
+ 0, 0, 0, 176, 11, 0,
+ 0, 3, 2, 0, 8, 128,
+ 2, 0, 255, 128, 11, 0,
+ 0, 160, 5, 0, 0, 4,
+ 4, 0, 7, 128, 2, 0,
+ 255, 128, 17, 32, 228, 160,
+ 0, 0, 0, 176, 4, 0,
+ 0, 4, 3, 0, 7, 128,
+ 4, 0, 228, 128, 13, 0,
+ 170, 160, 3, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 11, 0, 170, 160, 39, 0,
+ 0, 0, 1, 0, 0, 2,
+ 2, 0, 7, 128, 3, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 0, 8, 128, 11, 0,
+ 0, 160, 38, 0, 0, 1,
+ 1, 0, 228, 240, 2, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 128, 16, 0,
+ 85, 160, 5, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 11, 0, 85, 160,
+ 46, 0, 0, 2, 0, 0,
+ 1, 176, 2, 0, 255, 128,
+ 2, 0, 0, 4, 4, 0,
+ 7, 128, 0, 0, 228, 128,
+ 18, 32, 228, 161, 0, 0,
+ 0, 176, 8, 0, 0, 3,
+ 2, 0, 8, 128, 4, 0,
+ 228, 128, 4, 0, 228, 128,
+ 7, 0, 0, 2, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 2, 0, 255, 128,
+ 4, 0, 228, 128, 8, 0,
+ 0, 3, 3, 0, 8, 128,
+ 1, 0, 228, 128, 4, 0,
+ 228, 129, 11, 0, 0, 3,
+ 3, 0, 8, 128, 3, 0,
+ 255, 128, 11, 0, 0, 160,
+ 5, 0, 0, 4, 4, 0,
+ 7, 128, 3, 0, 255, 128,
+ 17, 32, 228, 160, 0, 0,
+ 0, 176, 6, 0, 0, 2,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 6, 0, 0, 3,
+ 3, 0, 8, 128, 17, 32,
+ 255, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 3, 0, 255, 129, 11, 0,
+ 170, 160, 11, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 11, 0, 0, 160,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 2, 0, 255, 128,
+ 4, 0, 228, 128, 4, 0,
+ 0, 4, 2, 0, 7, 128,
+ 4, 0, 228, 128, 13, 0,
+ 170, 160, 2, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 11, 0, 170, 160, 39, 0,
+ 0, 0, 1, 0, 0, 2,
+ 3, 0, 7, 128, 2, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 0, 8, 128, 11, 0,
+ 0, 160, 38, 0, 0, 1,
+ 2, 0, 228, 240, 2, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 128, 16, 0,
+ 170, 160, 5, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 11, 0, 85, 160,
+ 46, 0, 0, 2, 0, 0,
+ 1, 176, 2, 0, 255, 128,
+ 2, 0, 0, 4, 4, 0,
+ 7, 128, 0, 0, 228, 128,
+ 18, 32, 228, 161, 0, 0,
+ 0, 176, 8, 0, 0, 3,
+ 2, 0, 8, 128, 4, 0,
+ 228, 128, 4, 0, 228, 128,
+ 7, 0, 0, 2, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 2, 0, 255, 128,
+ 4, 0, 228, 128, 8, 0,
+ 0, 3, 4, 0, 8, 128,
+ 1, 0, 228, 128, 4, 0,
+ 228, 129, 8, 0, 0, 4,
+ 4, 0, 1, 128, 4, 0,
+ 228, 128, 19, 32, 228, 160,
+ 0, 0, 0, 176, 11, 0,
+ 0, 3, 4, 0, 2, 128,
+ 4, 0, 255, 128, 11, 0,
+ 0, 160, 1, 0, 0, 2,
+ 4, 0, 4, 128, 11, 0,
+ 170, 160, 2, 0, 0, 4,
+ 4, 0, 5, 128, 4, 0,
+ 228, 128, 18, 32, 255, 160,
+ 0, 0, 0, 176, 6, 0,
+ 0, 2, 4, 0, 4, 128,
+ 4, 0, 170, 128, 5, 0,
+ 0, 3, 4, 0, 1, 128,
+ 4, 0, 170, 128, 4, 0,
+ 0, 128, 12, 0, 0, 3,
+ 4, 0, 4, 128, 4, 0,
+ 0, 128, 11, 0, 0, 160,
+ 4, 0, 0, 4, 4, 0,
+ 2, 128, 4, 0, 170, 128,
+ 4, 0, 85, 129, 4, 0,
+ 85, 128, 11, 0, 0, 4,
+ 4, 0, 1, 128, 4, 0,
+ 0, 128, 19, 32, 255, 160,
+ 0, 0, 0, 176, 5, 0,
+ 0, 3, 4, 0, 1, 128,
+ 4, 0, 0, 128, 4, 0,
+ 85, 128, 5, 0, 0, 4,
+ 4, 0, 7, 128, 4, 0,
+ 0, 128, 17, 32, 228, 160,
+ 0, 0, 0, 176, 6, 0,
+ 0, 2, 2, 0, 8, 128,
+ 2, 0, 255, 128, 6, 0,
+ 0, 3, 4, 0, 8, 128,
+ 17, 32, 255, 160, 0, 0,
+ 0, 176, 4, 0, 0, 4,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 4, 0, 255, 129,
+ 11, 0, 170, 160, 11, 0,
+ 0, 3, 2, 0, 8, 128,
+ 2, 0, 255, 128, 11, 0,
+ 0, 160, 5, 0, 0, 3,
+ 4, 0, 7, 128, 2, 0,
+ 255, 128, 4, 0, 228, 128,
+ 4, 0, 0, 4, 3, 0,
+ 7, 128, 4, 0, 228, 128,
+ 13, 0, 170, 160, 3, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 11, 0, 170, 160,
+ 39, 0, 0, 0, 1, 0,
+ 0, 2, 3, 0, 8, 128,
+ 3, 0, 255, 144, 11, 0,
+ 0, 3, 1, 0, 15, 128,
+ 3, 0, 228, 128, 11, 0,
+ 0, 160, 10, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 11, 0, 170, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 208, 1, 0, 228, 128,
+ 12, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 14, 0,
+ 85, 161, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 14, 0, 170, 160,
+ 11, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 255, 160, 10, 0,
+ 0, 3, 0, 0, 4, 224,
+ 0, 0, 0, 128, 11, 0,
+ 170, 160, 1, 0, 0, 2,
+ 0, 0, 3, 224, 2, 0,
+ 228, 144, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/default_amb_VS.h b/src/d3d/shaders/default_amb_VS.h
new file mode 100644
index 0000000..6c22e23
--- /dev/null
+++ b/src/d3d/shaders/default_amb_VS.h
@@ -0,0 +1,156 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /Fh default_amb_VS.h default_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4x4 combinedMat;
+// float4 fogData;
+// float4 matCol;
+// float4 surfProps;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// combinedMat c0 4
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+//
+
+ vs_2_0
+ def c4, 0, 1, 0, 0
+ dcl_position v0
+ dcl_texcoord v1
+ dcl_color v2
+ mov r0.xyz, c15
+ mad r0.xyz, r0, c13.x, v2
+ mov r0.w, v2.w
+ max r0, r0, c4.x
+ min r0, r0, c4.y
+ mul oD0, r0, c12
+ mul r0, v0.y, c1
+ mad r0, c0, v0.x, r0
+ mad r0, c2, v0.z, r0
+ mad r0, c3, v0.w, r0
+ add r1.x, r0.w, -c14.y
+ mov oPos, r0
+ mul r0.x, r1.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c4.y
+ mov oT0.xy, v1
+
+// approximately 16 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 69, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 220, 0,
+ 0, 0, 0, 2, 254, 255,
+ 5, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 213, 0, 0, 0, 128, 0,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 160, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 2, 0,
+ 172, 0, 0, 0, 0, 0,
+ 0, 0, 188, 0, 0, 0,
+ 2, 0, 14, 0, 1, 0,
+ 58, 0, 144, 0, 0, 0,
+ 0, 0, 0, 0, 196, 0,
+ 0, 0, 2, 0, 12, 0,
+ 1, 0, 50, 0, 144, 0,
+ 0, 0, 0, 0, 0, 0,
+ 203, 0, 0, 0, 2, 0,
+ 13, 0, 1, 0, 54, 0,
+ 144, 0, 0, 0, 0, 0,
+ 0, 0, 97, 109, 98, 105,
+ 101, 110, 116, 76, 105, 103,
+ 104, 116, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 99, 111,
+ 109, 98, 105, 110, 101, 100,
+ 77, 97, 116, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 102, 111, 103, 68,
+ 97, 116, 97, 0, 109, 97,
+ 116, 67, 111, 108, 0, 115,
+ 117, 114, 102, 80, 114, 111,
+ 112, 115, 0, 118, 115, 95,
+ 50, 95, 48, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 57, 46, 50,
+ 57, 46, 57, 53, 50, 46,
+ 51, 49, 49, 49, 0, 171,
+ 171, 171, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 0, 0, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 31, 0, 0, 2,
+ 5, 0, 0, 128, 1, 0,
+ 15, 144, 31, 0, 0, 2,
+ 10, 0, 0, 128, 2, 0,
+ 15, 144, 1, 0, 0, 2,
+ 0, 0, 7, 128, 15, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 13, 0, 0, 160,
+ 2, 0, 228, 144, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 2, 0, 255, 144, 11, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 4, 0,
+ 0, 160, 10, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 4, 0, 85, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 208, 0, 0, 228, 128,
+ 12, 0, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 85, 144, 1, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 228, 160, 0, 0, 0, 144,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 2, 0, 228, 160, 0, 0,
+ 170, 144, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 3, 0, 228, 160,
+ 0, 0, 255, 144, 0, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 1, 128, 0, 0,
+ 255, 128, 14, 0, 85, 161,
+ 1, 0, 0, 2, 0, 0,
+ 15, 192, 0, 0, 228, 128,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 1, 0, 0, 128,
+ 14, 0, 170, 160, 11, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 0, 128, 14, 0,
+ 255, 160, 10, 0, 0, 3,
+ 0, 0, 4, 224, 0, 0,
+ 0, 128, 4, 0, 85, 160,
+ 1, 0, 0, 2, 0, 0,
+ 3, 224, 1, 0, 228, 144,
+ 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/default_amb_dir_VS.h b/src/d3d/shaders/default_amb_dir_VS.h
new file mode 100644
index 0000000..768042f
--- /dev/null
+++ b/src/d3d/shaders/default_amb_dir_VS.h
@@ -0,0 +1,272 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /DDIRECTIONALS /Fh default_amb_dir_VS.h
+// default_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4x4 combinedMat;
+// int4 firstLight;
+// float4 fogData;
+//
+// struct
+// {
+// float4 color;
+// float4 position;
+// float4 direction;
+//
+// } lights[8];
+//
+// float4 matCol;
+// float3x3 normalMat;
+// int numDirLights;
+// float4 surfProps;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// numDirLights i0 1
+// combinedMat c0 4
+// normalMat c8 3
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// firstLight c16 1
+// lights c17 24
+//
+
+ vs_2_0
+ def c4, 0, 3, 1, 0
+ dcl_position v0
+ dcl_normal v1
+ dcl_texcoord v2
+ dcl_color v3
+ mul r0, v0.y, c1
+ mad r0, c0, v0.x, r0
+ mad r0, c2, v0.z, r0
+ mad r0, c3, v0.w, r0
+ mov oPos, r0
+ mul r0.xyz, v1.y, c9
+ mad r0.xyz, c8, v1.x, r0
+ mad r0.xyz, c10, v1.z, r0
+ mov r1.x, c13.x
+ mad r1.xyz, c15, r1.x, v3
+ mov r2.xyz, r1
+ mov r1.w, c4.x
+ rep i0
+ add r3.x, r1.w, c16.x
+ mul r3.x, r3.x, c4.y
+ mova a0.x, r3.x
+ dp3 r3.x, r0, -c19[a0.x]
+ max r3.x, r3.x, c4.x
+ mul r3.xyz, r3.x, c17[a0.x]
+ mad r2.xyz, r3, c13.z, r2
+ add r1.w, r1.w, c4.z
+ endrep
+ mov r2.w, v3.w
+ max r1, r2, c4.x
+ min r1, r1, c4.z
+ mul oD0, r1, c12
+ add r0.x, r0.w, -c14.y
+ mul r0.x, r0.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c4.z
+ mov oT0.xy, v2
+
+// approximately 34 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 134, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 225, 1,
+ 0, 0, 0, 2, 254, 255,
+ 9, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 218, 1, 0, 0, 208, 0,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 224, 0,
+ 0, 0, 0, 0, 0, 0,
+ 240, 0, 0, 0, 2, 0,
+ 0, 0, 4, 0, 2, 0,
+ 252, 0, 0, 0, 0, 0,
+ 0, 0, 12, 1, 0, 0,
+ 2, 0, 16, 0, 1, 0,
+ 66, 0, 24, 1, 0, 0,
+ 0, 0, 0, 0, 40, 1,
+ 0, 0, 2, 0, 14, 0,
+ 1, 0, 58, 0, 224, 0,
+ 0, 0, 0, 0, 0, 0,
+ 48, 1, 0, 0, 2, 0,
+ 17, 0, 24, 0, 70, 0,
+ 124, 1, 0, 0, 0, 0,
+ 0, 0, 140, 1, 0, 0,
+ 2, 0, 12, 0, 1, 0,
+ 50, 0, 224, 0, 0, 0,
+ 0, 0, 0, 0, 147, 1,
+ 0, 0, 2, 0, 8, 0,
+ 3, 0, 34, 0, 160, 1,
+ 0, 0, 0, 0, 0, 0,
+ 176, 1, 0, 0, 1, 0,
+ 0, 0, 1, 0, 2, 0,
+ 192, 1, 0, 0, 0, 0,
+ 0, 0, 208, 1, 0, 0,
+ 2, 0, 13, 0, 1, 0,
+ 54, 0, 224, 0, 0, 0,
+ 0, 0, 0, 0, 97, 109,
+ 98, 105, 101, 110, 116, 76,
+ 105, 103, 104, 116, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 109, 98, 105, 110,
+ 101, 100, 77, 97, 116, 0,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 102, 105,
+ 114, 115, 116, 76, 105, 103,
+ 104, 116, 0, 171, 1, 0,
+ 2, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 102, 111, 103, 68,
+ 97, 116, 97, 0, 108, 105,
+ 103, 104, 116, 115, 0, 99,
+ 111, 108, 111, 114, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 111, 115, 105, 116, 105,
+ 111, 110, 0, 100, 105, 114,
+ 101, 99, 116, 105, 111, 110,
+ 0, 171, 55, 1, 0, 0,
+ 64, 1, 0, 0, 80, 1,
+ 0, 0, 64, 1, 0, 0,
+ 89, 1, 0, 0, 64, 1,
+ 0, 0, 5, 0, 0, 0,
+ 1, 0, 12, 0, 8, 0,
+ 3, 0, 100, 1, 0, 0,
+ 109, 97, 116, 67, 111, 108,
+ 0, 110, 111, 114, 109, 97,
+ 108, 77, 97, 116, 0, 171,
+ 171, 171, 3, 0, 3, 0,
+ 3, 0, 3, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 110, 117, 109, 68, 105, 114,
+ 76, 105, 103, 104, 116, 115,
+ 0, 171, 171, 171, 0, 0,
+ 2, 0, 1, 0, 1, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 117, 114, 102,
+ 80, 114, 111, 112, 115, 0,
+ 118, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 57, 46, 50, 57, 46, 57,
+ 53, 50, 46, 51, 49, 49,
+ 49, 0, 171, 171, 81, 0,
+ 0, 5, 4, 0, 15, 160,
+ 0, 0, 0, 0, 0, 0,
+ 64, 64, 0, 0, 128, 63,
+ 0, 0, 0, 0, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 144, 31, 0,
+ 0, 2, 3, 0, 0, 128,
+ 1, 0, 15, 144, 31, 0,
+ 0, 2, 5, 0, 0, 128,
+ 2, 0, 15, 144, 31, 0,
+ 0, 2, 10, 0, 0, 128,
+ 3, 0, 15, 144, 5, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 85, 144, 1, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 15, 128, 0, 0,
+ 228, 160, 0, 0, 0, 144,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 2, 0, 228, 160, 0, 0,
+ 170, 144, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 3, 0, 228, 160,
+ 0, 0, 255, 144, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 0, 15, 192, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 7, 128, 1, 0,
+ 85, 144, 9, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 8, 0, 228, 160,
+ 1, 0, 0, 144, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 7, 128, 10, 0,
+ 228, 160, 1, 0, 170, 144,
+ 0, 0, 228, 128, 1, 0,
+ 0, 2, 1, 0, 1, 128,
+ 13, 0, 0, 160, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 15, 0, 228, 160, 1, 0,
+ 0, 128, 3, 0, 228, 144,
+ 1, 0, 0, 2, 2, 0,
+ 7, 128, 1, 0, 228, 128,
+ 1, 0, 0, 2, 1, 0,
+ 8, 128, 4, 0, 0, 160,
+ 38, 0, 0, 1, 0, 0,
+ 228, 240, 2, 0, 0, 3,
+ 3, 0, 1, 128, 1, 0,
+ 255, 128, 16, 0, 0, 160,
+ 5, 0, 0, 3, 3, 0,
+ 1, 128, 3, 0, 0, 128,
+ 4, 0, 85, 160, 46, 0,
+ 0, 2, 0, 0, 1, 176,
+ 3, 0, 0, 128, 8, 0,
+ 0, 4, 3, 0, 1, 128,
+ 0, 0, 228, 128, 19, 32,
+ 228, 161, 0, 0, 0, 176,
+ 11, 0, 0, 3, 3, 0,
+ 1, 128, 3, 0, 0, 128,
+ 4, 0, 0, 160, 5, 0,
+ 0, 4, 3, 0, 7, 128,
+ 3, 0, 0, 128, 17, 32,
+ 228, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 3, 0, 228, 128,
+ 13, 0, 170, 160, 2, 0,
+ 228, 128, 2, 0, 0, 3,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 4, 0, 170, 160,
+ 39, 0, 0, 0, 1, 0,
+ 0, 2, 2, 0, 8, 128,
+ 3, 0, 255, 144, 11, 0,
+ 0, 3, 1, 0, 15, 128,
+ 2, 0, 228, 128, 4, 0,
+ 0, 160, 10, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 4, 0, 170, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 208, 1, 0, 228, 128,
+ 12, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 14, 0,
+ 85, 161, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 14, 0, 170, 160,
+ 11, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 255, 160, 10, 0,
+ 0, 3, 0, 0, 4, 224,
+ 0, 0, 0, 128, 4, 0,
+ 170, 160, 1, 0, 0, 2,
+ 0, 0, 3, 224, 2, 0,
+ 228, 144, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/default_tex_PS.h b/src/d3d/shaders/default_tex_PS.h
new file mode 100644
index 0000000..a25f918
--- /dev/null
+++ b/src/d3d/shaders/default_tex_PS.h
@@ -0,0 +1,89 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T ps_2_0 /DTEX /Fh default_tex_PS.h default_PS.hlsl
+//
+//
+// Parameters:
+//
+// float4 fogColor;
+// sampler2D tex0;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fogColor c0 1
+// tex0 s0 1
+//
+
+ ps_2_0
+ dcl t0.xyz
+ dcl v0
+ dcl_2d s0
+ texld r0, t0, s0
+ mad r0.xyz, v0, r0, -c0
+ mul r1.w, r0.w, v0.w
+ mad r1.xyz, t0.z, r0, c0
+ mov oC0, r1
+
+// approximately 5 instruction slots used (1 texture, 4 arithmetic)
+#endif
+
+const BYTE g_ps20_main[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 45, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 127, 0,
+ 0, 0, 0, 2, 255, 255,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 120, 0, 0, 0, 68, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 2, 0,
+ 104, 0, 0, 0, 0, 0,
+ 0, 0, 102, 111, 103, 67,
+ 111, 108, 111, 114, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 48, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 57, 46, 50, 57, 46, 57,
+ 53, 50, 46, 51, 49, 49,
+ 49, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 66, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 228, 144,
+ 0, 0, 228, 128, 0, 0,
+ 228, 161, 5, 0, 0, 3,
+ 1, 0, 8, 128, 0, 0,
+ 255, 128, 0, 0, 255, 144,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 0, 0, 170, 176,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 1, 0,
+ 228, 128, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/im2d_PS.h b/src/d3d/shaders/im2d_PS.h
new file mode 100644
index 0000000..71f8b28
--- /dev/null
+++ b/src/d3d/shaders/im2d_PS.h
@@ -0,0 +1,72 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T ps_2_0 /Fh im2d_PS.h im2d_PS.hlsl
+//
+//
+// Parameters:
+//
+// float4 fogColor;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fogColor c0 1
+//
+
+ ps_2_0
+ dcl t0.xyz
+ dcl v0
+ add r0.xyz, v0, -c0
+ mad r0.xyz, t0.z, r0, c0
+ mov r0.w, v0.w
+ mov oC0, r0
+
+// approximately 4 instruction slots used
+#endif
+
+const BYTE g_ps20_main[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 34, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 83, 0,
+ 0, 0, 0, 2, 255, 255,
+ 1, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 76, 0, 0, 0, 48, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 60, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 111, 103, 67, 111, 108,
+ 111, 114, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 115,
+ 95, 50, 95, 48, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 57, 46,
+ 50, 57, 46, 57, 53, 50,
+ 46, 51, 49, 49, 49, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 144,
+ 2, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 144,
+ 0, 0, 228, 161, 4, 0,
+ 0, 4, 0, 0, 7, 128,
+ 0, 0, 170, 176, 0, 0,
+ 228, 128, 0, 0, 228, 160,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 0, 0, 255, 144,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/im2d_PS.hlsl b/src/d3d/shaders/im2d_PS.hlsl
new file mode 100644
index 0000000..eaf279d
--- /dev/null
+++ b/src/d3d/shaders/im2d_PS.hlsl
@@ -0,0 +1,19 @@
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0;
+ float4 Color : COLOR0;
+};
+
+sampler2D tex0 : register(s0);
+
+float4 fogColor : register(c0);
+
+float4 main(VS_out input) : COLOR
+{
+ float4 color = input.Color;
+#ifdef TEX
+ color *= tex2D(tex0, input.TexCoord0.xy);
+#endif
+ color.rgb = lerp(fogColor.rgb, color.rgb, input.TexCoord0.z);
+ return color;
+}
diff --git a/src/d3d/shaders/im2d_VS.h b/src/d3d/shaders/im2d_VS.h
new file mode 100644
index 0000000..bc36b1f
--- /dev/null
+++ b/src/d3d/shaders/im2d_VS.h
@@ -0,0 +1,107 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /Fh im2d_VS.h im2d_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 fogData;
+// float4 xform;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fogData c14 1
+// xform c41 1
+//
+
+ vs_2_0
+ def c0, 1, 0, 0, 0
+ dcl_position v0
+ dcl_texcoord v1
+ dcl_color v2
+ add r0.x, v0.w, -c14.y
+ mul r0.x, r0.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c0.x
+ mad r0.xy, v0, c41, c41.zwzw
+ mov r0.z, v0.z
+ mul oPos.xyz, r0, v0.w
+ mov oPos.w, v0.w
+ mov oT0.xy, v1
+ mov oD0, v2
+
+// approximately 10 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 40, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 105, 0,
+ 0, 0, 0, 2, 254, 255,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 98, 0, 0, 0, 68, 0,
+ 0, 0, 2, 0, 14, 0,
+ 1, 0, 58, 0, 76, 0,
+ 0, 0, 0, 0, 0, 0,
+ 92, 0, 0, 0, 2, 0,
+ 41, 0, 1, 0, 166, 0,
+ 76, 0, 0, 0, 0, 0,
+ 0, 0, 102, 111, 103, 68,
+ 97, 116, 97, 0, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 120, 102, 111, 114,
+ 109, 0, 118, 115, 95, 50,
+ 95, 48, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 57, 46, 50, 57,
+ 46, 57, 53, 50, 46, 51,
+ 49, 49, 49, 0, 171, 171,
+ 81, 0, 0, 5, 0, 0,
+ 15, 160, 0, 0, 128, 63,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 144,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 1, 0, 15, 144,
+ 31, 0, 0, 2, 10, 0,
+ 0, 128, 2, 0, 15, 144,
+ 2, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 255, 144,
+ 14, 0, 85, 161, 5, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 0, 128, 14, 0,
+ 170, 160, 11, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 14, 0, 255, 160,
+ 10, 0, 0, 3, 0, 0,
+ 4, 224, 0, 0, 0, 128,
+ 0, 0, 0, 160, 4, 0,
+ 0, 4, 0, 0, 3, 128,
+ 0, 0, 228, 144, 41, 0,
+ 228, 160, 41, 0, 238, 160,
+ 1, 0, 0, 2, 0, 0,
+ 4, 128, 0, 0, 170, 144,
+ 5, 0, 0, 3, 0, 0,
+ 7, 192, 0, 0, 228, 128,
+ 0, 0, 255, 144, 1, 0,
+ 0, 2, 0, 0, 8, 192,
+ 0, 0, 255, 144, 1, 0,
+ 0, 2, 0, 0, 3, 224,
+ 1, 0, 228, 144, 1, 0,
+ 0, 2, 0, 0, 15, 208,
+ 2, 0, 228, 144, 255, 255,
+ 0, 0
+};
diff --git a/src/d3d/shaders/im2d_VS.hlsl b/src/d3d/shaders/im2d_VS.hlsl
new file mode 100644
index 0000000..20cf3ce
--- /dev/null
+++ b/src/d3d/shaders/im2d_VS.hlsl
@@ -0,0 +1,30 @@
+#include "standardConstants.h"
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float2 TexCoord : TEXCOORD0;
+ float4 Color : COLOR0;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0;
+ float4 Color : COLOR0;
+};
+
+float4 xform : register(c41);
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ output.Position = input.Position;
+ output.Position.xy = output.Position.xy * xform.xy + xform.zw;
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+ output.Position.xyz *= output.Position.w;
+ output.Color = input.Color;
+ output.TexCoord0.xy = input.TexCoord;
+
+ return output;
+}
diff --git a/src/d3d/shaders/im2d_tex_PS.h b/src/d3d/shaders/im2d_tex_PS.h
new file mode 100644
index 0000000..a91888b
--- /dev/null
+++ b/src/d3d/shaders/im2d_tex_PS.h
@@ -0,0 +1,89 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T ps_2_0 /DTEX /Fh im2d_tex_PS.h im2d_PS.hlsl
+//
+//
+// Parameters:
+//
+// float4 fogColor;
+// sampler2D tex0;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fogColor c0 1
+// tex0 s0 1
+//
+
+ ps_2_0
+ dcl t0.xyz
+ dcl v0
+ dcl_2d s0
+ texld r0, t0, s0
+ mad r0.xyz, v0, r0, -c0
+ mul r1.w, r0.w, v0.w
+ mad r1.xyz, t0.z, r0, c0
+ mov oC0, r1
+
+// approximately 5 instruction slots used (1 texture, 4 arithmetic)
+#endif
+
+const BYTE g_ps20_main[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 45, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 127, 0,
+ 0, 0, 0, 2, 255, 255,
+ 2, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 120, 0, 0, 0, 68, 0,
+ 0, 0, 2, 0, 0, 0,
+ 1, 0, 2, 0, 80, 0,
+ 0, 0, 0, 0, 0, 0,
+ 96, 0, 0, 0, 3, 0,
+ 0, 0, 1, 0, 2, 0,
+ 104, 0, 0, 0, 0, 0,
+ 0, 0, 102, 111, 103, 67,
+ 111, 108, 111, 114, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 116, 101, 120, 48, 0, 171,
+ 171, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 57, 46, 50, 57, 46, 57,
+ 53, 50, 46, 51, 49, 49,
+ 49, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 7, 176, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 31, 0, 0, 2,
+ 0, 0, 0, 144, 0, 8,
+ 15, 160, 66, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 176, 0, 8, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 228, 144,
+ 0, 0, 228, 128, 0, 0,
+ 228, 161, 5, 0, 0, 3,
+ 1, 0, 8, 128, 0, 0,
+ 255, 128, 0, 0, 255, 144,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 0, 0, 170, 176,
+ 0, 0, 228, 128, 0, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 8, 15, 128, 1, 0,
+ 228, 128, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/lighting.h b/src/d3d/shaders/lighting.h
new file mode 100644
index 0000000..4b08196
--- /dev/null
+++ b/src/d3d/shaders/lighting.h
@@ -0,0 +1,44 @@
+struct Light
+{
+ float4 color; // and radius
+ float4 position; // and -cos(angle)
+ float4 direction; // and falloff clamp
+};
+
+float3 DoDirLight(Light L, float3 N)
+{
+ float l = max(0.0, dot(N, -L.direction.xyz));
+ return l*L.color.xyz;
+}
+
+float3 DoDirLightSpec(Light L, float3 N, float3 V, float power)
+{
+ return pow(saturate(dot(N, normalize(V + -L.direction.xyz))), power)*L.color.xyz;
+}
+
+float3 DoPointLight(Light L, float3 V, float3 N)
+{
+ // As on PS2
+ float3 dir = V - L.position.xyz;
+ float dist = length(dir);
+ float atten = max(0.0, (1.0 - dist/L.color.w));
+ float l = max(0.0, dot(N, -normalize(dir)));
+ return l*L.color.xyz*atten;
+}
+
+float3 DoSpotLight(Light L, float3 V, float3 N)
+{
+ // As on PS2
+ float3 dir = V - L.position.xyz;
+ float dist = length(dir);
+ float atten = max(0.0, (1.0 - dist/L.color.w));
+ dir /= dist;
+ float l = max(0.0, dot(N, -dir));
+ float pcos = dot(dir, L.direction.xyz); // cos to point
+ float ccos = -L.position.w; // cos of cone
+ float falloff = (pcos-ccos)/(1.0-ccos);
+ if(falloff < 0) // outside of cone
+ l = 0;
+ l *= max(falloff, L.direction.w); // falloff clamp
+ return l*L.color.xyz*atten;
+}
diff --git a/src/d3d/shaders/make_default.cmd b/src/d3d/shaders/make_default.cmd
new file mode 100644
index 0000000..bc44429
--- /dev/null
+++ b/src/d3d/shaders/make_default.cmd
@@ -0,0 +1,11 @@
+@echo off
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /Fh default_amb_VS.h default_VS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /DDIRECTIONALS /Fh default_amb_dir_VS.h default_VS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /DDIRECTIONALS /DPOINTLIGHTS /DSPOTLIGHTS /Fh default_all_VS.h default_VS.hlsl
+
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T ps_2_0 /Fh default_PS.h default_PS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T ps_2_0 /DTEX /Fh default_tex_PS.h default_PS.hlsl
+
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /Fh im2d_VS.h im2d_VS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T ps_2_0 /Fh im2d_PS.h im2d_PS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T ps_2_0 /DTEX /Fh im2d_tex_PS.h im2d_PS.hlsl
diff --git a/src/d3d/shaders/make_matfx.cmd b/src/d3d/shaders/make_matfx.cmd
new file mode 100644
index 0000000..d0bc4fa
--- /dev/null
+++ b/src/d3d/shaders/make_matfx.cmd
@@ -0,0 +1,7 @@
+@echo off
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /Fh matfx_env_amb_VS.h matfx_env_VS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /DDIRECTIONALS /Fh matfx_env_amb_dir_VS.h matfx_env_VS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /DDIRECTIONALS /DPOINTLIGHTS /DSPOTLIGHTS /Fh matfx_env_all_VS.h matfx_env_VS.hlsl
+
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T ps_2_0 /Fh matfx_env_PS.h matfx_env_PS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T ps_2_0 /DTEX /Fh matfx_env_tex_PS.h matfx_env_PS.hlsl
diff --git a/src/d3d/shaders/make_skin.cmd b/src/d3d/shaders/make_skin.cmd
new file mode 100644
index 0000000..b7822ca
--- /dev/null
+++ b/src/d3d/shaders/make_skin.cmd
@@ -0,0 +1,4 @@
+@echo off
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /Fh skin_amb_VS.h skin_VS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /DDIRECTIONALS /Fh skin_amb_dir_VS.h skin_VS.hlsl
+"%DXSDK_DIR%\utilities\bin\x86\fxc.exe" /nologo /T vs_2_0 /DDIRECTIONALS /DPOINTLIGHTS /DSPOTLIGHTS /Fh skin_all_VS.h skin_VS.hlsl
diff --git a/src/d3d/shaders/matfx_env_PS.h b/src/d3d/shaders/matfx_env_PS.h
new file mode 100644
index 0000000..e39a1f5
--- /dev/null
+++ b/src/d3d/shaders/matfx_env_PS.h
@@ -0,0 +1,124 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T ps_2_0 /Fh matfx_env_PS.h matfx_env_PS.hlsl
+//
+//
+// Parameters:
+//
+// sampler2D envTex;
+// float4 fogColor;
+// float4 fxparams;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fogColor c0 1
+// fxparams c1 1
+// envTex s1 1
+//
+
+ ps_2_0
+ dcl t0.xyz
+ dcl t1.xy
+ dcl v0
+ dcl v1.xyz
+ dcl_2d s1
+ texld r0, t1, s1
+ mul r1.xyz, v1, c1.x
+ mul r0.xyz, r0, r1
+ mul r0.xyz, r0, t0.z
+ max r0.w, v0.w, c1.y
+ mul r0.xyz, r0.w, r0
+ add r1.xyz, v0, -c0
+ mad r1.xyz, t0.z, r1, c0
+ mad r0.xyz, r1, v0.w, r0
+ mov r0.w, v0.w
+ mov oC0, r0
+
+// approximately 11 instruction slots used (1 texture, 10 arithmetic)
+#endif
+
+const BYTE g_ps20_main[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 53, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 156, 0,
+ 0, 0, 0, 2, 255, 255,
+ 3, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 149, 0, 0, 0, 88, 0,
+ 0, 0, 3, 0, 1, 0,
+ 1, 0, 6, 0, 96, 0,
+ 0, 0, 0, 0, 0, 0,
+ 112, 0, 0, 0, 2, 0,
+ 0, 0, 1, 0, 2, 0,
+ 124, 0, 0, 0, 0, 0,
+ 0, 0, 140, 0, 0, 0,
+ 2, 0, 1, 0, 1, 0,
+ 6, 0, 124, 0, 0, 0,
+ 0, 0, 0, 0, 101, 110,
+ 118, 84, 101, 120, 0, 171,
+ 4, 0, 12, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 102, 111,
+ 103, 67, 111, 108, 111, 114,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 102, 120, 112, 97,
+ 114, 97, 109, 115, 0, 112,
+ 115, 95, 50, 95, 48, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 57,
+ 46, 50, 57, 46, 57, 53,
+ 50, 46, 51, 49, 49, 49,
+ 0, 171, 171, 171, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 7, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 3, 176, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 0, 0, 15, 144, 31, 0,
+ 0, 2, 0, 0, 0, 128,
+ 1, 0, 7, 144, 31, 0,
+ 0, 2, 0, 0, 0, 144,
+ 1, 8, 15, 160, 66, 0,
+ 0, 3, 0, 0, 15, 128,
+ 1, 0, 228, 176, 1, 8,
+ 228, 160, 5, 0, 0, 3,
+ 1, 0, 7, 128, 1, 0,
+ 228, 144, 1, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 1, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 228, 128, 0, 0,
+ 170, 176, 11, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 144, 1, 0, 85, 160,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 255, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 7, 128,
+ 0, 0, 228, 144, 0, 0,
+ 228, 161, 4, 0, 0, 4,
+ 1, 0, 7, 128, 0, 0,
+ 170, 176, 1, 0, 228, 128,
+ 0, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 7, 128,
+ 1, 0, 228, 128, 0, 0,
+ 255, 144, 0, 0, 228, 128,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 0, 0, 255, 144,
+ 1, 0, 0, 2, 0, 8,
+ 15, 128, 0, 0, 228, 128,
+ 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/matfx_env_PS.hlsl b/src/d3d/shaders/matfx_env_PS.hlsl
new file mode 100644
index 0000000..eb5187d
--- /dev/null
+++ b/src/d3d/shaders/matfx_env_PS.hlsl
@@ -0,0 +1,42 @@
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0;
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Color : COLOR0;
+ float4 EnvColor : COLOR1;
+};
+
+sampler2D diffTex : register(s0);
+sampler2D envTex : register(s1);
+
+float4 fogColor : register(c0);
+
+float4 fxparams : register(c1);
+
+#define shininess (fxparams.x)
+#define disableFBA (fxparams.y)
+
+float4 main(VS_out input) : COLOR
+{
+ float4 pass1 = input.Color;
+#ifdef TEX
+ pass1 *= tex2D(diffTex, input.TexCoord0.xy);
+#endif
+
+ float4 pass2 = input.EnvColor*shininess*tex2D(envTex, input.TexCoord1.xy);
+
+ pass1.rgb = lerp(fogColor.rgb, pass1.rgb, input.TexCoord0.z);
+ pass2.rgb = lerp(float3(0.0, 0.0, 0.0), pass2.rgb, input.TexCoord0.z);
+
+ // We simulate drawing this in two passes.
+ // First pass with standard blending, second with addition
+ // We premultiply alpha so render state should be one.
+ // For FB alpha rendering assume that diffuse alpha (pass1.a) was
+ // written to framebuffer, so just multiply pass2 by it as well then.
+ float fba = max(pass1.a, disableFBA);
+ float4 color;
+ color.rgb = pass1.rgb*pass1.a + pass2.rgb*fba;
+ color.a = pass1.a;
+
+ return color;
+}
diff --git a/src/d3d/shaders/matfx_env_VS.hlsl b/src/d3d/shaders/matfx_env_VS.hlsl
new file mode 100644
index 0000000..31b674c
--- /dev/null
+++ b/src/d3d/shaders/matfx_env_VS.hlsl
@@ -0,0 +1,59 @@
+#include "standardConstants.h"
+
+float4x4 texMat : register(c41);
+float4 colorClamp : register(c45);
+float4 envColor : register(c46);
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 TexCoord : TEXCOORD0;
+ float4 Prelight : COLOR0;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0; // also fog
+ float2 TexCoord1 : TEXCOORD1;
+ float4 Color : COLOR0;
+ float4 EnvColor : COLOR1;
+};
+
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ output.Position = mul(combinedMat, input.Position);
+ float3 V = mul(worldMat, input.Position).xyz;
+ float3 N = mul(normalMat, input.Normal);
+
+ output.TexCoord0.xy = input.TexCoord;
+ output.TexCoord1 = mul(texMat, float4(N, 1.0)).xy;
+
+ output.Color = input.Prelight;
+ output.Color.rgb += ambientLight.rgb * surfAmbient;
+
+ int i;
+#ifdef DIRECTIONALS
+ for(i = 0; i < numDirLights; i++)
+ output.Color.xyz += DoDirLight(lights[i+firstDirLight], N)*surfDiffuse;
+#endif
+#ifdef POINTLIGHTS
+ for(i = 0; i < numPointLights; i++)
+ output.Color.xyz += DoPointLight(lights[i+firstPointLight], V, N)*surfDiffuse;
+#endif
+#ifdef SPOTLIGHTS
+ for(i = 0; i < numSpotLights; i++)
+ output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], V, N)*surfDiffuse;
+#endif
+ // PS2 clamps before material color
+ output.Color = clamp(output.Color, 0.0, 1.0);
+ output.EnvColor = max(output.Color, colorClamp) * envColor;
+ output.Color *= matCol;
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/d3d/shaders/matfx_env_all_VS.h b/src/d3d/shaders/matfx_env_all_VS.h
new file mode 100644
index 0000000..65e1c15
--- /dev/null
+++ b/src/d3d/shaders/matfx_env_all_VS.h
@@ -0,0 +1,535 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /DDIRECTIONALS /DPOINTLIGHTS /DSPOTLIGHTS /Fh
+// matfx_env_all_VS.h matfx_env_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4 colorClamp;
+// float4x4 combinedMat;
+// float4 envColor;
+// int4 firstLight;
+// float4 fogData;
+//
+// struct
+// {
+// float4 color;
+// float4 position;
+// float4 direction;
+//
+// } lights[8];
+//
+// float4 matCol;
+// float3x3 normalMat;
+// int numDirLights;
+// int numPointLights;
+// int numSpotLights;
+// float4 surfProps;
+// float4x4 texMat;
+// float4x4 worldMat;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// -------------- ----- ----
+// numDirLights i0 1
+// numPointLights i1 1
+// numSpotLights i2 1
+// combinedMat c0 4
+// worldMat c4 4
+// normalMat c8 3
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// firstLight c16 1
+// lights c17 24
+// texMat c41 4
+// colorClamp c45 1
+// envColor c46 1
+//
+
+ vs_2_0
+ def c11, 0, 3, 1, 0
+ dcl_position v0
+ dcl_normal v1
+ dcl_texcoord v2
+ dcl_color v3
+ mul r0, v0.y, c1
+ mad r0, c0, v0.x, r0
+ mad r0, c2, v0.z, r0
+ mad r0, c3, v0.w, r0
+ mov oPos, r0
+ mul r0.xyz, v0.y, c5
+ mad r0.xyz, c4, v0.x, r0
+ mad r0.xyz, c6, v0.z, r0
+ mad r0.xyz, c7, v0.w, r0
+ mul r1.xyz, v1.y, c9
+ mad r1.xyz, c8, v1.x, r1
+ mad r1.xyz, c10, v1.z, r1
+ mov r2.x, c13.x
+ mad r2.xyz, c15, r2.x, v3
+ mov r3.xyz, r2
+ mov r1.w, c11.x
+ rep i0
+ add r2.w, r1.w, c16.x
+ mul r2.w, r2.w, c11.y
+ mova a0.x, r2.w
+ dp3 r2.w, r1, -c19[a0.x]
+ max r2.w, r2.w, c11.x
+ mul r4.xyz, r2.w, c17[a0.x]
+ mad r3.xyz, r4, c13.z, r3
+ add r1.w, r1.w, c11.z
+ endrep
+ mov r2.xyz, r3
+ mov r1.w, c11.x
+ rep i1
+ add r2.w, r1.w, c16.y
+ mul r2.w, r2.w, c11.y
+ mova a0.x, r2.w
+ add r4.xyz, r0, -c18[a0.x]
+ dp3 r2.w, r4, r4
+ rsq r2.w, r2.w
+ mul r4.xyz, r2.w, r4
+ dp3 r3.w, r1, -r4
+ max r3.w, r3.w, c11.x
+ mul r4.xyz, r3.w, c17[a0.x]
+ rcp r2.w, r2.w
+ rcp r3.w, c17[a0.x].w
+ mad r2.w, r2.w, -r3.w, c11.z
+ max r2.w, r2.w, c11.x
+ mul r4.xyz, r2.w, r4
+ mad r2.xyz, r4, c13.z, r2
+ add r1.w, r1.w, c11.z
+ endrep
+ mov r3.xyz, r2
+ mov r1.w, c11.x
+ rep i2
+ add r2.w, r1.w, c16.z
+ mul r2.w, r2.w, c11.y
+ mova a0.x, r2.w
+ add r4.xyz, r0, -c18[a0.x]
+ dp3 r2.w, r4, r4
+ rsq r2.w, r2.w
+ mul r4.xyz, r2.w, r4
+ dp3 r4.w, r1, -r4
+ dp3 r4.x, r4, c19[a0.x]
+ max r4.y, r4.w, c11.x
+ mov r4.z, c11.z
+ add r4.xz, r4, c18[a0.x].w
+ rcp r4.z, r4.z
+ mul r4.x, r4.z, r4.x
+ slt r4.z, r4.x, c11.x
+ mad r4.y, r4.z, -r4.y, r4.y
+ max r4.x, r4.x, c19[a0.x].w
+ mul r4.x, r4.x, r4.y
+ mul r4.xyz, r4.x, c17[a0.x]
+ rcp r2.w, r2.w
+ rcp r4.w, c17[a0.x].w
+ mad r2.w, r2.w, -r4.w, c11.z
+ max r2.w, r2.w, c11.x
+ mul r4.xyz, r2.w, r4
+ mad r3.xyz, r4, c13.z, r3
+ add r1.w, r1.w, c11.z
+ endrep
+ mov r3.w, v3.w
+ max r2, r3, c11.x
+ min r2, r2, c11.z
+ mul oD0, r2, c12
+ mul r0.xy, r1.y, c42
+ mad r0.xy, c41, r1.x, r0
+ mad r0.xy, c43, r1.z, r0
+ add oT1.xy, r0, c44
+ max r1, r2, c45
+ mul oD1, r1, c46
+ add r0.x, r0.w, -c14.y
+ mul r0.x, r0.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c11.z
+ mov oT0.xy, v2
+
+// approximately 101 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 180, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 154, 2,
+ 0, 0, 0, 2, 254, 255,
+ 15, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 147, 2, 0, 0, 72, 1,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 88, 1,
+ 0, 0, 0, 0, 0, 0,
+ 104, 1, 0, 0, 2, 0,
+ 45, 0, 1, 0, 182, 0,
+ 88, 1, 0, 0, 0, 0,
+ 0, 0, 115, 1, 0, 0,
+ 2, 0, 0, 0, 4, 0,
+ 2, 0, 128, 1, 0, 0,
+ 0, 0, 0, 0, 144, 1,
+ 0, 0, 2, 0, 46, 0,
+ 1, 0, 186, 0, 88, 1,
+ 0, 0, 0, 0, 0, 0,
+ 153, 1, 0, 0, 2, 0,
+ 16, 0, 1, 0, 66, 0,
+ 164, 1, 0, 0, 0, 0,
+ 0, 0, 180, 1, 0, 0,
+ 2, 0, 14, 0, 1, 0,
+ 58, 0, 88, 1, 0, 0,
+ 0, 0, 0, 0, 188, 1,
+ 0, 0, 2, 0, 17, 0,
+ 24, 0, 70, 0, 8, 2,
+ 0, 0, 0, 0, 0, 0,
+ 24, 2, 0, 0, 2, 0,
+ 12, 0, 1, 0, 50, 0,
+ 88, 1, 0, 0, 0, 0,
+ 0, 0, 31, 2, 0, 0,
+ 2, 0, 8, 0, 3, 0,
+ 34, 0, 44, 2, 0, 0,
+ 0, 0, 0, 0, 60, 2,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 2, 0, 76, 2,
+ 0, 0, 0, 0, 0, 0,
+ 92, 2, 0, 0, 1, 0,
+ 1, 0, 1, 0, 6, 0,
+ 76, 2, 0, 0, 0, 0,
+ 0, 0, 107, 2, 0, 0,
+ 1, 0, 2, 0, 1, 0,
+ 10, 0, 76, 2, 0, 0,
+ 0, 0, 0, 0, 121, 2,
+ 0, 0, 2, 0, 13, 0,
+ 1, 0, 54, 0, 88, 1,
+ 0, 0, 0, 0, 0, 0,
+ 131, 2, 0, 0, 2, 0,
+ 41, 0, 4, 0, 166, 0,
+ 128, 1, 0, 0, 0, 0,
+ 0, 0, 138, 2, 0, 0,
+ 2, 0, 4, 0, 4, 0,
+ 18, 0, 128, 1, 0, 0,
+ 0, 0, 0, 0, 97, 109,
+ 98, 105, 101, 110, 116, 76,
+ 105, 103, 104, 116, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 108, 111, 114, 67,
+ 108, 97, 109, 112, 0, 99,
+ 111, 109, 98, 105, 110, 101,
+ 100, 77, 97, 116, 0, 171,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 101, 110,
+ 118, 67, 111, 108, 111, 114,
+ 0, 102, 105, 114, 115, 116,
+ 76, 105, 103, 104, 116, 0,
+ 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 102, 111,
+ 103, 68, 97, 116, 97, 0,
+ 108, 105, 103, 104, 116, 115,
+ 0, 99, 111, 108, 111, 114,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 111, 115, 105,
+ 116, 105, 111, 110, 0, 100,
+ 105, 114, 101, 99, 116, 105,
+ 111, 110, 0, 171, 195, 1,
+ 0, 0, 204, 1, 0, 0,
+ 220, 1, 0, 0, 204, 1,
+ 0, 0, 229, 1, 0, 0,
+ 204, 1, 0, 0, 5, 0,
+ 0, 0, 1, 0, 12, 0,
+ 8, 0, 3, 0, 240, 1,
+ 0, 0, 109, 97, 116, 67,
+ 111, 108, 0, 110, 111, 114,
+ 109, 97, 108, 77, 97, 116,
+ 0, 171, 171, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 110, 117, 109, 68,
+ 105, 114, 76, 105, 103, 104,
+ 116, 115, 0, 171, 171, 171,
+ 0, 0, 2, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 110, 117,
+ 109, 80, 111, 105, 110, 116,
+ 76, 105, 103, 104, 116, 115,
+ 0, 110, 117, 109, 83, 112,
+ 111, 116, 76, 105, 103, 104,
+ 116, 115, 0, 115, 117, 114,
+ 102, 80, 114, 111, 112, 115,
+ 0, 116, 101, 120, 77, 97,
+ 116, 0, 119, 111, 114, 108,
+ 100, 77, 97, 116, 0, 118,
+ 115, 95, 50, 95, 48, 0,
+ 77, 105, 99, 114, 111, 115,
+ 111, 102, 116, 32, 40, 82,
+ 41, 32, 72, 76, 83, 76,
+ 32, 83, 104, 97, 100, 101,
+ 114, 32, 67, 111, 109, 112,
+ 105, 108, 101, 114, 32, 57,
+ 46, 50, 57, 46, 57, 53,
+ 50, 46, 51, 49, 49, 49,
+ 0, 171, 81, 0, 0, 5,
+ 11, 0, 15, 160, 0, 0,
+ 0, 0, 0, 0, 64, 64,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 31, 0, 0, 2,
+ 3, 0, 0, 128, 1, 0,
+ 15, 144, 31, 0, 0, 2,
+ 5, 0, 0, 128, 2, 0,
+ 15, 144, 31, 0, 0, 2,
+ 10, 0, 0, 128, 3, 0,
+ 15, 144, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 85, 144, 1, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 0, 0, 228, 160,
+ 0, 0, 0, 144, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 2, 0,
+ 228, 160, 0, 0, 170, 144,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 3, 0, 228, 160, 0, 0,
+ 255, 144, 0, 0, 228, 128,
+ 1, 0, 0, 2, 0, 0,
+ 15, 192, 0, 0, 228, 128,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 85, 144,
+ 5, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 7, 128,
+ 4, 0, 228, 160, 0, 0,
+ 0, 144, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 6, 0, 228, 160,
+ 0, 0, 170, 144, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 7, 128, 7, 0,
+ 228, 160, 0, 0, 255, 144,
+ 0, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 7, 128,
+ 1, 0, 85, 144, 9, 0,
+ 228, 160, 4, 0, 0, 4,
+ 1, 0, 7, 128, 8, 0,
+ 228, 160, 1, 0, 0, 144,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 10, 0, 228, 160, 1, 0,
+ 170, 144, 1, 0, 228, 128,
+ 1, 0, 0, 2, 2, 0,
+ 1, 128, 13, 0, 0, 160,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 15, 0, 228, 160,
+ 2, 0, 0, 128, 3, 0,
+ 228, 144, 1, 0, 0, 2,
+ 3, 0, 7, 128, 2, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 0, 8, 128, 11, 0,
+ 0, 160, 38, 0, 0, 1,
+ 0, 0, 228, 240, 2, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 128, 16, 0,
+ 0, 160, 5, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 11, 0, 85, 160,
+ 46, 0, 0, 2, 0, 0,
+ 1, 176, 2, 0, 255, 128,
+ 8, 0, 0, 4, 2, 0,
+ 8, 128, 1, 0, 228, 128,
+ 19, 32, 228, 161, 0, 0,
+ 0, 176, 11, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 11, 0, 0, 160,
+ 5, 0, 0, 4, 4, 0,
+ 7, 128, 2, 0, 255, 128,
+ 17, 32, 228, 160, 0, 0,
+ 0, 176, 4, 0, 0, 4,
+ 3, 0, 7, 128, 4, 0,
+ 228, 128, 13, 0, 170, 160,
+ 3, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 128, 11, 0,
+ 170, 160, 39, 0, 0, 0,
+ 1, 0, 0, 2, 2, 0,
+ 7, 128, 3, 0, 228, 128,
+ 1, 0, 0, 2, 1, 0,
+ 8, 128, 11, 0, 0, 160,
+ 38, 0, 0, 1, 1, 0,
+ 228, 240, 2, 0, 0, 3,
+ 2, 0, 8, 128, 1, 0,
+ 255, 128, 16, 0, 85, 160,
+ 5, 0, 0, 3, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 11, 0, 85, 160, 46, 0,
+ 0, 2, 0, 0, 1, 176,
+ 2, 0, 255, 128, 2, 0,
+ 0, 4, 4, 0, 7, 128,
+ 0, 0, 228, 128, 18, 32,
+ 228, 161, 0, 0, 0, 176,
+ 8, 0, 0, 3, 2, 0,
+ 8, 128, 4, 0, 228, 128,
+ 4, 0, 228, 128, 7, 0,
+ 0, 2, 2, 0, 8, 128,
+ 2, 0, 255, 128, 5, 0,
+ 0, 3, 4, 0, 7, 128,
+ 2, 0, 255, 128, 4, 0,
+ 228, 128, 8, 0, 0, 3,
+ 3, 0, 8, 128, 1, 0,
+ 228, 128, 4, 0, 228, 129,
+ 11, 0, 0, 3, 3, 0,
+ 8, 128, 3, 0, 255, 128,
+ 11, 0, 0, 160, 5, 0,
+ 0, 4, 4, 0, 7, 128,
+ 3, 0, 255, 128, 17, 32,
+ 228, 160, 0, 0, 0, 176,
+ 6, 0, 0, 2, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 6, 0, 0, 3, 3, 0,
+ 8, 128, 17, 32, 255, 160,
+ 0, 0, 0, 176, 4, 0,
+ 0, 4, 2, 0, 8, 128,
+ 2, 0, 255, 128, 3, 0,
+ 255, 129, 11, 0, 170, 160,
+ 11, 0, 0, 3, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 11, 0, 0, 160, 5, 0,
+ 0, 3, 4, 0, 7, 128,
+ 2, 0, 255, 128, 4, 0,
+ 228, 128, 4, 0, 0, 4,
+ 2, 0, 7, 128, 4, 0,
+ 228, 128, 13, 0, 170, 160,
+ 2, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 128, 11, 0,
+ 170, 160, 39, 0, 0, 0,
+ 1, 0, 0, 2, 3, 0,
+ 7, 128, 2, 0, 228, 128,
+ 1, 0, 0, 2, 1, 0,
+ 8, 128, 11, 0, 0, 160,
+ 38, 0, 0, 1, 2, 0,
+ 228, 240, 2, 0, 0, 3,
+ 2, 0, 8, 128, 1, 0,
+ 255, 128, 16, 0, 170, 160,
+ 5, 0, 0, 3, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 11, 0, 85, 160, 46, 0,
+ 0, 2, 0, 0, 1, 176,
+ 2, 0, 255, 128, 2, 0,
+ 0, 4, 4, 0, 7, 128,
+ 0, 0, 228, 128, 18, 32,
+ 228, 161, 0, 0, 0, 176,
+ 8, 0, 0, 3, 2, 0,
+ 8, 128, 4, 0, 228, 128,
+ 4, 0, 228, 128, 7, 0,
+ 0, 2, 2, 0, 8, 128,
+ 2, 0, 255, 128, 5, 0,
+ 0, 3, 4, 0, 7, 128,
+ 2, 0, 255, 128, 4, 0,
+ 228, 128, 8, 0, 0, 3,
+ 4, 0, 8, 128, 1, 0,
+ 228, 128, 4, 0, 228, 129,
+ 8, 0, 0, 4, 4, 0,
+ 1, 128, 4, 0, 228, 128,
+ 19, 32, 228, 160, 0, 0,
+ 0, 176, 11, 0, 0, 3,
+ 4, 0, 2, 128, 4, 0,
+ 255, 128, 11, 0, 0, 160,
+ 1, 0, 0, 2, 4, 0,
+ 4, 128, 11, 0, 170, 160,
+ 2, 0, 0, 4, 4, 0,
+ 5, 128, 4, 0, 228, 128,
+ 18, 32, 255, 160, 0, 0,
+ 0, 176, 6, 0, 0, 2,
+ 4, 0, 4, 128, 4, 0,
+ 170, 128, 5, 0, 0, 3,
+ 4, 0, 1, 128, 4, 0,
+ 170, 128, 4, 0, 0, 128,
+ 12, 0, 0, 3, 4, 0,
+ 4, 128, 4, 0, 0, 128,
+ 11, 0, 0, 160, 4, 0,
+ 0, 4, 4, 0, 2, 128,
+ 4, 0, 170, 128, 4, 0,
+ 85, 129, 4, 0, 85, 128,
+ 11, 0, 0, 4, 4, 0,
+ 1, 128, 4, 0, 0, 128,
+ 19, 32, 255, 160, 0, 0,
+ 0, 176, 5, 0, 0, 3,
+ 4, 0, 1, 128, 4, 0,
+ 0, 128, 4, 0, 85, 128,
+ 5, 0, 0, 4, 4, 0,
+ 7, 128, 4, 0, 0, 128,
+ 17, 32, 228, 160, 0, 0,
+ 0, 176, 6, 0, 0, 2,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 6, 0, 0, 3,
+ 4, 0, 8, 128, 17, 32,
+ 255, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 4, 0, 255, 129, 11, 0,
+ 170, 160, 11, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 11, 0, 0, 160,
+ 5, 0, 0, 3, 4, 0,
+ 7, 128, 2, 0, 255, 128,
+ 4, 0, 228, 128, 4, 0,
+ 0, 4, 3, 0, 7, 128,
+ 4, 0, 228, 128, 13, 0,
+ 170, 160, 3, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 11, 0, 170, 160, 39, 0,
+ 0, 0, 1, 0, 0, 2,
+ 3, 0, 8, 128, 3, 0,
+ 255, 144, 11, 0, 0, 3,
+ 2, 0, 15, 128, 3, 0,
+ 228, 128, 11, 0, 0, 160,
+ 10, 0, 0, 3, 2, 0,
+ 15, 128, 2, 0, 228, 128,
+ 11, 0, 170, 160, 5, 0,
+ 0, 3, 0, 0, 15, 208,
+ 2, 0, 228, 128, 12, 0,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 3, 128, 1, 0,
+ 85, 128, 42, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 41, 0, 228, 160,
+ 1, 0, 0, 128, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 3, 128, 43, 0,
+ 228, 160, 1, 0, 170, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 3, 224,
+ 0, 0, 228, 128, 44, 0,
+ 228, 160, 11, 0, 0, 3,
+ 1, 0, 15, 128, 2, 0,
+ 228, 128, 45, 0, 228, 160,
+ 5, 0, 0, 3, 1, 0,
+ 15, 208, 1, 0, 228, 128,
+ 46, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 14, 0,
+ 85, 161, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 14, 0, 170, 160,
+ 11, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 255, 160, 10, 0,
+ 0, 3, 0, 0, 4, 224,
+ 0, 0, 0, 128, 11, 0,
+ 170, 160, 1, 0, 0, 2,
+ 0, 0, 3, 224, 2, 0,
+ 228, 144, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/matfx_env_amb_VS.h b/src/d3d/shaders/matfx_env_amb_VS.h
new file mode 100644
index 0000000..0367d5e
--- /dev/null
+++ b/src/d3d/shaders/matfx_env_amb_VS.h
@@ -0,0 +1,225 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /Fh matfx_env_amb_VS.h matfx_env_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4 colorClamp;
+// float4x4 combinedMat;
+// float4 envColor;
+// float4 fogData;
+// float4 matCol;
+// float3x3 normalMat;
+// float4 surfProps;
+// float4x4 texMat;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// combinedMat c0 4
+// normalMat c8 3
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// texMat c41 4
+// colorClamp c45 1
+// envColor c46 1
+//
+
+ vs_2_0
+ def c4, 0, 1, 0, 0
+ dcl_position v0
+ dcl_normal v1
+ dcl_texcoord v2
+ dcl_color v3
+ mul r0.xyz, v1.y, c9
+ mad r0.xyz, c8, v1.x, r0
+ mad r0.xyz, c10, v1.z, r0
+ mul r0.yw, r0.y, c42.xxzy
+ mad r0.xy, c41, r0.x, r0.ywzw
+ mad r0.xy, c43, r0.z, r0
+ add oT1.xy, r0, c44
+ mov r0.xyz, c15
+ mad r0.xyz, r0, c13.x, v3
+ mov r0.w, v3.w
+ max r0, r0, c4.x
+ min r0, r0, c4.y
+ max r1, r0, c45
+ mul oD0, r0, c12
+ mul oD1, r1, c46
+ mul r0, v0.y, c1
+ mad r0, c0, v0.x, r0
+ mad r0, c2, v0.z, r0
+ mad r0, c3, v0.w, r0
+ add r1.x, r0.w, -c14.y
+ mov oPos, r0
+ mul r0.x, r1.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c4.y
+ mov oT0.xy, v2
+
+// approximately 25 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 103, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 100, 1,
+ 0, 0, 0, 2, 254, 255,
+ 9, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 93, 1, 0, 0, 208, 0,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 224, 0,
+ 0, 0, 0, 0, 0, 0,
+ 240, 0, 0, 0, 2, 0,
+ 45, 0, 1, 0, 182, 0,
+ 224, 0, 0, 0, 0, 0,
+ 0, 0, 251, 0, 0, 0,
+ 2, 0, 0, 0, 4, 0,
+ 2, 0, 8, 1, 0, 0,
+ 0, 0, 0, 0, 24, 1,
+ 0, 0, 2, 0, 46, 0,
+ 1, 0, 186, 0, 224, 0,
+ 0, 0, 0, 0, 0, 0,
+ 33, 1, 0, 0, 2, 0,
+ 14, 0, 1, 0, 58, 0,
+ 224, 0, 0, 0, 0, 0,
+ 0, 0, 41, 1, 0, 0,
+ 2, 0, 12, 0, 1, 0,
+ 50, 0, 224, 0, 0, 0,
+ 0, 0, 0, 0, 48, 1,
+ 0, 0, 2, 0, 8, 0,
+ 3, 0, 34, 0, 60, 1,
+ 0, 0, 0, 0, 0, 0,
+ 76, 1, 0, 0, 2, 0,
+ 13, 0, 1, 0, 54, 0,
+ 224, 0, 0, 0, 0, 0,
+ 0, 0, 86, 1, 0, 0,
+ 2, 0, 41, 0, 4, 0,
+ 166, 0, 8, 1, 0, 0,
+ 0, 0, 0, 0, 97, 109,
+ 98, 105, 101, 110, 116, 76,
+ 105, 103, 104, 116, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 108, 111, 114, 67,
+ 108, 97, 109, 112, 0, 99,
+ 111, 109, 98, 105, 110, 101,
+ 100, 77, 97, 116, 0, 171,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 101, 110,
+ 118, 67, 111, 108, 111, 114,
+ 0, 102, 111, 103, 68, 97,
+ 116, 97, 0, 109, 97, 116,
+ 67, 111, 108, 0, 110, 111,
+ 114, 109, 97, 108, 77, 97,
+ 116, 0, 171, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 115, 117, 114, 102,
+ 80, 114, 111, 112, 115, 0,
+ 116, 101, 120, 77, 97, 116,
+ 0, 118, 115, 95, 50, 95,
+ 48, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 144,
+ 31, 0, 0, 2, 3, 0,
+ 0, 128, 1, 0, 15, 144,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 2, 0, 15, 144,
+ 31, 0, 0, 2, 10, 0,
+ 0, 128, 3, 0, 15, 144,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 1, 0, 85, 144,
+ 9, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 7, 128,
+ 8, 0, 228, 160, 1, 0,
+ 0, 144, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 10, 0, 228, 160,
+ 1, 0, 170, 144, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 10, 128, 0, 0,
+ 85, 128, 42, 0, 96, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 41, 0, 228, 160,
+ 0, 0, 0, 128, 0, 0,
+ 237, 128, 4, 0, 0, 4,
+ 0, 0, 3, 128, 43, 0,
+ 228, 160, 0, 0, 170, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 3, 224,
+ 0, 0, 228, 128, 44, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 7, 128, 15, 0,
+ 228, 160, 4, 0, 0, 4,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 13, 0, 0, 160,
+ 3, 0, 228, 144, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 3, 0, 255, 144, 11, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 4, 0,
+ 0, 160, 10, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 4, 0, 85, 160,
+ 11, 0, 0, 3, 1, 0,
+ 15, 128, 0, 0, 228, 128,
+ 45, 0, 228, 160, 5, 0,
+ 0, 3, 0, 0, 15, 208,
+ 0, 0, 228, 128, 12, 0,
+ 228, 160, 5, 0, 0, 3,
+ 1, 0, 15, 208, 1, 0,
+ 228, 128, 46, 0, 228, 160,
+ 5, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 85, 144,
+ 1, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 0, 0, 228, 160, 0, 0,
+ 0, 144, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 2, 0, 228, 160,
+ 0, 0, 170, 144, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 3, 0,
+ 228, 160, 0, 0, 255, 144,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 1, 128,
+ 0, 0, 255, 128, 14, 0,
+ 85, 161, 1, 0, 0, 2,
+ 0, 0, 15, 192, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 1, 128, 1, 0,
+ 0, 128, 14, 0, 170, 160,
+ 11, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 255, 160, 10, 0,
+ 0, 3, 0, 0, 4, 224,
+ 0, 0, 0, 128, 4, 0,
+ 85, 160, 1, 0, 0, 2,
+ 0, 0, 3, 224, 2, 0,
+ 228, 144, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/matfx_env_amb_dir_VS.h b/src/d3d/shaders/matfx_env_amb_dir_VS.h
new file mode 100644
index 0000000..b93e572
--- /dev/null
+++ b/src/d3d/shaders/matfx_env_amb_dir_VS.h
@@ -0,0 +1,316 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /DDIRECTIONALS /Fh matfx_env_amb_dir_VS.h
+// matfx_env_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4 colorClamp;
+// float4x4 combinedMat;
+// float4 envColor;
+// int4 firstLight;
+// float4 fogData;
+//
+// struct
+// {
+// float4 color;
+// float4 position;
+// float4 direction;
+//
+// } lights[8];
+//
+// float4 matCol;
+// float3x3 normalMat;
+// int numDirLights;
+// float4 surfProps;
+// float4x4 texMat;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// numDirLights i0 1
+// combinedMat c0 4
+// normalMat c8 3
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// firstLight c16 1
+// lights c17 24
+// texMat c41 4
+// colorClamp c45 1
+// envColor c46 1
+//
+
+ vs_2_0
+ def c4, 0, 3, 1, 0
+ dcl_position v0
+ dcl_normal v1
+ dcl_texcoord v2
+ dcl_color v3
+ mul r0, v0.y, c1
+ mad r0, c0, v0.x, r0
+ mad r0, c2, v0.z, r0
+ mad r0, c3, v0.w, r0
+ mov oPos, r0
+ mul r0.xyz, v1.y, c9
+ mad r0.xyz, c8, v1.x, r0
+ mad r0.xyz, c10, v1.z, r0
+ mov r1.x, c13.x
+ mad r1.xyz, c15, r1.x, v3
+ mov r2.xyz, r1
+ mov r1.w, c4.x
+ rep i0
+ add r3.x, r1.w, c16.x
+ mul r3.x, r3.x, c4.y
+ mova a0.x, r3.x
+ dp3 r3.x, r0, -c19[a0.x]
+ max r3.x, r3.x, c4.x
+ mul r3.xyz, r3.x, c17[a0.x]
+ mad r2.xyz, r3, c13.z, r2
+ add r1.w, r1.w, c4.z
+ endrep
+ mov r2.w, v3.w
+ max r1, r2, c4.x
+ min r1, r1, c4.z
+ mul oD0, r1, c12
+ mul r2.xy, r0.y, c42
+ mad r0.xy, c41, r0.x, r2
+ mad r0.xy, c43, r0.z, r0
+ add oT1.xy, r0, c44
+ max r1, r1, c45
+ mul oD1, r1, c46
+ add r0.x, r0.w, -c14.y
+ mul r0.x, r0.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c4.z
+ mov oT0.xy, v2
+
+// approximately 40 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 156, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 56, 2,
+ 0, 0, 0, 2, 254, 255,
+ 12, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 49, 2, 0, 0, 12, 1,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 28, 1,
+ 0, 0, 0, 0, 0, 0,
+ 44, 1, 0, 0, 2, 0,
+ 45, 0, 1, 0, 182, 0,
+ 28, 1, 0, 0, 0, 0,
+ 0, 0, 55, 1, 0, 0,
+ 2, 0, 0, 0, 4, 0,
+ 2, 0, 68, 1, 0, 0,
+ 0, 0, 0, 0, 84, 1,
+ 0, 0, 2, 0, 46, 0,
+ 1, 0, 186, 0, 28, 1,
+ 0, 0, 0, 0, 0, 0,
+ 93, 1, 0, 0, 2, 0,
+ 16, 0, 1, 0, 66, 0,
+ 104, 1, 0, 0, 0, 0,
+ 0, 0, 120, 1, 0, 0,
+ 2, 0, 14, 0, 1, 0,
+ 58, 0, 28, 1, 0, 0,
+ 0, 0, 0, 0, 128, 1,
+ 0, 0, 2, 0, 17, 0,
+ 24, 0, 70, 0, 204, 1,
+ 0, 0, 0, 0, 0, 0,
+ 220, 1, 0, 0, 2, 0,
+ 12, 0, 1, 0, 50, 0,
+ 28, 1, 0, 0, 0, 0,
+ 0, 0, 227, 1, 0, 0,
+ 2, 0, 8, 0, 3, 0,
+ 34, 0, 240, 1, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 1, 0, 0, 0,
+ 1, 0, 2, 0, 16, 2,
+ 0, 0, 0, 0, 0, 0,
+ 32, 2, 0, 0, 2, 0,
+ 13, 0, 1, 0, 54, 0,
+ 28, 1, 0, 0, 0, 0,
+ 0, 0, 42, 2, 0, 0,
+ 2, 0, 41, 0, 4, 0,
+ 166, 0, 68, 1, 0, 0,
+ 0, 0, 0, 0, 97, 109,
+ 98, 105, 101, 110, 116, 76,
+ 105, 103, 104, 116, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 99, 111, 108, 111, 114, 67,
+ 108, 97, 109, 112, 0, 99,
+ 111, 109, 98, 105, 110, 101,
+ 100, 77, 97, 116, 0, 171,
+ 3, 0, 3, 0, 4, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 101, 110,
+ 118, 67, 111, 108, 111, 114,
+ 0, 102, 105, 114, 115, 116,
+ 76, 105, 103, 104, 116, 0,
+ 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 102, 111,
+ 103, 68, 97, 116, 97, 0,
+ 108, 105, 103, 104, 116, 115,
+ 0, 99, 111, 108, 111, 114,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 112, 111, 115, 105,
+ 116, 105, 111, 110, 0, 100,
+ 105, 114, 101, 99, 116, 105,
+ 111, 110, 0, 171, 135, 1,
+ 0, 0, 144, 1, 0, 0,
+ 160, 1, 0, 0, 144, 1,
+ 0, 0, 169, 1, 0, 0,
+ 144, 1, 0, 0, 5, 0,
+ 0, 0, 1, 0, 12, 0,
+ 8, 0, 3, 0, 180, 1,
+ 0, 0, 109, 97, 116, 67,
+ 111, 108, 0, 110, 111, 114,
+ 109, 97, 108, 77, 97, 116,
+ 0, 171, 171, 171, 3, 0,
+ 3, 0, 3, 0, 3, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 110, 117, 109, 68,
+ 105, 114, 76, 105, 103, 104,
+ 116, 115, 0, 171, 171, 171,
+ 0, 0, 2, 0, 1, 0,
+ 1, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 115, 117,
+ 114, 102, 80, 114, 111, 112,
+ 115, 0, 116, 101, 120, 77,
+ 97, 116, 0, 118, 115, 95,
+ 50, 95, 48, 0, 77, 105,
+ 99, 114, 111, 115, 111, 102,
+ 116, 32, 40, 82, 41, 32,
+ 72, 76, 83, 76, 32, 83,
+ 104, 97, 100, 101, 114, 32,
+ 67, 111, 109, 112, 105, 108,
+ 101, 114, 32, 57, 46, 50,
+ 57, 46, 57, 53, 50, 46,
+ 51, 49, 49, 49, 0, 171,
+ 171, 171, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 0, 0, 0, 0, 64, 64,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 31, 0, 0, 2,
+ 3, 0, 0, 128, 1, 0,
+ 15, 144, 31, 0, 0, 2,
+ 5, 0, 0, 128, 2, 0,
+ 15, 144, 31, 0, 0, 2,
+ 10, 0, 0, 128, 3, 0,
+ 15, 144, 5, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 85, 144, 1, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 0, 0, 228, 160,
+ 0, 0, 0, 144, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 2, 0,
+ 228, 160, 0, 0, 170, 144,
+ 0, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 3, 0, 228, 160, 0, 0,
+ 255, 144, 0, 0, 228, 128,
+ 1, 0, 0, 2, 0, 0,
+ 15, 192, 0, 0, 228, 128,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 1, 0, 85, 144,
+ 9, 0, 228, 160, 4, 0,
+ 0, 4, 0, 0, 7, 128,
+ 8, 0, 228, 160, 1, 0,
+ 0, 144, 0, 0, 228, 128,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 10, 0, 228, 160,
+ 1, 0, 170, 144, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 0, 1, 128, 13, 0,
+ 0, 160, 4, 0, 0, 4,
+ 1, 0, 7, 128, 15, 0,
+ 228, 160, 1, 0, 0, 128,
+ 3, 0, 228, 144, 1, 0,
+ 0, 2, 2, 0, 7, 128,
+ 1, 0, 228, 128, 1, 0,
+ 0, 2, 1, 0, 8, 128,
+ 4, 0, 0, 160, 38, 0,
+ 0, 1, 0, 0, 228, 240,
+ 2, 0, 0, 3, 3, 0,
+ 1, 128, 1, 0, 255, 128,
+ 16, 0, 0, 160, 5, 0,
+ 0, 3, 3, 0, 1, 128,
+ 3, 0, 0, 128, 4, 0,
+ 85, 160, 46, 0, 0, 2,
+ 0, 0, 1, 176, 3, 0,
+ 0, 128, 8, 0, 0, 4,
+ 3, 0, 1, 128, 0, 0,
+ 228, 128, 19, 32, 228, 161,
+ 0, 0, 0, 176, 11, 0,
+ 0, 3, 3, 0, 1, 128,
+ 3, 0, 0, 128, 4, 0,
+ 0, 160, 5, 0, 0, 4,
+ 3, 0, 7, 128, 3, 0,
+ 0, 128, 17, 32, 228, 160,
+ 0, 0, 0, 176, 4, 0,
+ 0, 4, 2, 0, 7, 128,
+ 3, 0, 228, 128, 13, 0,
+ 170, 160, 2, 0, 228, 128,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 4, 0, 170, 160, 39, 0,
+ 0, 0, 1, 0, 0, 2,
+ 2, 0, 8, 128, 3, 0,
+ 255, 144, 11, 0, 0, 3,
+ 1, 0, 15, 128, 2, 0,
+ 228, 128, 4, 0, 0, 160,
+ 10, 0, 0, 3, 1, 0,
+ 15, 128, 1, 0, 228, 128,
+ 4, 0, 170, 160, 5, 0,
+ 0, 3, 0, 0, 15, 208,
+ 1, 0, 228, 128, 12, 0,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 3, 128, 0, 0,
+ 85, 128, 42, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 3, 128, 41, 0, 228, 160,
+ 0, 0, 0, 128, 2, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 3, 128, 43, 0,
+ 228, 160, 0, 0, 170, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 3, 224,
+ 0, 0, 228, 128, 44, 0,
+ 228, 160, 11, 0, 0, 3,
+ 1, 0, 15, 128, 1, 0,
+ 228, 128, 45, 0, 228, 160,
+ 5, 0, 0, 3, 1, 0,
+ 15, 208, 1, 0, 228, 128,
+ 46, 0, 228, 160, 2, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 14, 0,
+ 85, 161, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 14, 0, 170, 160,
+ 11, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 255, 160, 10, 0,
+ 0, 3, 0, 0, 4, 224,
+ 0, 0, 0, 128, 4, 0,
+ 170, 160, 1, 0, 0, 2,
+ 0, 0, 3, 224, 2, 0,
+ 228, 144, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/matfx_env_tex_PS.h b/src/d3d/shaders/matfx_env_tex_PS.h
new file mode 100644
index 0000000..e2e7429
--- /dev/null
+++ b/src/d3d/shaders/matfx_env_tex_PS.h
@@ -0,0 +1,141 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T ps_2_0 /DTEX /Fh matfx_env_tex_PS.h matfx_env_PS.hlsl
+//
+//
+// Parameters:
+//
+// sampler2D diffTex;
+// sampler2D envTex;
+// float4 fogColor;
+// float4 fxparams;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// fogColor c0 1
+// fxparams c1 1
+// diffTex s0 1
+// envTex s1 1
+//
+
+ ps_2_0
+ dcl t0.xyz
+ dcl t1.xy
+ dcl v0
+ dcl v1.xyz
+ dcl_2d s0
+ dcl_2d s1
+ texld r0, t1, s1
+ texld r1, t0, s0
+ mul r2.xyz, v1, c1.x
+ mul r0.xyz, r0, r2
+ mul r0.xyz, r0, t0.z
+ mul r2.w, r1.w, v0.w
+ mad r1.xyz, v0, r1, -c0
+ mad r1.xyz, t0.z, r1, c0
+ max r0.w, r2.w, c1.y
+ mul r0.xyz, r0.w, r0
+ mad r2.xyz, r1, r2.w, r0
+ mov oC0, r2
+
+// approximately 12 instruction slots used (2 texture, 10 arithmetic)
+#endif
+
+const BYTE g_ps20_main[] =
+{
+ 0, 2, 255, 255, 254, 255,
+ 64, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 200, 0,
+ 0, 0, 0, 2, 255, 255,
+ 4, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 193, 0, 0, 0, 108, 0,
+ 0, 0, 3, 0, 0, 0,
+ 1, 0, 2, 0, 116, 0,
+ 0, 0, 0, 0, 0, 0,
+ 132, 0, 0, 0, 3, 0,
+ 1, 0, 1, 0, 6, 0,
+ 140, 0, 0, 0, 0, 0,
+ 0, 0, 156, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 2, 0, 168, 0, 0, 0,
+ 0, 0, 0, 0, 184, 0,
+ 0, 0, 2, 0, 1, 0,
+ 1, 0, 6, 0, 168, 0,
+ 0, 0, 0, 0, 0, 0,
+ 100, 105, 102, 102, 84, 101,
+ 120, 0, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 101, 110, 118, 84, 101, 120,
+ 0, 171, 4, 0, 12, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 111, 103, 67, 111, 108,
+ 111, 114, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 102, 120,
+ 112, 97, 114, 97, 109, 115,
+ 0, 112, 115, 95, 50, 95,
+ 48, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 7, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 3, 176,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 144,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 1, 0, 7, 144,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 0, 8, 15, 160,
+ 31, 0, 0, 2, 0, 0,
+ 0, 144, 1, 8, 15, 160,
+ 66, 0, 0, 3, 0, 0,
+ 15, 128, 1, 0, 228, 176,
+ 1, 8, 228, 160, 66, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 228, 176, 0, 8,
+ 228, 160, 5, 0, 0, 3,
+ 2, 0, 7, 128, 1, 0,
+ 228, 144, 1, 0, 0, 160,
+ 5, 0, 0, 3, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 2, 0, 228, 128, 5, 0,
+ 0, 3, 0, 0, 7, 128,
+ 0, 0, 228, 128, 0, 0,
+ 170, 176, 5, 0, 0, 3,
+ 2, 0, 8, 128, 1, 0,
+ 255, 128, 0, 0, 255, 144,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 0, 0, 228, 144,
+ 1, 0, 228, 128, 0, 0,
+ 228, 161, 4, 0, 0, 4,
+ 1, 0, 7, 128, 0, 0,
+ 170, 176, 1, 0, 228, 128,
+ 0, 0, 228, 160, 11, 0,
+ 0, 3, 0, 0, 8, 128,
+ 2, 0, 255, 128, 1, 0,
+ 85, 160, 5, 0, 0, 3,
+ 0, 0, 7, 128, 0, 0,
+ 255, 128, 0, 0, 228, 128,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 1, 0, 228, 128,
+ 2, 0, 255, 128, 0, 0,
+ 228, 128, 1, 0, 0, 2,
+ 0, 8, 15, 128, 2, 0,
+ 228, 128, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/skin_VS.hlsl b/src/d3d/shaders/skin_VS.hlsl
new file mode 100644
index 0000000..bede50f
--- /dev/null
+++ b/src/d3d/shaders/skin_VS.hlsl
@@ -0,0 +1,63 @@
+#include "standardConstants.h"
+
+float4x3 boneMatrices[64] : register(c41);
+
+struct VS_in
+{
+ float4 Position : POSITION;
+ float3 Normal : NORMAL;
+ float2 TexCoord : TEXCOORD0;
+ float4 Prelight : COLOR0;
+ float4 Weights : BLENDWEIGHT;
+ int4 Indices : BLENDINDICES;
+};
+
+struct VS_out {
+ float4 Position : POSITION;
+ float3 TexCoord0 : TEXCOORD0; // also fog
+ float4 Color : COLOR0;
+};
+
+
+VS_out main(in VS_in input)
+{
+ VS_out output;
+
+ int j;
+ float3 SkinVertex = float3(0.0, 0.0, 0.0);
+ float3 SkinNormal = float3(0.0, 0.0, 0.0);
+ for(j = 0; j < 4; j++){
+ SkinVertex += mul(input.Position, boneMatrices[input.Indices[j]]).xyz * input.Weights[j];
+ SkinNormal += mul(input.Normal, (float3x3)boneMatrices[input.Indices[j]]).xyz * input.Weights[j];
+ }
+
+ output.Position = mul(combinedMat, float4(SkinVertex, 1.0));
+ float3 Vertex = mul(worldMat, float4(SkinVertex, 1.0)).xyz;
+ float3 Normal = mul(normalMat, SkinNormal);
+
+ output.TexCoord0.xy = input.TexCoord;
+
+ output.Color = input.Prelight;
+ output.Color.rgb += ambientLight.rgb * surfAmbient;
+
+ int i;
+#ifdef DIRECTIONALS
+ for(i = 0; i < numDirLights; i++)
+ output.Color.xyz += DoDirLight(lights[i+firstDirLight], Normal)*surfDiffuse;
+#endif
+#ifdef POINTLIGHTS
+ for(i = 0; i < numPointLights; i++)
+ output.Color.xyz += DoPointLight(lights[i+firstPointLight], Vertex.xyz, Normal)*surfDiffuse;
+#endif
+#ifdef SPOTLIGHTS
+ for(i = 0; i < numSpotLights; i++)
+ output.Color.xyz += DoSpotLight(lights[i+firstSpotLight], Vertex.xyz, Normal)*surfDiffuse;
+#endif
+ // PS2 clamps before material color
+ output.Color = clamp(output.Color, 0.0, 1.0);
+ output.Color *= matCol;
+
+ output.TexCoord0.z = clamp((output.Position.w - fogEnd)*fogRange, fogDisable, 1.0);
+
+ return output;
+}
diff --git a/src/d3d/shaders/skin_all_VS.h b/src/d3d/shaders/skin_all_VS.h
new file mode 100644
index 0000000..67f89f3
--- /dev/null
+++ b/src/d3d/shaders/skin_all_VS.h
@@ -0,0 +1,664 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /DDIRECTIONALS /DPOINTLIGHTS /DSPOTLIGHTS /Fh
+// skin_all_VS.h skin_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4x3 boneMatrices[64];
+// float4x4 combinedMat;
+// int4 firstLight;
+// float4 fogData;
+//
+// struct
+// {
+// float4 color;
+// float4 position;
+// float4 direction;
+//
+// } lights[8];
+//
+// float4 matCol;
+// float3x3 normalMat;
+// int numDirLights;
+// int numPointLights;
+// int numSpotLights;
+// float4 surfProps;
+// float4x4 worldMat;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// -------------- ----- ----
+// numDirLights i0 1
+// numPointLights i1 1
+// numSpotLights i2 1
+// combinedMat c0 4
+// worldMat c4 4
+// normalMat c8 3
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// firstLight c16 1
+// lights c17 24
+// boneMatrices c41 192
+//
+
+ vs_2_0
+ def c11, 3, 0, 1, 0
+ dcl_position v0
+ dcl_normal v1
+ dcl_texcoord v2
+ dcl_color v3
+ dcl_blendweight v4
+ dcl_blendindices v5
+ mul r0, v5, c11.x
+ mova a0.x, r0.x
+ dp3 r1.z, v1, c43[a0.x]
+ dp4 r2.x, v0, c41[a0.x]
+ dp4 r2.y, v0, c42[a0.x]
+ dp4 r2.z, v0, c43[a0.x]
+ dp3 r1.x, v1, c41[a0.x]
+ dp3 r1.y, v1, c42[a0.x]
+ mova a0.x, r0.y
+ dp3 r3.z, v1, c43[a0.x]
+ dp4 r4.x, v0, c41[a0.x]
+ dp4 r4.y, v0, c42[a0.x]
+ dp4 r4.z, v0, c43[a0.x]
+ mul r4.xyz, r4, v4.y
+ mad r2.xyz, r2, v4.x, r4
+ dp3 r3.x, v1, c41[a0.x]
+ dp3 r3.y, v1, c42[a0.x]
+ mul r3.xyz, r3, v4.y
+ mad r1.xyz, r1, v4.x, r3
+ mova a0.x, r0.z
+ dp3 r0.z, v1, c43[a0.x]
+ dp4 r3.x, v0, c41[a0.x]
+ dp4 r3.y, v0, c42[a0.x]
+ dp4 r3.z, v0, c43[a0.x]
+ mad r2.xyz, r3, v4.z, r2
+ dp3 r0.x, v1, c41[a0.x]
+ dp3 r0.y, v1, c42[a0.x]
+ mad r0.xyz, r0, v4.z, r1
+ mova a0.x, r0.w
+ dp3 r1.z, v1, c43[a0.x]
+ dp4 r3.x, v0, c41[a0.x]
+ dp4 r3.y, v0, c42[a0.x]
+ dp4 r3.z, v0, c43[a0.x]
+ mad r2.xyz, r3, v4.w, r2
+ dp3 r1.x, v1, c41[a0.x]
+ dp3 r1.y, v1, c42[a0.x]
+ mad r0.xyz, r1, v4.w, r0
+ mul r1.xyz, r2.y, c5
+ mad r1.xyz, c4, r2.x, r1
+ mad r1.xyz, c6, r2.z, r1
+ add r1.xyz, r1, c7
+ mul r3.xyz, r0.y, c9
+ mad r0.xyw, c8.xyzz, r0.x, r3.xyzz
+ mad r0.xyz, c10, r0.z, r0.xyww
+ mov r3.x, c13.x
+ mad r3.xyz, c15, r3.x, v3
+ mov r4.xyz, r3
+ mov r0.w, c11.y
+ rep i0
+ add r1.w, r0.w, c16.x
+ mul r1.w, r1.w, c11.x
+ mova a0.x, r1.w
+ dp3 r1.w, r0, -c19[a0.x]
+ max r1.w, r1.w, c11.y
+ mul r5.xyz, r1.w, c17[a0.x]
+ mad r4.xyz, r5, c13.z, r4
+ add r0.w, r0.w, c11.z
+ endrep
+ mov r3.xyz, r4
+ mov r0.w, c11.y
+ rep i1
+ add r1.w, r0.w, c16.y
+ mul r1.w, r1.w, c11.x
+ mova a0.x, r1.w
+ add r5.xyz, r1, -c18[a0.x]
+ dp3 r1.w, r5, r5
+ rsq r1.w, r1.w
+ mul r5.xyz, r1.w, r5
+ dp3 r2.w, r0, -r5
+ max r2.w, r2.w, c11.y
+ mul r5.xyz, r2.w, c17[a0.x]
+ rcp r1.w, r1.w
+ rcp r2.w, c17[a0.x].w
+ mad r1.w, r1.w, -r2.w, c11.z
+ max r1.w, r1.w, c11.y
+ mul r5.xyz, r1.w, r5
+ mad r3.xyz, r5, c13.z, r3
+ add r0.w, r0.w, c11.z
+ endrep
+ mov r4.xyz, r3
+ mov r0.w, c11.y
+ rep i2
+ add r1.w, r0.w, c16.z
+ mul r1.w, r1.w, c11.x
+ mova a0.x, r1.w
+ add r5.xyz, r1, -c18[a0.x]
+ dp3 r1.w, r5, r5
+ rsq r1.w, r1.w
+ mul r5.xyz, r1.w, r5
+ dp3 r2.w, r0, -r5
+ dp3 r3.w, r5, c19[a0.x]
+ max r2.w, r2.w, c11.y
+ add r3.w, r3.w, c18[a0.x].w
+ mov r5.z, c11.z
+ add r5.x, r5.z, c18[a0.x].w
+ rcp r5.x, r5.x
+ mul r3.w, r3.w, r5.x
+ slt r5.x, r3.w, c11.y
+ mad r2.w, r5.x, -r2.w, r2.w
+ max r3.w, r3.w, c19[a0.x].w
+ mul r2.w, r2.w, r3.w
+ mul r5.xyz, r2.w, c17[a0.x]
+ rcp r1.w, r1.w
+ rcp r2.w, c17[a0.x].w
+ mad r1.w, r1.w, -r2.w, c11.z
+ max r1.w, r1.w, c11.y
+ mul r5.xyz, r1.w, r5
+ mad r4.xyz, r5, c13.z, r4
+ add r0.w, r0.w, c11.z
+ endrep
+ mov r4.w, v3.w
+ max r0, r4, c11.y
+ min r0, r0, c11.z
+ mul oD0, r0, c12
+ mul r0, r2.y, c1
+ mad r0, c0, r2.x, r0
+ mad r0, c2, r2.z, r0
+ add r0, r0, c3
+ mov oPos, r0
+ add r0.x, r0.w, -c14.y
+ mul r0.x, r0.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c11.z
+ mov oT0.xy, v2
+
+// approximately 133 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 171, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 119, 2,
+ 0, 0, 0, 2, 254, 255,
+ 13, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 112, 2, 0, 0, 32, 1,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 48, 1,
+ 0, 0, 0, 0, 0, 0,
+ 64, 1, 0, 0, 2, 0,
+ 41, 0, 192, 0, 166, 0,
+ 80, 1, 0, 0, 0, 0,
+ 0, 0, 96, 1, 0, 0,
+ 2, 0, 0, 0, 4, 0,
+ 2, 0, 108, 1, 0, 0,
+ 0, 0, 0, 0, 124, 1,
+ 0, 0, 2, 0, 16, 0,
+ 1, 0, 66, 0, 136, 1,
+ 0, 0, 0, 0, 0, 0,
+ 152, 1, 0, 0, 2, 0,
+ 14, 0, 1, 0, 58, 0,
+ 48, 1, 0, 0, 0, 0,
+ 0, 0, 160, 1, 0, 0,
+ 2, 0, 17, 0, 24, 0,
+ 70, 0, 236, 1, 0, 0,
+ 0, 0, 0, 0, 252, 1,
+ 0, 0, 2, 0, 12, 0,
+ 1, 0, 50, 0, 48, 1,
+ 0, 0, 0, 0, 0, 0,
+ 3, 2, 0, 0, 2, 0,
+ 8, 0, 3, 0, 34, 0,
+ 16, 2, 0, 0, 0, 0,
+ 0, 0, 32, 2, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 2, 0, 48, 2, 0, 0,
+ 0, 0, 0, 0, 64, 2,
+ 0, 0, 1, 0, 1, 0,
+ 1, 0, 6, 0, 48, 2,
+ 0, 0, 0, 0, 0, 0,
+ 79, 2, 0, 0, 1, 0,
+ 2, 0, 1, 0, 10, 0,
+ 48, 2, 0, 0, 0, 0,
+ 0, 0, 93, 2, 0, 0,
+ 2, 0, 13, 0, 1, 0,
+ 54, 0, 48, 1, 0, 0,
+ 0, 0, 0, 0, 103, 2,
+ 0, 0, 2, 0, 4, 0,
+ 4, 0, 18, 0, 108, 1,
+ 0, 0, 0, 0, 0, 0,
+ 97, 109, 98, 105, 101, 110,
+ 116, 76, 105, 103, 104, 116,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 77, 97, 116, 114, 105, 99,
+ 101, 115, 0, 171, 171, 171,
+ 3, 0, 3, 0, 4, 0,
+ 3, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 99, 111,
+ 109, 98, 105, 110, 101, 100,
+ 77, 97, 116, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 102, 105, 114, 115,
+ 116, 76, 105, 103, 104, 116,
+ 0, 171, 1, 0, 2, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 111, 103, 68, 97, 116,
+ 97, 0, 108, 105, 103, 104,
+ 116, 115, 0, 99, 111, 108,
+ 111, 114, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 111,
+ 115, 105, 116, 105, 111, 110,
+ 0, 100, 105, 114, 101, 99,
+ 116, 105, 111, 110, 0, 171,
+ 167, 1, 0, 0, 176, 1,
+ 0, 0, 192, 1, 0, 0,
+ 176, 1, 0, 0, 201, 1,
+ 0, 0, 176, 1, 0, 0,
+ 5, 0, 0, 0, 1, 0,
+ 12, 0, 8, 0, 3, 0,
+ 212, 1, 0, 0, 109, 97,
+ 116, 67, 111, 108, 0, 110,
+ 111, 114, 109, 97, 108, 77,
+ 97, 116, 0, 171, 171, 171,
+ 3, 0, 3, 0, 3, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 110, 117,
+ 109, 68, 105, 114, 76, 105,
+ 103, 104, 116, 115, 0, 171,
+ 171, 171, 0, 0, 2, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 110, 117, 109, 80, 111, 105,
+ 110, 116, 76, 105, 103, 104,
+ 116, 115, 0, 110, 117, 109,
+ 83, 112, 111, 116, 76, 105,
+ 103, 104, 116, 115, 0, 115,
+ 117, 114, 102, 80, 114, 111,
+ 112, 115, 0, 119, 111, 114,
+ 108, 100, 77, 97, 116, 0,
+ 118, 115, 95, 50, 95, 48,
+ 0, 77, 105, 99, 114, 111,
+ 115, 111, 102, 116, 32, 40,
+ 82, 41, 32, 72, 76, 83,
+ 76, 32, 83, 104, 97, 100,
+ 101, 114, 32, 67, 111, 109,
+ 112, 105, 108, 101, 114, 32,
+ 57, 46, 50, 57, 46, 57,
+ 53, 50, 46, 51, 49, 49,
+ 49, 0, 81, 0, 0, 5,
+ 11, 0, 15, 160, 0, 0,
+ 64, 64, 0, 0, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 31, 0, 0, 2,
+ 3, 0, 0, 128, 1, 0,
+ 15, 144, 31, 0, 0, 2,
+ 5, 0, 0, 128, 2, 0,
+ 15, 144, 31, 0, 0, 2,
+ 10, 0, 0, 128, 3, 0,
+ 15, 144, 31, 0, 0, 2,
+ 1, 0, 0, 128, 4, 0,
+ 15, 144, 31, 0, 0, 2,
+ 2, 0, 0, 128, 5, 0,
+ 15, 144, 5, 0, 0, 3,
+ 0, 0, 15, 128, 5, 0,
+ 228, 144, 11, 0, 0, 160,
+ 46, 0, 0, 2, 0, 0,
+ 1, 176, 0, 0, 0, 128,
+ 8, 0, 0, 4, 1, 0,
+ 4, 128, 1, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 0, 176, 9, 0, 0, 4,
+ 2, 0, 1, 128, 0, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 0, 176, 9, 0,
+ 0, 4, 2, 0, 2, 128,
+ 0, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 0, 176,
+ 9, 0, 0, 4, 2, 0,
+ 4, 128, 0, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 0, 176, 8, 0, 0, 4,
+ 1, 0, 1, 128, 1, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 0, 176, 8, 0,
+ 0, 4, 1, 0, 2, 128,
+ 1, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 0, 176,
+ 46, 0, 0, 2, 0, 0,
+ 1, 176, 0, 0, 85, 128,
+ 8, 0, 0, 4, 3, 0,
+ 4, 128, 1, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 0, 176, 9, 0, 0, 4,
+ 4, 0, 1, 128, 0, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 0, 176, 9, 0,
+ 0, 4, 4, 0, 2, 128,
+ 0, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 0, 176,
+ 9, 0, 0, 4, 4, 0,
+ 4, 128, 0, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 0, 176, 5, 0, 0, 3,
+ 4, 0, 7, 128, 4, 0,
+ 228, 128, 4, 0, 85, 144,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 2, 0, 228, 128,
+ 4, 0, 0, 144, 4, 0,
+ 228, 128, 8, 0, 0, 4,
+ 3, 0, 1, 128, 1, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 0, 176, 8, 0,
+ 0, 4, 3, 0, 2, 128,
+ 1, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 0, 176,
+ 5, 0, 0, 3, 3, 0,
+ 7, 128, 3, 0, 228, 128,
+ 4, 0, 85, 144, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 144, 3, 0, 228, 128,
+ 46, 0, 0, 2, 0, 0,
+ 1, 176, 0, 0, 170, 128,
+ 8, 0, 0, 4, 0, 0,
+ 4, 128, 1, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 0, 176, 9, 0, 0, 4,
+ 3, 0, 1, 128, 0, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 0, 176, 9, 0,
+ 0, 4, 3, 0, 2, 128,
+ 0, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 0, 176,
+ 9, 0, 0, 4, 3, 0,
+ 4, 128, 0, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 0, 176, 4, 0, 0, 4,
+ 2, 0, 7, 128, 3, 0,
+ 228, 128, 4, 0, 170, 144,
+ 2, 0, 228, 128, 8, 0,
+ 0, 4, 0, 0, 1, 128,
+ 1, 0, 228, 144, 41, 32,
+ 228, 160, 0, 0, 0, 176,
+ 8, 0, 0, 4, 0, 0,
+ 2, 128, 1, 0, 228, 144,
+ 42, 32, 228, 160, 0, 0,
+ 0, 176, 4, 0, 0, 4,
+ 0, 0, 7, 128, 0, 0,
+ 228, 128, 4, 0, 170, 144,
+ 1, 0, 228, 128, 46, 0,
+ 0, 2, 0, 0, 1, 176,
+ 0, 0, 255, 128, 8, 0,
+ 0, 4, 1, 0, 4, 128,
+ 1, 0, 228, 144, 43, 32,
+ 228, 160, 0, 0, 0, 176,
+ 9, 0, 0, 4, 3, 0,
+ 1, 128, 0, 0, 228, 144,
+ 41, 32, 228, 160, 0, 0,
+ 0, 176, 9, 0, 0, 4,
+ 3, 0, 2, 128, 0, 0,
+ 228, 144, 42, 32, 228, 160,
+ 0, 0, 0, 176, 9, 0,
+ 0, 4, 3, 0, 4, 128,
+ 0, 0, 228, 144, 43, 32,
+ 228, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 3, 0, 228, 128,
+ 4, 0, 255, 144, 2, 0,
+ 228, 128, 8, 0, 0, 4,
+ 1, 0, 1, 128, 1, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 0, 176, 8, 0,
+ 0, 4, 1, 0, 2, 128,
+ 1, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 1, 0, 228, 128,
+ 4, 0, 255, 144, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 1, 0, 7, 128, 2, 0,
+ 85, 128, 5, 0, 228, 160,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 4, 0, 228, 160,
+ 2, 0, 0, 128, 1, 0,
+ 228, 128, 4, 0, 0, 4,
+ 1, 0, 7, 128, 6, 0,
+ 228, 160, 2, 0, 170, 128,
+ 1, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 7, 128,
+ 1, 0, 228, 128, 7, 0,
+ 228, 160, 5, 0, 0, 3,
+ 3, 0, 7, 128, 0, 0,
+ 85, 128, 9, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 11, 128, 8, 0, 164, 160,
+ 0, 0, 0, 128, 3, 0,
+ 164, 128, 4, 0, 0, 4,
+ 0, 0, 7, 128, 10, 0,
+ 228, 160, 0, 0, 170, 128,
+ 0, 0, 244, 128, 1, 0,
+ 0, 2, 3, 0, 1, 128,
+ 13, 0, 0, 160, 4, 0,
+ 0, 4, 3, 0, 7, 128,
+ 15, 0, 228, 160, 3, 0,
+ 0, 128, 3, 0, 228, 144,
+ 1, 0, 0, 2, 4, 0,
+ 7, 128, 3, 0, 228, 128,
+ 1, 0, 0, 2, 0, 0,
+ 8, 128, 11, 0, 85, 160,
+ 38, 0, 0, 1, 0, 0,
+ 228, 240, 2, 0, 0, 3,
+ 1, 0, 8, 128, 0, 0,
+ 255, 128, 16, 0, 0, 160,
+ 5, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 11, 0, 0, 160, 46, 0,
+ 0, 2, 0, 0, 1, 176,
+ 1, 0, 255, 128, 8, 0,
+ 0, 4, 1, 0, 8, 128,
+ 0, 0, 228, 128, 19, 32,
+ 228, 161, 0, 0, 0, 176,
+ 11, 0, 0, 3, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 11, 0, 85, 160, 5, 0,
+ 0, 4, 5, 0, 7, 128,
+ 1, 0, 255, 128, 17, 32,
+ 228, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 4, 0,
+ 7, 128, 5, 0, 228, 128,
+ 13, 0, 170, 160, 4, 0,
+ 228, 128, 2, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 11, 0, 170, 160,
+ 39, 0, 0, 0, 1, 0,
+ 0, 2, 3, 0, 7, 128,
+ 4, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 11, 0, 85, 160, 38, 0,
+ 0, 1, 1, 0, 228, 240,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 16, 0, 85, 160, 5, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 128, 11, 0,
+ 0, 160, 46, 0, 0, 2,
+ 0, 0, 1, 176, 1, 0,
+ 255, 128, 2, 0, 0, 4,
+ 5, 0, 7, 128, 1, 0,
+ 228, 128, 18, 32, 228, 161,
+ 0, 0, 0, 176, 8, 0,
+ 0, 3, 1, 0, 8, 128,
+ 5, 0, 228, 128, 5, 0,
+ 228, 128, 7, 0, 0, 2,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 5, 0, 0, 3,
+ 5, 0, 7, 128, 1, 0,
+ 255, 128, 5, 0, 228, 128,
+ 8, 0, 0, 3, 2, 0,
+ 8, 128, 0, 0, 228, 128,
+ 5, 0, 228, 129, 11, 0,
+ 0, 3, 2, 0, 8, 128,
+ 2, 0, 255, 128, 11, 0,
+ 85, 160, 5, 0, 0, 4,
+ 5, 0, 7, 128, 2, 0,
+ 255, 128, 17, 32, 228, 160,
+ 0, 0, 0, 176, 6, 0,
+ 0, 2, 1, 0, 8, 128,
+ 1, 0, 255, 128, 6, 0,
+ 0, 3, 2, 0, 8, 128,
+ 17, 32, 255, 160, 0, 0,
+ 0, 176, 4, 0, 0, 4,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 2, 0, 255, 129,
+ 11, 0, 170, 160, 11, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 128, 11, 0,
+ 85, 160, 5, 0, 0, 3,
+ 5, 0, 7, 128, 1, 0,
+ 255, 128, 5, 0, 228, 128,
+ 4, 0, 0, 4, 3, 0,
+ 7, 128, 5, 0, 228, 128,
+ 13, 0, 170, 160, 3, 0,
+ 228, 128, 2, 0, 0, 3,
+ 0, 0, 8, 128, 0, 0,
+ 255, 128, 11, 0, 170, 160,
+ 39, 0, 0, 0, 1, 0,
+ 0, 2, 4, 0, 7, 128,
+ 3, 0, 228, 128, 1, 0,
+ 0, 2, 0, 0, 8, 128,
+ 11, 0, 85, 160, 38, 0,
+ 0, 1, 2, 0, 228, 240,
+ 2, 0, 0, 3, 1, 0,
+ 8, 128, 0, 0, 255, 128,
+ 16, 0, 170, 160, 5, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 128, 11, 0,
+ 0, 160, 46, 0, 0, 2,
+ 0, 0, 1, 176, 1, 0,
+ 255, 128, 2, 0, 0, 4,
+ 5, 0, 7, 128, 1, 0,
+ 228, 128, 18, 32, 228, 161,
+ 0, 0, 0, 176, 8, 0,
+ 0, 3, 1, 0, 8, 128,
+ 5, 0, 228, 128, 5, 0,
+ 228, 128, 7, 0, 0, 2,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 5, 0, 0, 3,
+ 5, 0, 7, 128, 1, 0,
+ 255, 128, 5, 0, 228, 128,
+ 8, 0, 0, 3, 2, 0,
+ 8, 128, 0, 0, 228, 128,
+ 5, 0, 228, 129, 8, 0,
+ 0, 4, 3, 0, 8, 128,
+ 5, 0, 228, 128, 19, 32,
+ 228, 160, 0, 0, 0, 176,
+ 11, 0, 0, 3, 2, 0,
+ 8, 128, 2, 0, 255, 128,
+ 11, 0, 85, 160, 2, 0,
+ 0, 4, 3, 0, 8, 128,
+ 3, 0, 255, 128, 18, 32,
+ 255, 160, 0, 0, 0, 176,
+ 1, 0, 0, 2, 5, 0,
+ 4, 128, 11, 0, 170, 160,
+ 2, 0, 0, 4, 5, 0,
+ 1, 128, 5, 0, 170, 128,
+ 18, 32, 255, 160, 0, 0,
+ 0, 176, 6, 0, 0, 2,
+ 5, 0, 1, 128, 5, 0,
+ 0, 128, 5, 0, 0, 3,
+ 3, 0, 8, 128, 3, 0,
+ 255, 128, 5, 0, 0, 128,
+ 12, 0, 0, 3, 5, 0,
+ 1, 128, 3, 0, 255, 128,
+ 11, 0, 85, 160, 4, 0,
+ 0, 4, 2, 0, 8, 128,
+ 5, 0, 0, 128, 2, 0,
+ 255, 129, 2, 0, 255, 128,
+ 11, 0, 0, 4, 3, 0,
+ 8, 128, 3, 0, 255, 128,
+ 19, 32, 255, 160, 0, 0,
+ 0, 176, 5, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 3, 0, 255, 128,
+ 5, 0, 0, 4, 5, 0,
+ 7, 128, 2, 0, 255, 128,
+ 17, 32, 228, 160, 0, 0,
+ 0, 176, 6, 0, 0, 2,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 6, 0, 0, 3,
+ 2, 0, 8, 128, 17, 32,
+ 255, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 1, 0,
+ 8, 128, 1, 0, 255, 128,
+ 2, 0, 255, 129, 11, 0,
+ 170, 160, 11, 0, 0, 3,
+ 1, 0, 8, 128, 1, 0,
+ 255, 128, 11, 0, 85, 160,
+ 5, 0, 0, 3, 5, 0,
+ 7, 128, 1, 0, 255, 128,
+ 5, 0, 228, 128, 4, 0,
+ 0, 4, 4, 0, 7, 128,
+ 5, 0, 228, 128, 13, 0,
+ 170, 160, 4, 0, 228, 128,
+ 2, 0, 0, 3, 0, 0,
+ 8, 128, 0, 0, 255, 128,
+ 11, 0, 170, 160, 39, 0,
+ 0, 0, 1, 0, 0, 2,
+ 4, 0, 8, 128, 3, 0,
+ 255, 144, 11, 0, 0, 3,
+ 0, 0, 15, 128, 4, 0,
+ 228, 128, 11, 0, 85, 160,
+ 10, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 11, 0, 170, 160, 5, 0,
+ 0, 3, 0, 0, 15, 208,
+ 0, 0, 228, 128, 12, 0,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 2, 0,
+ 85, 128, 1, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 15, 128, 0, 0, 228, 160,
+ 2, 0, 0, 128, 0, 0,
+ 228, 128, 4, 0, 0, 4,
+ 0, 0, 15, 128, 2, 0,
+ 228, 160, 2, 0, 170, 128,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 15, 128,
+ 0, 0, 228, 128, 3, 0,
+ 228, 160, 1, 0, 0, 2,
+ 0, 0, 15, 192, 0, 0,
+ 228, 128, 2, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 255, 128, 14, 0, 85, 161,
+ 5, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 170, 160, 11, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 0, 128, 14, 0,
+ 255, 160, 10, 0, 0, 3,
+ 0, 0, 4, 224, 0, 0,
+ 0, 128, 11, 0, 170, 160,
+ 1, 0, 0, 2, 0, 0,
+ 3, 224, 2, 0, 228, 144,
+ 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/skin_amb_VS.h b/src/d3d/shaders/skin_amb_VS.h
new file mode 100644
index 0000000..e0a5548
--- /dev/null
+++ b/src/d3d/shaders/skin_amb_VS.h
@@ -0,0 +1,253 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /Fh skin_amb_VS.h skin_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4x3 boneMatrices[64];
+// float4x4 combinedMat;
+// float4 fogData;
+// float4 matCol;
+// float4 surfProps;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// combinedMat c0 4
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// boneMatrices c41 192
+//
+
+ vs_2_0
+ def c4, 3, 0, 1, 0
+ dcl_position v0
+ dcl_texcoord v1
+ dcl_color v2
+ dcl_blendweight v3
+ dcl_blendindices v4
+ mov r0.xyz, c15
+ mad r0.xyz, r0, c13.x, v2
+ mov r0.w, v2.w
+ max r0, r0, c4.y
+ min r0, r0, c4.z
+ mul oD0, r0, c12
+ mul r0, v4, c4.x
+ mova a0.x, r0.y
+ dp4 r1.x, v0, c41[a0.x]
+ dp4 r1.y, v0, c42[a0.x]
+ dp4 r1.z, v0, c43[a0.x]
+ mul r1.xyz, r1, v3.y
+ mova a0.x, r0.x
+ dp4 r2.x, v0, c41[a0.x]
+ dp4 r2.y, v0, c42[a0.x]
+ dp4 r2.z, v0, c43[a0.x]
+ mad r1.xyz, r2, v3.x, r1
+ mova a0.xy, r0.zwzw
+ dp4 r0.x, v0, c41[a0.x]
+ dp4 r0.y, v0, c42[a0.x]
+ dp4 r0.z, v0, c43[a0.x]
+ mad r0.xyz, r0, v3.z, r1
+ dp4 r1.x, v0, c41[a0.y]
+ dp4 r1.y, v0, c42[a0.y]
+ dp4 r1.z, v0, c43[a0.y]
+ mad r0.xyz, r1, v3.w, r0
+ mul r1, r0.y, c1
+ mad r1, c0, r0.x, r1
+ mad r0, c2, r0.z, r1
+ add r0, r0, c3
+ add r1.x, r0.w, -c14.y
+ mov oPos, r0
+ mul r0.x, r1.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c4.z
+ mov oT0.xy, v1
+
+// approximately 36 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 82, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 16, 1,
+ 0, 0, 0, 2, 254, 255,
+ 6, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 9, 1, 0, 0, 148, 0,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 180, 0, 0, 0, 2, 0,
+ 41, 0, 192, 0, 166, 0,
+ 196, 0, 0, 0, 0, 0,
+ 0, 0, 212, 0, 0, 0,
+ 2, 0, 0, 0, 4, 0,
+ 2, 0, 224, 0, 0, 0,
+ 0, 0, 0, 0, 240, 0,
+ 0, 0, 2, 0, 14, 0,
+ 1, 0, 58, 0, 164, 0,
+ 0, 0, 0, 0, 0, 0,
+ 248, 0, 0, 0, 2, 0,
+ 12, 0, 1, 0, 50, 0,
+ 164, 0, 0, 0, 0, 0,
+ 0, 0, 255, 0, 0, 0,
+ 2, 0, 13, 0, 1, 0,
+ 54, 0, 164, 0, 0, 0,
+ 0, 0, 0, 0, 97, 109,
+ 98, 105, 101, 110, 116, 76,
+ 105, 103, 104, 116, 0, 171,
+ 171, 171, 1, 0, 3, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 98, 111, 110, 101, 77, 97,
+ 116, 114, 105, 99, 101, 115,
+ 0, 171, 171, 171, 3, 0,
+ 3, 0, 4, 0, 3, 0,
+ 64, 0, 0, 0, 0, 0,
+ 0, 0, 99, 111, 109, 98,
+ 105, 110, 101, 100, 77, 97,
+ 116, 0, 3, 0, 3, 0,
+ 4, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 111, 103, 68, 97, 116,
+ 97, 0, 109, 97, 116, 67,
+ 111, 108, 0, 115, 117, 114,
+ 102, 80, 114, 111, 112, 115,
+ 0, 118, 115, 95, 50, 95,
+ 48, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 171,
+ 81, 0, 0, 5, 4, 0,
+ 15, 160, 0, 0, 64, 64,
+ 0, 0, 0, 0, 0, 0,
+ 128, 63, 0, 0, 0, 0,
+ 31, 0, 0, 2, 0, 0,
+ 0, 128, 0, 0, 15, 144,
+ 31, 0, 0, 2, 5, 0,
+ 0, 128, 1, 0, 15, 144,
+ 31, 0, 0, 2, 10, 0,
+ 0, 128, 2, 0, 15, 144,
+ 31, 0, 0, 2, 1, 0,
+ 0, 128, 3, 0, 15, 144,
+ 31, 0, 0, 2, 2, 0,
+ 0, 128, 4, 0, 15, 144,
+ 1, 0, 0, 2, 0, 0,
+ 7, 128, 15, 0, 228, 160,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 13, 0, 0, 160, 2, 0,
+ 228, 144, 1, 0, 0, 2,
+ 0, 0, 8, 128, 2, 0,
+ 255, 144, 11, 0, 0, 3,
+ 0, 0, 15, 128, 0, 0,
+ 228, 128, 4, 0, 85, 160,
+ 10, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 4, 0, 170, 160, 5, 0,
+ 0, 3, 0, 0, 15, 208,
+ 0, 0, 228, 128, 12, 0,
+ 228, 160, 5, 0, 0, 3,
+ 0, 0, 15, 128, 4, 0,
+ 228, 144, 4, 0, 0, 160,
+ 46, 0, 0, 2, 0, 0,
+ 1, 176, 0, 0, 85, 128,
+ 9, 0, 0, 4, 1, 0,
+ 1, 128, 0, 0, 228, 144,
+ 41, 32, 228, 160, 0, 0,
+ 0, 176, 9, 0, 0, 4,
+ 1, 0, 2, 128, 0, 0,
+ 228, 144, 42, 32, 228, 160,
+ 0, 0, 0, 176, 9, 0,
+ 0, 4, 1, 0, 4, 128,
+ 0, 0, 228, 144, 43, 32,
+ 228, 160, 0, 0, 0, 176,
+ 5, 0, 0, 3, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 3, 0, 85, 144, 46, 0,
+ 0, 2, 0, 0, 1, 176,
+ 0, 0, 0, 128, 9, 0,
+ 0, 4, 2, 0, 1, 128,
+ 0, 0, 228, 144, 41, 32,
+ 228, 160, 0, 0, 0, 176,
+ 9, 0, 0, 4, 2, 0,
+ 2, 128, 0, 0, 228, 144,
+ 42, 32, 228, 160, 0, 0,
+ 0, 176, 9, 0, 0, 4,
+ 2, 0, 4, 128, 0, 0,
+ 228, 144, 43, 32, 228, 160,
+ 0, 0, 0, 176, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 2, 0, 228, 128, 3, 0,
+ 0, 144, 1, 0, 228, 128,
+ 46, 0, 0, 2, 0, 0,
+ 3, 176, 0, 0, 238, 128,
+ 9, 0, 0, 4, 0, 0,
+ 1, 128, 0, 0, 228, 144,
+ 41, 32, 228, 160, 0, 0,
+ 0, 176, 9, 0, 0, 4,
+ 0, 0, 2, 128, 0, 0,
+ 228, 144, 42, 32, 228, 160,
+ 0, 0, 0, 176, 9, 0,
+ 0, 4, 0, 0, 4, 128,
+ 0, 0, 228, 144, 43, 32,
+ 228, 160, 0, 0, 0, 176,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 0, 0, 228, 128,
+ 3, 0, 170, 144, 1, 0,
+ 228, 128, 9, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 85, 176, 9, 0,
+ 0, 4, 1, 0, 2, 128,
+ 0, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 85, 176,
+ 9, 0, 0, 4, 1, 0,
+ 4, 128, 0, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 85, 176, 4, 0, 0, 4,
+ 0, 0, 7, 128, 1, 0,
+ 228, 128, 3, 0, 255, 144,
+ 0, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 1, 0,
+ 228, 160, 4, 0, 0, 4,
+ 1, 0, 15, 128, 0, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 2, 0, 228, 160, 0, 0,
+ 170, 128, 1, 0, 228, 128,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 3, 0, 228, 160, 2, 0,
+ 0, 3, 1, 0, 1, 128,
+ 0, 0, 255, 128, 14, 0,
+ 85, 161, 1, 0, 0, 2,
+ 0, 0, 15, 192, 0, 0,
+ 228, 128, 5, 0, 0, 3,
+ 0, 0, 1, 128, 1, 0,
+ 0, 128, 14, 0, 170, 160,
+ 11, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 255, 160, 10, 0,
+ 0, 3, 0, 0, 4, 224,
+ 0, 0, 0, 128, 4, 0,
+ 170, 160, 1, 0, 0, 2,
+ 0, 0, 3, 224, 1, 0,
+ 228, 144, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/skin_amb_dir_VS.h b/src/d3d/shaders/skin_amb_dir_VS.h
new file mode 100644
index 0000000..7949749
--- /dev/null
+++ b/src/d3d/shaders/skin_amb_dir_VS.h
@@ -0,0 +1,503 @@
+#if 0
+//
+// Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+//
+// fxc /nologo /T vs_2_0 /DDIRECTIONALS /Fh skin_amb_dir_VS.h skin_VS.hlsl
+//
+//
+// Parameters:
+//
+// float4 ambientLight;
+// float4x3 boneMatrices[64];
+// float4x4 combinedMat;
+// int4 firstLight;
+// float4 fogData;
+//
+// struct
+// {
+// float4 color;
+// float4 position;
+// float4 direction;
+//
+// } lights[8];
+//
+// float4 matCol;
+// float3x3 normalMat;
+// int numDirLights;
+// float4 surfProps;
+//
+//
+// Registers:
+//
+// Name Reg Size
+// ------------ ----- ----
+// numDirLights i0 1
+// combinedMat c0 4
+// normalMat c8 3
+// matCol c12 1
+// surfProps c13 1
+// fogData c14 1
+// ambientLight c15 1
+// firstLight c16 1
+// lights c17 24
+// boneMatrices c41 192
+//
+
+ vs_2_0
+ def c4, 3, 0, 1, 0
+ dcl_position v0
+ dcl_normal v1
+ dcl_texcoord v2
+ dcl_color v3
+ dcl_blendweight v4
+ dcl_blendindices v5
+ mul r0, v5, c4.x
+ mova a0.w, r0.x
+ dp3 r1.z, v1, c43[a0.w]
+ mova a0.w, r0.x
+ dp3 r1.x, v1, c41[a0.w]
+ mova a0.w, r0.x
+ dp3 r1.y, v1, c42[a0.w]
+ mova a0.w, r0.y
+ dp3 r2.z, v1, c43[a0.w]
+ mova a0.w, r0.y
+ dp3 r2.x, v1, c41[a0.w]
+ mova a0.w, r0.y
+ dp3 r2.y, v1, c42[a0.w]
+ mul r2.xyz, r2, v4.y
+ mad r1.xyz, r1, v4.x, r2
+ mova a0.w, r0.z
+ dp3 r2.z, v1, c43[a0.w]
+ mova a0.w, r0.z
+ dp3 r2.x, v1, c41[a0.w]
+ mova a0.w, r0.z
+ dp3 r2.y, v1, c42[a0.w]
+ mad r1.xyz, r2, v4.z, r1
+ mova a0.w, r0.w
+ dp3 r2.z, v1, c43[a0.w]
+ mova a0.w, r0.w
+ dp3 r2.x, v1, c41[a0.w]
+ mova a0.w, r0.w
+ dp3 r2.y, v1, c42[a0.w]
+ mad r1.xyz, r2, v4.w, r1
+ mul r2.xyz, r1.y, c9
+ mad r1.xyw, c8.xyzz, r1.x, r2.xyzz
+ mad r1.xyz, c10, r1.z, r1.xyww
+ mov r2.x, c13.x
+ mad r2.xyz, c15, r2.x, v3
+ mov r3.xyz, r2
+ mov r1.w, c4.y
+ rep i0
+ add r2.w, r1.w, c16.x
+ mul r2.w, r2.w, c4.x
+ mova a0.w, r2.w
+ dp3 r4.x, r1, -c19[a0.w]
+ max r4.x, r4.x, c4.y
+ mova a0.w, r2.w
+ mul r4.xyz, r4.x, c17[a0.w]
+ mad r3.xyz, r4, c13.z, r3
+ add r1.w, r1.w, c4.z
+ endrep
+ mov r3.w, v3.w
+ max r1, r3, c4.y
+ min r1, r1, c4.z
+ mul oD0, r1, c12
+ mova a0.w, r0.y
+ dp4 r1.x, v0, c41[a0.w]
+ mova a0.w, r0.y
+ dp4 r1.y, v0, c42[a0.w]
+ mova a0.w, r0.y
+ dp4 r1.z, v0, c43[a0.w]
+ mul r1.xyz, r1, v4.y
+ mova a0.w, r0.x
+ dp4 r2.x, v0, c41[a0.w]
+ mova a0.w, r0.x
+ dp4 r2.y, v0, c42[a0.w]
+ mova a0.w, r0.x
+ dp4 r2.z, v0, c43[a0.w]
+ mad r1.xyz, r2, v4.x, r1
+ mova a0.w, r0.z
+ dp4 r2.x, v0, c41[a0.w]
+ mova a0.w, r0.z
+ dp4 r2.y, v0, c42[a0.w]
+ mova a0.w, r0.z
+ dp4 r2.z, v0, c43[a0.w]
+ mad r0.xyz, r2, v4.z, r1
+ mova a0.w, r0.w
+ dp4 r1.x, v0, c41[a0.w]
+ mova a0.w, r0.w
+ dp4 r1.y, v0, c42[a0.w]
+ mova a0.w, r0.w
+ dp4 r1.z, v0, c43[a0.w]
+ mad r0.xyz, r1, v4.w, r0
+ mul r1, r0.y, c1
+ mad r1, c0, r0.x, r1
+ mad r0, c2, r0.z, r1
+ add r0, r0, c3
+ mov oPos, r0
+ add r0.x, r0.w, -c14.y
+ mul r0.x, r0.x, c14.z
+ max r0.x, r0.x, c14.w
+ min oT0.z, r0.x, c4.z
+ mov oT0.xy, v2
+
+// approximately 92 instruction slots used
+#endif
+
+const BYTE g_vs20_main[] =
+{
+ 0, 2, 254, 255, 254, 255,
+ 147, 0, 67, 84, 65, 66,
+ 28, 0, 0, 0, 21, 2,
+ 0, 0, 0, 2, 254, 255,
+ 10, 0, 0, 0, 28, 0,
+ 0, 0, 0, 1, 0, 0,
+ 14, 2, 0, 0, 228, 0,
+ 0, 0, 2, 0, 15, 0,
+ 1, 0, 62, 0, 244, 0,
+ 0, 0, 0, 0, 0, 0,
+ 4, 1, 0, 0, 2, 0,
+ 41, 0, 192, 0, 166, 0,
+ 20, 1, 0, 0, 0, 0,
+ 0, 0, 36, 1, 0, 0,
+ 2, 0, 0, 0, 4, 0,
+ 2, 0, 48, 1, 0, 0,
+ 0, 0, 0, 0, 64, 1,
+ 0, 0, 2, 0, 16, 0,
+ 1, 0, 66, 0, 76, 1,
+ 0, 0, 0, 0, 0, 0,
+ 92, 1, 0, 0, 2, 0,
+ 14, 0, 1, 0, 58, 0,
+ 244, 0, 0, 0, 0, 0,
+ 0, 0, 100, 1, 0, 0,
+ 2, 0, 17, 0, 24, 0,
+ 70, 0, 176, 1, 0, 0,
+ 0, 0, 0, 0, 192, 1,
+ 0, 0, 2, 0, 12, 0,
+ 1, 0, 50, 0, 244, 0,
+ 0, 0, 0, 0, 0, 0,
+ 199, 1, 0, 0, 2, 0,
+ 8, 0, 3, 0, 34, 0,
+ 212, 1, 0, 0, 0, 0,
+ 0, 0, 228, 1, 0, 0,
+ 1, 0, 0, 0, 1, 0,
+ 2, 0, 244, 1, 0, 0,
+ 0, 0, 0, 0, 4, 2,
+ 0, 0, 2, 0, 13, 0,
+ 1, 0, 54, 0, 244, 0,
+ 0, 0, 0, 0, 0, 0,
+ 97, 109, 98, 105, 101, 110,
+ 116, 76, 105, 103, 104, 116,
+ 0, 171, 171, 171, 1, 0,
+ 3, 0, 1, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 98, 111, 110, 101,
+ 77, 97, 116, 114, 105, 99,
+ 101, 115, 0, 171, 171, 171,
+ 3, 0, 3, 0, 4, 0,
+ 3, 0, 64, 0, 0, 0,
+ 0, 0, 0, 0, 99, 111,
+ 109, 98, 105, 110, 101, 100,
+ 77, 97, 116, 0, 3, 0,
+ 3, 0, 4, 0, 4, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 102, 105, 114, 115,
+ 116, 76, 105, 103, 104, 116,
+ 0, 171, 1, 0, 2, 0,
+ 1, 0, 4, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 102, 111, 103, 68, 97, 116,
+ 97, 0, 108, 105, 103, 104,
+ 116, 115, 0, 99, 111, 108,
+ 111, 114, 0, 171, 171, 171,
+ 1, 0, 3, 0, 1, 0,
+ 4, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 112, 111,
+ 115, 105, 116, 105, 111, 110,
+ 0, 100, 105, 114, 101, 99,
+ 116, 105, 111, 110, 0, 171,
+ 107, 1, 0, 0, 116, 1,
+ 0, 0, 132, 1, 0, 0,
+ 116, 1, 0, 0, 141, 1,
+ 0, 0, 116, 1, 0, 0,
+ 5, 0, 0, 0, 1, 0,
+ 12, 0, 8, 0, 3, 0,
+ 152, 1, 0, 0, 109, 97,
+ 116, 67, 111, 108, 0, 110,
+ 111, 114, 109, 97, 108, 77,
+ 97, 116, 0, 171, 171, 171,
+ 3, 0, 3, 0, 3, 0,
+ 3, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 110, 117,
+ 109, 68, 105, 114, 76, 105,
+ 103, 104, 116, 115, 0, 171,
+ 171, 171, 0, 0, 2, 0,
+ 1, 0, 1, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 115, 117, 114, 102, 80, 114,
+ 111, 112, 115, 0, 118, 115,
+ 95, 50, 95, 48, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 57, 46,
+ 50, 57, 46, 57, 53, 50,
+ 46, 51, 49, 49, 49, 0,
+ 171, 171, 81, 0, 0, 5,
+ 4, 0, 15, 160, 0, 0,
+ 64, 64, 0, 0, 0, 0,
+ 0, 0, 128, 63, 0, 0,
+ 0, 0, 31, 0, 0, 2,
+ 0, 0, 0, 128, 0, 0,
+ 15, 144, 31, 0, 0, 2,
+ 3, 0, 0, 128, 1, 0,
+ 15, 144, 31, 0, 0, 2,
+ 5, 0, 0, 128, 2, 0,
+ 15, 144, 31, 0, 0, 2,
+ 10, 0, 0, 128, 3, 0,
+ 15, 144, 31, 0, 0, 2,
+ 1, 0, 0, 128, 4, 0,
+ 15, 144, 31, 0, 0, 2,
+ 2, 0, 0, 128, 5, 0,
+ 15, 144, 5, 0, 0, 3,
+ 0, 0, 15, 128, 5, 0,
+ 228, 144, 4, 0, 0, 160,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 0, 128,
+ 8, 0, 0, 4, 1, 0,
+ 4, 128, 1, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 255, 176, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 0, 128, 8, 0, 0, 4,
+ 1, 0, 1, 128, 1, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 255, 176, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 0, 128, 8, 0,
+ 0, 4, 1, 0, 2, 128,
+ 1, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 255, 176,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 85, 128,
+ 8, 0, 0, 4, 2, 0,
+ 4, 128, 1, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 255, 176, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 85, 128, 8, 0, 0, 4,
+ 2, 0, 1, 128, 1, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 255, 176, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 85, 128, 8, 0,
+ 0, 4, 2, 0, 2, 128,
+ 1, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 255, 176,
+ 5, 0, 0, 3, 2, 0,
+ 7, 128, 2, 0, 228, 128,
+ 4, 0, 85, 144, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 144, 2, 0, 228, 128,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 170, 128,
+ 8, 0, 0, 4, 2, 0,
+ 4, 128, 1, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 255, 176, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 170, 128, 8, 0, 0, 4,
+ 2, 0, 1, 128, 1, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 255, 176, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 170, 128, 8, 0,
+ 0, 4, 2, 0, 2, 128,
+ 1, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 255, 176,
+ 4, 0, 0, 4, 1, 0,
+ 7, 128, 2, 0, 228, 128,
+ 4, 0, 170, 144, 1, 0,
+ 228, 128, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 255, 128, 8, 0, 0, 4,
+ 2, 0, 4, 128, 1, 0,
+ 228, 144, 43, 32, 228, 160,
+ 0, 0, 255, 176, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 255, 128, 8, 0,
+ 0, 4, 2, 0, 1, 128,
+ 1, 0, 228, 144, 41, 32,
+ 228, 160, 0, 0, 255, 176,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 255, 128,
+ 8, 0, 0, 4, 2, 0,
+ 2, 128, 1, 0, 228, 144,
+ 42, 32, 228, 160, 0, 0,
+ 255, 176, 4, 0, 0, 4,
+ 1, 0, 7, 128, 2, 0,
+ 228, 128, 4, 0, 255, 144,
+ 1, 0, 228, 128, 5, 0,
+ 0, 3, 2, 0, 7, 128,
+ 1, 0, 85, 128, 9, 0,
+ 228, 160, 4, 0, 0, 4,
+ 1, 0, 11, 128, 8, 0,
+ 164, 160, 1, 0, 0, 128,
+ 2, 0, 164, 128, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 10, 0, 228, 160, 1, 0,
+ 170, 128, 1, 0, 244, 128,
+ 1, 0, 0, 2, 2, 0,
+ 1, 128, 13, 0, 0, 160,
+ 4, 0, 0, 4, 2, 0,
+ 7, 128, 15, 0, 228, 160,
+ 2, 0, 0, 128, 3, 0,
+ 228, 144, 1, 0, 0, 2,
+ 3, 0, 7, 128, 2, 0,
+ 228, 128, 1, 0, 0, 2,
+ 1, 0, 8, 128, 4, 0,
+ 85, 160, 38, 0, 0, 1,
+ 0, 0, 228, 240, 2, 0,
+ 0, 3, 2, 0, 8, 128,
+ 1, 0, 255, 128, 16, 0,
+ 0, 160, 5, 0, 0, 3,
+ 2, 0, 8, 128, 2, 0,
+ 255, 128, 4, 0, 0, 160,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 2, 0, 255, 128,
+ 8, 0, 0, 4, 4, 0,
+ 1, 128, 1, 0, 228, 128,
+ 19, 32, 228, 161, 0, 0,
+ 255, 176, 11, 0, 0, 3,
+ 4, 0, 1, 128, 4, 0,
+ 0, 128, 4, 0, 85, 160,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 2, 0, 255, 128,
+ 5, 0, 0, 4, 4, 0,
+ 7, 128, 4, 0, 0, 128,
+ 17, 32, 228, 160, 0, 0,
+ 255, 176, 4, 0, 0, 4,
+ 3, 0, 7, 128, 4, 0,
+ 228, 128, 13, 0, 170, 160,
+ 3, 0, 228, 128, 2, 0,
+ 0, 3, 1, 0, 8, 128,
+ 1, 0, 255, 128, 4, 0,
+ 170, 160, 39, 0, 0, 0,
+ 1, 0, 0, 2, 3, 0,
+ 8, 128, 3, 0, 255, 144,
+ 11, 0, 0, 3, 1, 0,
+ 15, 128, 3, 0, 228, 128,
+ 4, 0, 85, 160, 10, 0,
+ 0, 3, 1, 0, 15, 128,
+ 1, 0, 228, 128, 4, 0,
+ 170, 160, 5, 0, 0, 3,
+ 0, 0, 15, 208, 1, 0,
+ 228, 128, 12, 0, 228, 160,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 85, 128,
+ 9, 0, 0, 4, 1, 0,
+ 1, 128, 0, 0, 228, 144,
+ 41, 32, 228, 160, 0, 0,
+ 255, 176, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 85, 128, 9, 0, 0, 4,
+ 1, 0, 2, 128, 0, 0,
+ 228, 144, 42, 32, 228, 160,
+ 0, 0, 255, 176, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 85, 128, 9, 0,
+ 0, 4, 1, 0, 4, 128,
+ 0, 0, 228, 144, 43, 32,
+ 228, 160, 0, 0, 255, 176,
+ 5, 0, 0, 3, 1, 0,
+ 7, 128, 1, 0, 228, 128,
+ 4, 0, 85, 144, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 0, 128, 9, 0,
+ 0, 4, 2, 0, 1, 128,
+ 0, 0, 228, 144, 41, 32,
+ 228, 160, 0, 0, 255, 176,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 0, 128,
+ 9, 0, 0, 4, 2, 0,
+ 2, 128, 0, 0, 228, 144,
+ 42, 32, 228, 160, 0, 0,
+ 255, 176, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 0, 128, 9, 0, 0, 4,
+ 2, 0, 4, 128, 0, 0,
+ 228, 144, 43, 32, 228, 160,
+ 0, 0, 255, 176, 4, 0,
+ 0, 4, 1, 0, 7, 128,
+ 2, 0, 228, 128, 4, 0,
+ 0, 144, 1, 0, 228, 128,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 170, 128,
+ 9, 0, 0, 4, 2, 0,
+ 1, 128, 0, 0, 228, 144,
+ 41, 32, 228, 160, 0, 0,
+ 255, 176, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 170, 128, 9, 0, 0, 4,
+ 2, 0, 2, 128, 0, 0,
+ 228, 144, 42, 32, 228, 160,
+ 0, 0, 255, 176, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 170, 128, 9, 0,
+ 0, 4, 2, 0, 4, 128,
+ 0, 0, 228, 144, 43, 32,
+ 228, 160, 0, 0, 255, 176,
+ 4, 0, 0, 4, 0, 0,
+ 7, 128, 2, 0, 228, 128,
+ 4, 0, 170, 144, 1, 0,
+ 228, 128, 46, 0, 0, 2,
+ 0, 0, 8, 176, 0, 0,
+ 255, 128, 9, 0, 0, 4,
+ 1, 0, 1, 128, 0, 0,
+ 228, 144, 41, 32, 228, 160,
+ 0, 0, 255, 176, 46, 0,
+ 0, 2, 0, 0, 8, 176,
+ 0, 0, 255, 128, 9, 0,
+ 0, 4, 1, 0, 2, 128,
+ 0, 0, 228, 144, 42, 32,
+ 228, 160, 0, 0, 255, 176,
+ 46, 0, 0, 2, 0, 0,
+ 8, 176, 0, 0, 255, 128,
+ 9, 0, 0, 4, 1, 0,
+ 4, 128, 0, 0, 228, 144,
+ 43, 32, 228, 160, 0, 0,
+ 255, 176, 4, 0, 0, 4,
+ 0, 0, 7, 128, 1, 0,
+ 228, 128, 4, 0, 255, 144,
+ 0, 0, 228, 128, 5, 0,
+ 0, 3, 1, 0, 15, 128,
+ 0, 0, 85, 128, 1, 0,
+ 228, 160, 4, 0, 0, 4,
+ 1, 0, 15, 128, 0, 0,
+ 228, 160, 0, 0, 0, 128,
+ 1, 0, 228, 128, 4, 0,
+ 0, 4, 0, 0, 15, 128,
+ 2, 0, 228, 160, 0, 0,
+ 170, 128, 1, 0, 228, 128,
+ 2, 0, 0, 3, 0, 0,
+ 15, 128, 0, 0, 228, 128,
+ 3, 0, 228, 160, 1, 0,
+ 0, 2, 0, 0, 15, 192,
+ 0, 0, 228, 128, 2, 0,
+ 0, 3, 0, 0, 1, 128,
+ 0, 0, 255, 128, 14, 0,
+ 85, 161, 5, 0, 0, 3,
+ 0, 0, 1, 128, 0, 0,
+ 0, 128, 14, 0, 170, 160,
+ 11, 0, 0, 3, 0, 0,
+ 1, 128, 0, 0, 0, 128,
+ 14, 0, 255, 160, 10, 0,
+ 0, 3, 0, 0, 4, 224,
+ 0, 0, 0, 128, 4, 0,
+ 170, 160, 1, 0, 0, 2,
+ 0, 0, 3, 224, 2, 0,
+ 228, 144, 255, 255, 0, 0
+};
diff --git a/src/d3d/shaders/standardConstants.h b/src/d3d/shaders/standardConstants.h
new file mode 100644
index 0000000..088df7d
--- /dev/null
+++ b/src/d3d/shaders/standardConstants.h
@@ -0,0 +1,28 @@
+float4x4 combinedMat : register(c0);
+float4x4 worldMat : register(c4);
+float3x3 normalMat : register(c8);
+float4 matCol : register(c12);
+float4 surfProps : register(c13);
+float4 fogData : register(c14);
+float4 ambientLight : register(c15);
+
+#define surfAmbient (surfProps.x)
+#define surfSpecular (surfProps.y)
+#define surfDiffuse (surfProps.z)
+
+#define fogStart (fogData.x)
+#define fogEnd (fogData.y)
+#define fogRange (fogData.z)
+#define fogDisable (fogData.w)
+
+#include "lighting.h"
+
+int numDirLights : register(i0);
+int numPointLights : register(i1);
+int numSpotLights : register(i2);
+int4 firstLight : register(c16);
+Light lights[8] : register(c17);
+
+#define firstDirLight (firstLight.x)
+#define firstPointLight (firstLight.y)
+#define firstSpotLight (firstLight.z)
diff --git a/src/d3d/xbox.cpp b/src/d3d/xbox.cpp
new file mode 100644
index 0000000..62fab0c
--- /dev/null
+++ b/src/d3d/xbox.cpp
@@ -0,0 +1,1005 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwxbox.h"
+
+#include "rwxboximpl.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace xbox {
+
+static void*
+driverOpen(void *o, int32, int32)
+{
+ engine->driver[PLATFORM_XBOX]->defaultPipeline = makeDefaultPipeline();
+
+ engine->driver[PLATFORM_XBOX]->rasterNativeOffset = nativeRasterOffset;
+ engine->driver[PLATFORM_XBOX]->rasterCreate = rasterCreate;
+ engine->driver[PLATFORM_XBOX]->rasterLock = rasterLock;
+ engine->driver[PLATFORM_XBOX]->rasterUnlock = rasterUnlock;
+ engine->driver[PLATFORM_XBOX]->rasterNumLevels = rasterNumLevels;
+ engine->driver[PLATFORM_XBOX]->rasterToImage = rasterToImage;
+
+ return o;
+}
+
+static void*
+driverClose(void *o, int32, int32)
+{
+ return o;
+}
+
+void
+registerPlatformPlugins(void)
+{
+ Driver::registerPlugin(PLATFORM_XBOX, 0, PLATFORM_XBOX,
+ driverOpen, driverClose);
+ registerNativeRaster();
+}
+
+void*
+destroyNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_XBOX)
+ return object;
+ InstanceDataHeader *header =
+ (InstanceDataHeader*)geometry->instData;
+ geometry->instData = nil;
+ rwFree(header->vertexBuffer);
+ rwFree(header->begin);
+ rwFree(header->data);
+ rwFree(header);
+ return object;
+}
+
+Stream*
+readNativeData(Stream *stream, int32, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ uint32 vers;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, &vers)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_XBOX){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ if(vers < 0x35000){
+ RWERROR((ERR_VERSION, vers));
+ return nil;
+ }
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geometry->instData = header;
+ header->platform = PLATFORM_XBOX;
+
+ int32 size = stream->readI32();
+ // The 0x18 byte are the resentryheader.
+ // We don't have it but it's used for alignment.
+ header->data = rwNewT(uint8, size + 0x18, MEMDUR_EVENT | ID_GEOMETRY);
+ uint8 *p = header->data+0x18+4;
+ stream->read8(p, size-4);
+
+ header->size = size;
+ header->serialNumber = *(uint16*)p; p += 2;
+ header->numMeshes = *(uint16*)p; p += 2;
+ header->primType = *(uint32*)p; p += 4;
+ header->numVertices = *(uint32*)p; p += 4;
+ header->stride = *(uint32*)p; p += 4;
+ // RxXboxVertexFormat in 3.3 here
+ p += 4; // skip vertexBuffer pointer
+ header->vertexAlpha = *(bool32*)p; p += 4;
+ p += 8; // skip begin, end pointers
+
+ InstanceData *inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+ header->begin = inst;
+ for(int i = 0; i < header->numMeshes; i++){
+ inst->minVert = *(uint32*)p; p += 4;
+ inst->numVertices = *(int32*)p; p += 4;
+ inst->numIndices = *(int32*)p; p += 4;
+ inst->indexBuffer = header->data + *(uint32*)p; p += 4;
+ p += 8; // skip material and vertexShader
+ inst->vertexShader = 0;
+ // pixelShader in 3.3 here
+ inst++;
+ }
+ header->end = inst;
+
+ header->vertexBuffer = rwNewT(uint8, header->stride*header->numVertices, MEMDUR_EVENT | ID_GEOMETRY);
+ stream->read8(header->vertexBuffer, header->stride*header->numVertices);
+ return stream;
+}
+
+Stream*
+writeNativeData(Stream *stream, int32 len, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_XBOX)
+ return stream;
+ stream->writeU32(PLATFORM_XBOX);
+ assert(rw::version >= 0x35000 && "can't write native Xbox data < 0x35000");
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+
+ // we just fill header->data and write that
+ uint8 *p = header->data+0x18;
+ *(int32*)p = header->size; p += 4;
+ *(uint16*)p = header->serialNumber; p += 2;
+ *(uint16*)p = header->numMeshes; p += 2;
+ *(uint32*)p = header->primType; p += 4;
+ *(uint32*)p = header->numVertices; p += 4;
+ *(uint32*)p = header->stride; p += 4;
+ // RxXboxVertexFormat in 3.3 here
+ p += 4; // skip vertexBuffer pointer
+ *(bool32*)p = header->vertexAlpha; p += 4;
+ p += 8; // skip begin, end pointers
+
+ InstanceData *inst = header->begin;
+ for(int i = 0; i < header->numMeshes; i++){
+ *(uint32*)p = inst->minVert; p += 4;
+ *(int32*)p = inst->numVertices; p += 4;
+ *(int32*)p = inst->numIndices; p += 4;
+ *(uint32*)p = (uint8*)inst->indexBuffer - header->data; p += 4;
+ p += 8; // skip material and vertexShader
+ // pixelShader in 3.3 here
+ inst++;
+ }
+
+ stream->write8(header->data+0x18, header->size);
+ stream->write8(header->vertexBuffer, header->stride*header->numVertices);
+ return stream;
+}
+
+int32
+getSizeNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_XBOX)
+ return 0;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ return 12 + 4 + header->size + header->stride*header->numVertices;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+enum {
+ D3DPT_TRIANGLELIST = 5,
+ D3DPT_TRIANGLESTRIP = 6
+};
+
+static void
+instance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // TODO: allow for REINSTANCE (or not, xbox can't render)
+ if(geo->instData)
+ return;
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ MeshHeader *meshh = geo->meshHeader;
+ geo->instData = header;
+ header->platform = PLATFORM_XBOX;
+
+ header->size = 0x24 + meshh->numMeshes*0x18 + 0x10;
+ Mesh *mesh = meshh->getMeshes();
+ for(uint32 i = 0; i < meshh->numMeshes; i++)
+ header->size += (mesh++->numIndices*2 + 0xF) & ~0xF;
+ // The 0x18 byte are the resentryheader.
+ // We don't have it but it's used for alignment.
+ header->data = rwNewT(uint8, header->size + 0x18, MEMDUR_EVENT | ID_GEOMETRY);
+ header->serialNumber = meshh->serialNum;
+ header->numMeshes = meshh->numMeshes;
+ header->primType = meshh->flags == MeshHeader::TRISTRIP ?
+ D3DPT_TRIANGLESTRIP : D3DPT_TRIANGLELIST;
+ header->numVertices = geo->numVertices;
+ header->vertexAlpha = 0;
+ // set by the instanceCB
+ header->stride = 0;
+ header->vertexBuffer = nil;
+
+ InstanceData *inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+ header->begin = inst;
+ mesh = meshh->getMeshes();
+ uint8 *indexbuf = (uint8*)header->data + ((0x18 + 0x24 + header->numMeshes*0x18 + 0xF)&~0xF);
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ findMinVertAndNumVertices(mesh->indices, mesh->numIndices,
+ &inst->minVert, &inst->numVertices);
+ inst->numIndices = mesh->numIndices;
+ inst->indexBuffer = indexbuf;
+ memcpy(inst->indexBuffer, mesh->indices, inst->numIndices*sizeof(uint16));
+ indexbuf += (inst->numIndices*2 + 0xF) & ~0xF;
+ inst->material = mesh->material;
+ inst->vertexShader = 0; // TODO?
+ mesh++;
+ inst++;
+ }
+ header->end = inst;
+
+ pipe->instanceCB(geo, header);
+}
+
+static void
+uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ if((geo->flags & Geometry::NATIVE) == 0)
+ return;
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_XBOX);
+
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ InstanceData *inst = header->begin;
+ Mesh *mesh = geo->meshHeader->getMeshes();
+ // For some reason numIndices in mesh and instance data are not always equal
+ // And primitive isn't always correct either. Maybe some internal conversion...
+ geo->meshHeader->totalIndices = 0;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ mesh[i].numIndices = inst[i].numIndices;
+ geo->meshHeader->totalIndices += mesh[i].numIndices;
+ }
+ geo->meshHeader->flags = header->primType == D3DPT_TRIANGLESTRIP ?
+ MeshHeader::TRISTRIP : 0;
+
+ geo->numTriangles = geo->meshHeader->guessNumTriangles();
+ geo->allocateData();
+ geo->allocateMeshes(geo->meshHeader->numMeshes, geo->meshHeader->totalIndices, 0);
+
+ mesh = geo->meshHeader->getMeshes();
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ uint16 *indices = (uint16*)inst->indexBuffer;
+ memcpy(mesh->indices, indices, inst->numIndices*2);
+ mesh++;
+ inst++;
+ }
+
+ pipe->uninstanceCB(geo, header);
+ geo->generateTriangles();
+ geo->flags &= ~Geometry::NATIVE;
+ destroyNativeData(geo, 0, 0);
+}
+
+void
+ObjPipeline::init(void)
+{
+ this->rw::ObjPipeline::init(PLATFORM_XBOX);
+ this->impl.instance = xbox::instance;
+ this->impl.uninstance = xbox::uninstance;
+ this->instanceCB = nil;
+ this->uninstanceCB = nil;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+int v3dFormatMap[] = {
+ -1, VERT_BYTE3, VERT_SHORT3, VERT_NORMSHORT3, VERT_COMPNORM, VERT_FLOAT3
+};
+
+int v2dFormatMap[] = {
+ -1, VERT_BYTE2, VERT_SHORT2, VERT_NORMSHORT2, VERT_COMPNORM, VERT_FLOAT2
+};
+
+void
+defaultInstanceCB(Geometry *geo, InstanceDataHeader *header)
+{
+ uint32 *vertexFmt = getVertexFmt(geo);
+ if(*vertexFmt == 0)
+ *vertexFmt = makeVertexFmt(geo->flags, geo->numTexCoordSets);
+ header->stride = getVertexFmtStride(*vertexFmt);
+ header->vertexBuffer = rwNewT(uint8, header->stride*header->numVertices, MEMDUR_EVENT | ID_GEOMETRY);
+ uint8 *dst = (uint8*)header->vertexBuffer;
+
+ uint32 fmt = *vertexFmt;
+ uint32 sel = fmt & 0xF;
+ instV3d(v3dFormatMap[sel], dst, geo->morphTargets[0].vertices,
+ header->numVertices, header->stride);
+ dst += sel == 4 ? 4 : 3*vertexFormatSizes[sel];
+
+ sel = (fmt >> 4) & 0xF;
+ if(sel){
+ instV3d(v3dFormatMap[sel], dst, geo->morphTargets[0].normals,
+ header->numVertices, header->stride);
+ dst += sel == 4 ? 4 : 3*vertexFormatSizes[sel];
+ }
+
+ if(fmt & 0x1000000){
+ header->vertexAlpha = instColor(VERT_ARGB, dst, geo->colors,
+ header->numVertices, header->stride);
+ dst += 4;
+ }
+
+ for(int i = 0; i < 4; i++){
+ sel = (fmt >> (i*4 + 8)) & 0xF;
+ if(sel == 0)
+ break;
+ instTexCoords(v2dFormatMap[sel], dst, geo->texCoords[i],
+ header->numVertices, header->stride);
+ dst += sel == 4 ? 4 : 2*vertexFormatSizes[sel];
+ }
+
+ if(fmt & 0xE000000)
+ assert(0 && "can't instance tangents or whatever it is");
+}
+
+void
+defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header)
+{
+ uint32 *vertexFmt = getVertexFmt(geo);
+ uint32 fmt = *vertexFmt;
+ assert(fmt != 0);
+ uint8 *src = (uint8*)header->vertexBuffer;
+
+ uint32 sel = fmt & 0xF;
+ uninstV3d(v3dFormatMap[sel], geo->morphTargets[0].vertices, src,
+ header->numVertices, header->stride);
+ src += sel == 4 ? 4 : 3*vertexFormatSizes[sel];
+
+ sel = (fmt >> 4) & 0xF;
+ if(sel){
+ uninstV3d(v3dFormatMap[sel], geo->morphTargets[0].normals, src,
+ header->numVertices, header->stride);
+ src += sel == 4 ? 4 : 3*vertexFormatSizes[sel];
+ }
+
+ if(fmt & 0x1000000){
+ uninstColor(VERT_ARGB, geo->colors, src,
+ header->numVertices, header->stride);
+ src += 4;
+ }
+
+ for(int i = 0; i < 4; i++){
+ sel = (fmt >> (i*4 + 8)) & 0xF;
+ if(sel == 0)
+ break;
+ uninstTexCoords(v2dFormatMap[sel], geo->texCoords[i], src,
+ header->numVertices, header->stride);
+ src += sel == 4 ? 4 : 2*vertexFormatSizes[sel];
+ }
+}
+
+ObjPipeline*
+makeDefaultPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ return pipe;
+}
+
+// Native Texture and Raster
+
+int32 nativeRasterOffset;
+
+static uint32
+calculateTextureSize(uint32 width, uint32 height, uint32 depth, uint32 format)
+{
+#define D3DFMT_W11V11U10 65
+ switch(format){
+ default:
+ case D3DFMT_UNKNOWN:
+ return 0;
+ case D3DFMT_A8:
+ case D3DFMT_P8:
+ case D3DFMT_L8:
+ case D3DFMT_AL8:
+ case D3DFMT_LIN_A8:
+ case D3DFMT_LIN_AL8:
+ case D3DFMT_LIN_L8:
+ return width * height * depth;
+ case D3DFMT_R5G6B5:
+ case D3DFMT_R6G5B5:
+ case D3DFMT_X1R5G5B5:
+ case D3DFMT_A1R5G5B5:
+ case D3DFMT_A4R4G4B4:
+ case D3DFMT_R4G4B4A4:
+ case D3DFMT_R5G5B5A1:
+ case D3DFMT_R8B8:
+ case D3DFMT_G8B8:
+ case D3DFMT_A8L8:
+ case D3DFMT_L16:
+ //case D3DFMT_V8U8:
+ //case D3DFMT_L6V5U5:
+ case D3DFMT_D16_LOCKABLE:
+ //case D3DFMT_D16:
+ case D3DFMT_F16:
+ case D3DFMT_YUY2:
+ case D3DFMT_UYVY:
+ case D3DFMT_LIN_A1R5G5B5:
+ case D3DFMT_LIN_A4R4G4B4:
+ case D3DFMT_LIN_G8B8:
+ case D3DFMT_LIN_R4G4B4A4:
+ case D3DFMT_LIN_R5G5B5A1:
+ case D3DFMT_LIN_R5G6B5:
+ case D3DFMT_LIN_R6G5B5:
+ case D3DFMT_LIN_R8B8:
+ case D3DFMT_LIN_X1R5G5B5:
+ case D3DFMT_LIN_A8L8:
+ case D3DFMT_LIN_L16:
+ //case D3DFMT_LIN_V8U8:
+ //case D3DFMT_LIN_L6V5U5:
+ case D3DFMT_LIN_D16:
+ case D3DFMT_LIN_F16:
+ return width * 2 * height * depth;
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_X8R8G8B8:
+ case D3DFMT_A8B8G8R8:
+ case D3DFMT_B8G8R8A8:
+ case D3DFMT_R8G8B8A8:
+ //case D3DFMT_X8L8V8U8:
+ //case D3DFMT_Q8W8V8U8:
+ case D3DFMT_V16U16:
+ case D3DFMT_D24S8:
+ case D3DFMT_F24S8:
+ case D3DFMT_LIN_A8B8G8R8:
+ case D3DFMT_LIN_A8R8G8B8:
+ case D3DFMT_LIN_B8G8R8A8:
+ case D3DFMT_LIN_R8G8B8A8:
+ case D3DFMT_LIN_X8R8G8B8:
+ case D3DFMT_LIN_V16U16:
+ //case D3DFMT_LIN_X8L8V8U8:
+ //case D3DFMT_LIN_Q8W8V8U8:
+ case D3DFMT_LIN_D24S8:
+ case D3DFMT_LIN_F24S8:
+ return width * 4 * height * depth;
+ case D3DFMT_DXT1:
+ assert(depth <= 1);
+ return ((width + 3) >> 2) * ((height + 3) >> 2) * 8;
+ //case D3DFMT_DXT2:
+ case D3DFMT_DXT3:
+ //case D3DFMT_DXT4:
+ case D3DFMT_DXT5:
+ assert(depth <= 1);
+ return ((width + 3) >> 2) * ((height + 3) >> 2) * 16;
+ }
+}
+
+static void*
+createTexture(int32 width, int32 height, int32 numlevels, uint32 format)
+{
+ int32 w = width;
+ int32 h = height;
+ int32 size = 0;
+ for(int32 i = 0; i < numlevels; i++){
+ size += calculateTextureSize(w, h, 1, format);
+ w /= 2;
+ if(w == 0) w = 1;
+ h /= 2;
+ if(h == 0) h = 1;
+ }
+ size = (size+3)&~3;
+ uint8 *data = (uint8*)rwNew(sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1)+size,
+ MEMDUR_EVENT | ID_DRIVER);
+ RasterLevels *levels = (RasterLevels*)data;
+ data += sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(numlevels-1);
+ levels->numlevels = numlevels;
+ levels->format = format;
+ w = width;
+ h = height;
+ for(int32 i = 0; i < numlevels; i++){
+ levels->levels[i].width = w;
+ levels->levels[i].height = h;
+ levels->levels[i].data = data;
+ levels->levels[i].size = calculateTextureSize(w, h, 1, format);
+ data += levels->levels[i].size;
+ w /= 2;
+ if(w == 0) w = 1;
+ h /= 2;
+ if(h == 0) h = 1;
+ }
+ return levels;
+}
+
+struct RasterFormatInfo
+{
+ uint32 d3dformat;
+ int32 depth;
+ bool32 hasAlpha;
+ uint32 rwFormat;
+};
+
+// indexed directly by RW format
+static RasterFormatInfo formatInfoRW[16] = {
+ { 0, 0, 0, 0},
+ { D3DFMT_A1R5G5B5, 16, 1, Raster::C1555 },
+ { D3DFMT_R5G6B5, 16, 0, Raster::C565 },
+ { D3DFMT_A4R4G4B4, 16, 1, Raster::C4444 },
+ { D3DFMT_L8, 8, 0, Raster::LUM8 },
+ { D3DFMT_A8R8G8B8, 32, 1, Raster::C8888 },
+ { D3DFMT_X8R8G8B8, 32, 0, Raster::C888 },
+ { D3DFMT_UNKNOWN, 16, 0, Raster::D16 },
+ { D3DFMT_UNKNOWN, 32, 0, Raster::D24 },
+ { D3DFMT_UNKNOWN, 32, 0, Raster::D32 },
+ { D3DFMT_X1R5G5B5, 16, 0, Raster::C555 }
+};
+
+static void
+rasterSetFormat(Raster *raster)
+{
+ assert(raster->format != 0); // no default yet
+
+ XboxRaster *natras = GETXBOXRASTEREXT(raster);
+ if(raster->format & (Raster::PAL4 | Raster::PAL8)){
+ natras->format = D3DFMT_P8;
+ raster->depth = 8;
+ }else{
+ natras->format = formatInfoRW[(raster->format >> 8) & 0xF].d3dformat;
+ raster->depth = formatInfoRW[(raster->format >> 8) & 0xF].depth;
+ }
+ natras->bpp = raster->depth/8;
+ natras->hasAlpha = formatInfoRW[(raster->format >> 8) & 0xF].hasAlpha;
+ raster->stride = raster->width&natras->bpp;
+}
+
+static Raster*
+rasterCreateTexture(Raster *raster)
+{
+ XboxRaster *natras = GETXBOXRASTEREXT(raster);
+ int32 levels;
+
+ if(natras->format == D3DFMT_P8)
+ natras->palette = (uint8*)rwNew(4*256, MEMDUR_EVENT | ID_DRIVER);
+ levels = Raster::calculateNumLevels(raster->width, raster->height);
+ assert(natras->texture == nil);
+ natras->texture = createTexture(raster->width, raster->height,
+ raster->format & Raster::MIPMAP ? levels : 1,
+ natras->format);
+ if(natras->texture == nil){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+ return raster;
+}
+
+Raster*
+rasterCreate(Raster *raster)
+{
+ rasterSetFormat(raster);
+
+ Raster *ret = raster;
+
+ // Dummy to use as subraster
+ if(raster->width == 0 || raster->height == 0){
+ raster->flags |= Raster::DONTALLOCATE;
+ raster->stride = 0;
+ goto ret;
+ }
+ if(raster->flags & Raster::DONTALLOCATE)
+ goto ret;
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ ret = rasterCreateTexture(raster);
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ret:
+ raster->originalWidth = raster->width;
+ raster->originalHeight = raster->height;
+ raster->originalStride = raster->stride;
+ raster->originalPixels = raster->pixels;
+ return ret;
+}
+
+uint8*
+rasterLock(Raster *raster, int32 level, int32 lockMode)
+{
+ XboxRaster *natras = GETXBOXRASTEREXT(raster);
+
+ // check if already locked
+ if(raster->privateFlags & (Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE))
+ return nil;
+
+ RasterLevels *levels = (RasterLevels*)natras->texture;
+ raster->pixels = levels->levels[level].data;
+ raster->width = levels->levels[level].width;
+ raster->height = levels->levels[level].height;
+ raster->stride = raster->width*natras->bpp;
+
+ if(lockMode & Raster::LOCKREAD) raster->privateFlags |= Raster::PRIVATELOCK_READ;
+ if(lockMode & Raster::LOCKWRITE) raster->privateFlags |= Raster::PRIVATELOCK_WRITE;
+
+ return raster->pixels;
+}
+
+void
+rasterUnlock(Raster *raster, int32 level)
+{
+ raster->width = raster->originalWidth;
+ raster->height = raster->originalHeight;
+ raster->stride = raster->originalStride;
+ raster->pixels = raster->originalPixels;
+
+ raster->privateFlags &= ~(Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE);
+}
+
+int32
+rasterNumLevels(Raster *raster)
+{
+ XboxRaster *natras = GETXBOXRASTEREXT(raster);
+ RasterLevels *levels = (RasterLevels*)natras->texture;
+ return levels->numlevels;
+}
+
+static void
+unswizzle(uint8 *dst, uint8 *src, int32 w, int32 h, int32 bpp)
+{
+ uint32 maskU = 0;
+ uint32 maskV = 0;
+ int32 i = 1;
+ int32 j = 1;
+ int32 c;
+ do{
+ c = 0;
+ if(i < w){
+ maskU |= j;
+ j <<= 1;
+ c = j;
+ }
+ if(i < h){
+ maskV |= j;
+ j <<= 1;
+ c = j;
+ }
+ i <<= 1;
+ }while(c);
+ int32 x, y, u, v;
+ v = 0;
+ for(y = 0; y < h; y++){
+ u = 0;
+ for(x = 0; x < w; x++){
+ memcpy(&dst[(y*w + x)*bpp], &src[(u|v)*bpp], bpp);
+ u = (u - maskU) & maskU;
+ }
+ v = (v - maskV) & maskV;
+ }
+}
+
+Image*
+rasterToImage(Raster *raster)
+{
+ int32 depth;
+ Image *image;
+
+ bool unlock = false;
+ if(raster->pixels == nil){
+ raster->lock(0, Raster::LOCKREAD);
+ unlock = true;
+ }
+
+ XboxRaster *natras = GETXBOXRASTEREXT(raster);
+ if(natras->customFormat){
+ int w = raster->width;
+ int h = raster->height;
+ // pixels are in the upper right corner
+ if(w < 4) w = 4;
+ if(h < 4) h = 4;
+ image = Image::create(w, h, 32);
+ image->allocate();
+ uint8 *pix = raster->pixels;
+ switch(natras->format){
+ case D3DFMT_DXT1:
+ image->setPixelsDXT(1, pix);
+ // TODO: is this correct?
+ if(!natras->hasAlpha)
+ image->removeMask();
+ break;
+ case D3DFMT_DXT3:
+ image->setPixelsDXT(3, pix);
+ break;
+ case D3DFMT_DXT5:
+ image->setPixelsDXT(5, pix);
+ break;
+ default:
+ assert(0 && "unknown format");
+ image->destroy();
+ if(unlock)
+ raster->unlock(0);
+ return nil;
+ }
+ // fix it up again
+ image->width = raster->width;
+ image->height = raster->height;
+
+ if(unlock)
+ raster->unlock(0);
+ return image;
+ }
+
+ switch(raster->format & 0xF00){
+ case Raster::C1555:
+ depth = 16;
+ break;
+ case Raster::C8888:
+ depth = 32;
+ break;
+ case Raster::C888:
+ depth = 24;
+ break;
+ case Raster::C555:
+ depth = 16;
+ break;
+
+ default:
+ case Raster::C565:
+ case Raster::C4444:
+ case Raster::LUM8:
+ assert(0 && "unsupported raster format");
+ }
+ int32 pallength = 0;
+ if((raster->format & Raster::PAL4) == Raster::PAL4){
+ depth = 4;
+ pallength = 16;
+ }else if((raster->format & Raster::PAL8) == Raster::PAL8){
+ depth = 8;
+ pallength = 256;
+ }
+
+ image = Image::create(raster->width, raster->height, depth);
+ image->allocate();
+
+ if(pallength){
+ uint8 *out = image->palette;
+ uint8 *in = (uint8*)natras->palette;
+ // bytes are BGRA unlike regular d3d!
+ for(int32 i = 0; i < pallength; i++){
+ conv_BGRA8888_from_RGBA8888(out, in);
+ in += 4;
+ out += 4;
+ }
+ }
+
+ uint8 *imgpixels = image->pixels;
+ uint8 *pixels = raster->pixels;
+
+ // NB:
+ assert(image->bpp == (int)natras->bpp);
+ assert(image->stride == raster->stride);
+ unswizzle(imgpixels, pixels, image->width, image->height, image->bpp);
+ // Fix RGB order
+ uint8 tmp;
+ if(depth > 8)
+ for(int32 y = 0; y < image->height; y++){
+ uint8 *imgrow = imgpixels;
+ // uint8 *rasrow = pixels;
+ for(int32 x = 0; x < image->width; x++){
+ switch(raster->format & 0xF00){
+ case Raster::C8888:
+ case Raster::C888:
+ tmp = imgrow[0];
+ imgrow[0] = imgrow[2];
+ imgrow[2] = tmp;
+ imgrow += image->bpp;
+ break;
+ }
+ }
+ imgpixels += image->stride;
+ // pixels += raster->stride;
+ }
+ image->compressPalette();
+
+ if(unlock)
+ raster->unlock(0);
+
+ return image;
+}
+
+int32
+getLevelSize(Raster *raster, int32 level)
+{
+ XboxRaster *ras = GETXBOXRASTEREXT(raster);
+ RasterLevels *levels = (RasterLevels*)ras->texture;
+ return levels->levels[level].size;
+}
+
+static void*
+createNativeRaster(void *object, int32 offset, int32)
+{
+ XboxRaster *raster = PLUGINOFFSET(XboxRaster, object, offset);
+ raster->texture = nil;
+ raster->palette = nil;
+ raster->format = 0;
+ raster->hasAlpha = 0;
+ raster->customFormat = 0;
+ raster->unknownFlag = 0;
+ return object;
+}
+
+static void*
+destroyNativeRaster(void *object, int32, int32)
+{
+ // TODO:
+ return object;
+}
+
+static void*
+copyNativeRaster(void *dst, void *, int32 offset, int32)
+{
+ XboxRaster *raster = PLUGINOFFSET(XboxRaster, dst, offset);
+ raster->texture = nil;
+ raster->palette = nil;
+ raster->format = 0;
+ raster->hasAlpha = 0;
+ raster->unknownFlag = 0;
+ return dst;
+}
+
+void
+registerNativeRaster(void)
+{
+ nativeRasterOffset = Raster::registerPlugin(sizeof(XboxRaster),
+ ID_RASTERXBOX,
+ createNativeRaster,
+ destroyNativeRaster,
+ copyNativeRaster);
+}
+
+Texture*
+readNativeTexture(Stream *stream)
+{
+ uint32 vers, platform;
+ if(!findChunk(stream, ID_STRUCT, nil, &vers)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_XBOX){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ if(vers < 0x34001){
+ RWERROR((ERR_VERSION, vers));
+ return nil;
+ }
+ Texture *tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+
+
+ // Texture
+ tex->filterAddressing = stream->readU32();
+ stream->read8(tex->name, 32);
+ stream->read8(tex->mask, 32);
+
+//if(strcmp(tex->name, "bluallu") == 0)
+//__debugbreak();
+
+ // Raster
+ int32 format = stream->readI32();
+ bool32 hasAlpha = stream->readI16();
+ bool32 unknownFlag = stream->readI16();
+ int32 width = stream->readU16();
+ int32 height = stream->readU16();
+ int32 depth = stream->readU8();
+ int32 numLevels = stream->readU8();
+ int32 type = stream->readU8();
+ int32 compression = stream->readU8();
+ int32 totalSize = stream->readI32();
+
+ // isn't this the cube map flag?
+ assert(unknownFlag == 0);
+ Raster *raster;
+ if(compression){
+ raster = Raster::create(width, height, depth, format | type | Raster::DONTALLOCATE, PLATFORM_XBOX);
+ XboxRaster *ras = GETXBOXRASTEREXT(raster);
+ ras->format = compression;
+ ras->hasAlpha = hasAlpha;
+ ras->texture = createTexture(raster->width, raster->height,
+ raster->format & Raster::MIPMAP ? numLevels : 1,
+ ras->format);
+ ras->customFormat = 1;
+ raster->flags &= ~Raster::DONTALLOCATE;
+ }else
+ raster = Raster::create(width, height, depth, format | type, PLATFORM_XBOX);
+ XboxRaster *ras = GETXBOXRASTEREXT(raster);
+ tex->raster = raster;
+
+ if(raster->format & Raster::PAL4)
+ stream->read8(ras->palette, 4*32);
+ else if(raster->format & Raster::PAL8)
+ stream->read8(ras->palette, 4*256);
+
+ // exploit the fact that mipmaps are allocated consecutively
+ uint8 *data = raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ stream->read8(data, totalSize);
+ raster->unlock(0);
+
+ return tex;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+ int32 chunksize = getSizeNativeTexture(tex);
+ writeChunkHeader(stream, ID_STRUCT, chunksize-12);
+ stream->writeU32(PLATFORM_XBOX);
+
+ // Texture
+ stream->writeU32(tex->filterAddressing);
+ stream->write8(tex->name, 32);
+ stream->write8(tex->mask, 32);
+
+ // Raster
+ Raster *raster = tex->raster;
+ XboxRaster *ras = GETXBOXRASTEREXT(raster);
+ int32 numLevels = raster->getNumLevels();
+ stream->writeI32(raster->format);
+ stream->writeI16(ras->hasAlpha);
+ stream->writeI16(ras->unknownFlag);
+ stream->writeU16(raster->width);
+ stream->writeU16(raster->height);
+ stream->writeU8(raster->depth);
+ stream->writeU8(numLevels);
+ stream->writeU8(raster->type);
+ stream->writeU8(ras->format);
+
+ int32 totalSize = 0;
+ for(int32 i = 0; i < numLevels; i++)
+ totalSize += getLevelSize(tex->raster, i);
+ totalSize = (totalSize+3)&~3;
+ stream->writeI32(totalSize);
+
+ if(raster->format & Raster::PAL4)
+ stream->write8(ras->palette, 4*32);
+ else if(raster->format & Raster::PAL8)
+ stream->write8(ras->palette, 4*256);
+
+ // exploit the fact that mipmaps are allocated consecutively
+ uint8 *data = raster->lock(0, Raster::LOCKREAD);
+ stream->write8(data, totalSize);
+ raster->unlock(0);
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+ uint32 size = 12 + 72 + 16 + 4;
+ int32 levels = tex->raster->getNumLevels();
+ for(int32 i = 0; i < levels; i++)
+ size += getLevelSize(tex->raster, i);
+ size = (size+3)&~3;
+ if(tex->raster->format & Raster::PAL4)
+ size += 4*32;
+ else if(tex->raster->format & Raster::PAL8)
+ size += 4*256;
+ return size;
+}
+
+}
+}
diff --git a/src/d3d/xboxmatfx.cpp b/src/d3d/xboxmatfx.cpp
new file mode 100644
index 0000000..a6b8718
--- /dev/null
+++ b/src/d3d/xboxmatfx.cpp
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwxbox.h"
+
+namespace rw {
+namespace xbox {
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+ matFXGlobals.pipelines[PLATFORM_XBOX] = makeMatFXPipeline();
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_XBOX])->destroy();
+ matFXGlobals.pipelines[PLATFORM_XBOX] = nil;
+ return o;
+}
+
+void
+initMatFX(void)
+{
+ Driver::registerPlugin(PLATFORM_XBOX, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+}
+
+ObjPipeline*
+makeMatFXPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->pluginID = ID_MATFX;
+ pipe->pluginData = 0;
+ return pipe;
+}
+
+}
+}
diff --git a/src/d3d/xboxskin.cpp b/src/d3d/xboxskin.cpp
new file mode 100644
index 0000000..0ce5cba
--- /dev/null
+++ b/src/d3d/xboxskin.cpp
@@ -0,0 +1,252 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwxbox.h"
+
+#define PLUGIN_ID ID_SKIN
+
+namespace rw {
+namespace xbox {
+
+struct NativeSkin
+{
+ int32 table1[256]; // maps indices to bones
+ int32 table2[256]; // maps bones to indices
+ int32 numUsedBones;
+ void *vertexBuffer;
+ int32 stride;
+};
+
+Stream*
+readNativeSkin(Stream *stream, int32, void *object, int32 offset)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ uint32 vers, platform;
+ if(!findChunk(stream, ID_STRUCT, nil, &vers)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_XBOX){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ if(vers < 0x35000){
+ RWERROR((ERR_VERSION, vers));
+ return nil;
+ }
+
+ Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
+ *PLUGINOFFSET(Skin*, geometry, offset) = skin;
+
+ int32 numBones = stream->readI32();
+ skin->init(numBones, 0, 0);
+ NativeSkin *natskin = rwNewT(NativeSkin, 1, MEMDUR_EVENT | ID_SKIN);
+ skin->platformData = natskin;
+ stream->read32(natskin->table1, 256*sizeof(int32));
+ stream->read32(natskin->table2, 256*sizeof(int32));
+ natskin->numUsedBones = stream->readI32();
+ skin->numWeights = stream->readI32();
+ stream->seek(4); // skip pointer to vertexBuffer
+ natskin->stride = stream->readI32();
+ int32 size = geometry->numVertices*natskin->stride;
+ natskin->vertexBuffer = rwNewT(uint8, size, MEMDUR_EVENT | ID_SKIN);
+ stream->read8(natskin->vertexBuffer, size);
+ stream->read32(skin->inverseMatrices, skin->numBones*64);
+
+ readSkinSplitData(stream, skin);
+ return stream;
+}
+
+Stream*
+writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ assert(skin->platformData);
+ assert(rw::version >= 0x35000 && "can't handle native xbox skin < 0x35000");
+ NativeSkin *natskin = (NativeSkin*)skin->platformData;
+
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ stream->writeU32(PLATFORM_XBOX);
+ stream->writeI32(skin->numBones);
+ stream->write32(natskin->table1, 256*sizeof(int32));
+ stream->write32(natskin->table2, 256*sizeof(int32));
+ stream->writeI32(natskin->numUsedBones);
+ stream->writeI32(skin->numWeights);
+ stream->writeU32(0xBADEAFFE); // pointer to vertexBuffer
+ stream->writeI32(natskin->stride);
+ stream->write8(natskin->vertexBuffer,
+ geometry->numVertices*natskin->stride);
+ stream->write32(skin->inverseMatrices, skin->numBones*64);
+
+ writeSkinSplitData(stream, skin);
+ return stream;
+}
+
+int32
+getSizeNativeSkin(void *object, int32 offset)
+{
+ Geometry *geometry = (Geometry*)object;
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ if(skin == nil)
+ return -1;
+ if(skin->platformData == nil)
+ return -1;
+ NativeSkin *natskin = (NativeSkin*)skin->platformData;
+ return 12 + 8 + 2*256*4 + 4*4 +
+ natskin->stride*geometry->numVertices + skin->numBones*64 +
+ skinSplitDataSize(skin);
+}
+
+void
+skinInstanceCB(Geometry *geo, InstanceDataHeader *header)
+{
+ defaultInstanceCB(geo, header);
+
+ Skin *skin = Skin::get(geo);
+ if(skin == nil)
+ return;
+ NativeSkin *natskin = rwNewT(NativeSkin, 1, MEMDUR_EVENT | ID_SKIN);
+ skin->platformData = natskin;
+
+ natskin->numUsedBones = skin->numUsedBones;
+ memset(natskin->table1, 0xFF, sizeof(natskin->table1));
+ memset(natskin->table2, 0x00, sizeof(natskin->table2));
+ for(int32 i = 0; i < skin->numUsedBones; i++){
+ natskin->table1[i] = skin->usedBones[i];
+ natskin->table2[skin->usedBones[i]] = i;
+ }
+
+ natskin->stride = 3*skin->numWeights;
+ uint8 *vbuf = rwNewT(uint8, header->numVertices*natskin->stride, MEMDUR_EVENT | ID_SKIN);
+ natskin->vertexBuffer = vbuf;
+
+ int32 w[4];
+ int sum;
+ float32 *weights = skin->weights;
+ uint8 *p = vbuf;
+ int32 numVertices = header->numVertices;
+ while(numVertices--){
+ sum = 0;
+ for(int i = 1; i < skin->numWeights; i++){
+ w[i] = (int32)(weights[i]*255.0f + 0.5f);
+ sum += w[i];
+ }
+ w[0] = 255 - sum;
+ for(int i = 0; i < skin->numWeights; i++)
+ p[i] = w[i];
+ p += natskin->stride;
+ weights += 4;
+ }
+
+ numVertices = header->numVertices;
+ p = vbuf + skin->numWeights;
+ uint8 *indices = skin->indices;
+ uint16 *idx;
+ while(numVertices--){
+ idx = (uint16*)p;
+ for(int i = 0; i < skin->numWeights; i++)
+ idx[i] = 3*natskin->table2[indices[i]];
+ p += natskin->stride;
+ indices += 4;
+ }
+}
+
+void
+skinUninstanceCB(Geometry *geo, InstanceDataHeader *header)
+{
+ defaultUninstanceCB(geo, header);
+
+ Skin *skin = Skin::get(geo);
+ if(skin == nil)
+ return;
+ NativeSkin *natskin = (NativeSkin*)skin->platformData;
+
+ uint8 *data = skin->data;
+ float *invMats = skin->inverseMatrices;
+ skin->init(skin->numBones, natskin->numUsedBones, geo->numVertices);
+ memcpy(skin->inverseMatrices, invMats, skin->numBones*64);
+ rwFree(data);
+
+ for(int32 j = 0; j < skin->numUsedBones; j++)
+ skin->usedBones[j] = natskin->table1[j];
+
+ float *weights = skin->weights;
+ uint8 *indices = skin->indices;
+ uint8 *p = (uint8*)natskin->vertexBuffer;
+ int32 numVertices = header->numVertices;
+ float w[4];
+ uint8 i[4];
+ uint16 *ip;
+ while(numVertices--){
+ w[0] = w[1] = w[2] = w[3] = 0.0f;
+ i[0] = i[1] = i[2] = i[3] = 0;
+
+ for(int32 j = 0; j < skin->numWeights; j++)
+ w[j] = *p++/255.0f;
+
+ ip = (uint16*)p;
+ for(int32 j = 0; j < skin->numWeights; j++){
+ i[j] = natskin->table1[*ip++/3];
+ if(w[j] == 0.0f) i[j] = 0; // clean up a bit
+ }
+ p = (uint8*)ip;
+
+ for(int32 j = 0; j < 4; j++){
+ *weights++ = w[j];
+ *indices++ = i[j];
+ }
+ }
+
+ rwFree(natskin->vertexBuffer);
+ rwFree(natskin);
+}
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+ skinGlobals.pipelines[PLATFORM_XBOX] = makeSkinPipeline();
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_XBOX])->destroy();
+ skinGlobals.pipelines[PLATFORM_XBOX] = nil;
+ return o;
+}
+
+void
+initSkin(void)
+{
+ Driver::registerPlugin(PLATFORM_XBOX, 0, ID_SKIN,
+ skinOpen, skinClose);
+}
+
+ObjPipeline*
+makeSkinPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = skinUninstanceCB;
+ pipe->pluginID = ID_SKIN;
+ pipe->pluginData = 1;
+ return pipe;
+}
+
+}
+}
diff --git a/src/d3d/xboxvfmt.cpp b/src/d3d/xboxvfmt.cpp
new file mode 100644
index 0000000..b6569dc
--- /dev/null
+++ b/src/d3d/xboxvfmt.cpp
@@ -0,0 +1,115 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwxbox.h"
+
+namespace rw {
+namespace xbox {
+
+static int32 vertexFmtOffset;
+
+uint32 vertexFormatSizes[6] = {
+ 0, 1, 2, 2, 4, 4
+};
+
+uint32*
+getVertexFmt(Geometry *g)
+{
+ return PLUGINOFFSET(uint32, g, vertexFmtOffset);
+}
+
+uint32
+makeVertexFmt(int32 flags, uint32 numTexSets)
+{
+ if(numTexSets > 4)
+ numTexSets = 4;
+ uint32 fmt = 0x5; // FLOAT3
+ if(flags & Geometry::NORMALS)
+ fmt |= 0x40; // NORMPACKED3
+ for(uint32 i = 0; i < numTexSets; i++)
+ fmt |= 0x500 << i*4; // FLOAT2
+ if(flags & Geometry::PRELIT)
+ fmt |= 0x1000000; // D3DCOLOR
+ return fmt;
+}
+
+uint32
+getVertexFmtStride(uint32 fmt)
+{
+ uint32 stride = 0;
+ uint32 v = fmt & 0xF;
+ uint32 n = (fmt >> 4) & 0xF;
+ stride += v == 4 ? 4 : 3*vertexFormatSizes[v];
+ stride += n == 4 ? 4 : 3*vertexFormatSizes[n];
+ if(fmt & 0x1000000)
+ stride += 4;
+ for(int i = 0; i < 4; i++){
+ uint32 t = (fmt >> (i*4 + 8)) & 0xF;
+ stride += t == 4 ? 4 : 2*vertexFormatSizes[t];
+ }
+ if(fmt & 0xE000000)
+ stride += 8;
+ return stride;
+}
+
+static void*
+createVertexFmt(void *object, int32 offset, int32)
+{
+ *PLUGINOFFSET(uint32, object, offset) = 0;
+ return object;
+}
+
+static void*
+copyVertexFmt(void *dst, void *src, int32 offset, int32)
+{
+ *PLUGINOFFSET(uint32, dst, offset) = *PLUGINOFFSET(uint32, src, offset);
+ return dst;
+}
+
+static Stream*
+readVertexFmt(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ uint32 fmt = stream->readU32();
+ *PLUGINOFFSET(uint32, object, offset) = fmt;
+ // TODO: ? create and attach "vertex shader"
+ return stream;
+}
+
+static Stream*
+writeVertexFmt(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ stream->writeI32(*PLUGINOFFSET(uint32, object, offset));
+ return stream;
+}
+
+static int32
+getSizeVertexFmt(void*, int32, int32)
+{
+ if(rw::platform != PLATFORM_XBOX)
+ return -1;
+ return 4;
+}
+
+void
+registerVertexFormatPlugin(void)
+{
+ vertexFmtOffset = Geometry::registerPlugin(sizeof(uint32), ID_VERTEXFMT,
+ createVertexFmt, nil, copyVertexFmt);
+ Geometry::registerPluginStream(ID_VERTEXFMT,
+ readVertexFmt,
+ writeVertexFmt,
+ getSizeVertexFmt);
+}
+
+}
+}
diff --git a/src/engine.cpp b/src/engine.cpp
new file mode 100644
index 0000000..0583cb3
--- /dev/null
+++ b/src/engine.cpp
@@ -0,0 +1,573 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <new>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "ps2/rwps2.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+#include "gl/rwgl3.h"
+#include "gl/rwwdgl.h"
+
+#define PLUGIN_ID 0
+
+// on windows
+//#ifdef DEBUG
+//#include <crtdbg.h>
+//#define free(p) _free_dbg(p, _NORMAL_BLOCK);
+//#define malloc(sz) _malloc_dbg(sz, _NORMAL_BLOCK, __FILE__, __LINE__)
+//#define realloc(p, sz) _realloc_dbg(p, sz, _NORMAL_BLOCK, __FILE__, __LINE__)
+//#endif
+
+namespace rw {
+
+Engine *engine;
+PluginList Engine::s_plglist(sizeof(Engine));
+Engine::State Engine::state = Dead;
+MemoryFunctions Engine::memfuncs;
+PluginList Driver::s_plglist[NUM_PLATFORMS];
+
+const char *allocLocation;
+
+void *malloc_h(size_t sz, uint32 hint) { if(sz == 0) return nil; return malloc(sz); }
+void *realloc_h(void *p, size_t sz, uint32 hint) { return realloc(p, sz); }
+
+struct MemoryBlock
+{
+ size_t sz;
+ uint32 hint;
+ void *origPtr;
+ const char *codeline;
+ LLLink inAllocList;
+};
+LinkList allocations;
+size_t totalMemoryAllocated;
+
+// We align managed memory blocks on a 16 byte boundary
+
+#define ALIGN16(x) ((x) + 0xF & ~0xF)
+void*
+malloc_managed(size_t sz, uint32 hint)
+{
+ void *origPtr;
+ uint8 *data;
+ MemoryBlock *mem;
+
+ if(sz == 0) return nil;
+ origPtr = malloc(sz + sizeof(MemoryBlock) + 15);
+ if(origPtr == nil)
+ return nil;
+ totalMemoryAllocated += sz;
+ data = (uint8*)origPtr;
+ data += sizeof(MemoryBlock);
+ data = (uint8*)ALIGN16((uintptr)data);
+ mem = (MemoryBlock*)(data-sizeof(MemoryBlock));
+
+ mem->sz = sz;
+ mem->hint = hint;
+ mem->origPtr = origPtr;
+ mem->codeline = allocLocation;
+ allocations.add(&mem->inAllocList);
+
+ return data;
+}
+
+void*
+realloc_managed(void *p, size_t sz, uint32 hint)
+{
+ void *origPtr;
+ MemoryBlock *mem;
+ uint32 offset;
+
+ if(p == nil)
+ return malloc_managed(sz, hint);
+
+ mem = (MemoryBlock*)((uint8*)p-sizeof(MemoryBlock));
+ offset = (uint8*)p - (uint8*)mem->origPtr;
+
+ mem->inAllocList.remove();
+
+ origPtr = realloc(mem->origPtr, sz + sizeof(MemoryBlock) + 15);
+ if(origPtr == nil){
+ allocations.add(&mem->inAllocList);
+ return nil;
+ }
+ p = (uint8*)origPtr + offset;
+ mem = (MemoryBlock*)((uint8*)p-sizeof(MemoryBlock));
+ totalMemoryAllocated -= mem->sz;
+ mem->sz = sz;
+ mem->hint = hint;
+ mem->origPtr = origPtr;
+ mem->codeline = allocLocation;
+ allocations.add(&mem->inAllocList);
+ totalMemoryAllocated += mem->sz;
+
+ return p;
+}
+
+void
+free_managed(void *p)
+{
+ MemoryBlock *mem;
+ if(p == nil)
+ return;
+ mem = (MemoryBlock*)((uint8*)p-sizeof(MemoryBlock));
+ mem->inAllocList.remove();
+ totalMemoryAllocated -= mem->sz;
+ free(mem->origPtr);
+}
+
+void
+printleaks(void)
+{
+ FORLIST(lnk, allocations){
+ MemoryBlock *mem = LLLinkGetData(lnk, MemoryBlock, inAllocList);
+ printf("sz %zu hint %X\n %s\n", mem->sz, mem->hint, mem->codeline);
+ }
+}
+
+// TODO: make the debug out configurable
+void *mustmalloc_h(size_t sz, uint32 hint)
+{
+ void *ret;
+ ret = Engine::memfuncs.rwmalloc(sz, hint);
+ if(ret || sz == 0)
+ return ret;
+ fprintf(stderr, "Error: out of memory\n");
+ exit(1);
+ return nil;
+}
+void *mustrealloc_h(void *p, size_t sz, uint32 hint)
+{
+ void *ret;
+ ret = Engine::memfuncs.rwrealloc(p, sz, hint);
+ if(ret || sz == 0)
+ return ret;
+ fprintf(stderr, "Error: out of memory\n");
+ exit(1);
+ return nil;
+}
+
+char *strdup_LOC(const char *s, uint32 hint, const char *here) {
+ char *t;
+ size_t sz = strlen(s)+1;
+ t = (char*)malloc_LOC(sz, hint, here);
+ if(t)
+ memcpy(t, s, sz);
+ return t;
+}
+
+MemoryFunctions defaultMemfuncs = {
+ malloc_h,
+ realloc_h,
+ free,
+ nil,
+ nil
+};
+
+MemoryFunctions managedMemfuncs = {
+ malloc_managed,
+ realloc_managed,
+ free_managed,
+ nil,
+ nil
+};
+
+// This function mainly registers engine plugins
+bool32
+Engine::init(MemoryFunctions *memfuncs)
+{
+ if(engine || Engine::state != Dead){
+ RWERROR((ERR_ENGINEINIT));
+ return 0;
+ }
+
+ totalMemoryAllocated = 0;
+ allocations.init();
+
+ if(memfuncs)
+ Engine::memfuncs = *memfuncs;
+ else
+ Engine::memfuncs = defaultMemfuncs;
+
+ if(Engine::memfuncs.rwmustmalloc == nil)
+ Engine::memfuncs.rwmustmalloc = mustmalloc_h;
+ if(Engine::memfuncs.rwmustrealloc == nil)
+ Engine::memfuncs.rwmustrealloc = mustrealloc_h;
+
+ PluginList::open();
+
+ for(uint i = 0; i < NUM_PLATFORMS; i++)
+ new (&Driver::s_plglist[i]) PluginList(sizeof(Driver));
+
+ // core plugin attach here
+ Frame::registerModule();
+ Image::registerModule();
+ Raster::registerModule();
+ Texture::registerModule();
+
+ // TODO: reset all allocation counts here. or maybe do that in modules?
+ Frame::numAllocated = 0;
+ Image::numAllocated = 0;
+ Raster::numAllocated = 0;
+ Texture::numAllocated = 0;
+ TexDictionary::numAllocated = 0;
+ Geometry::numAllocated = 0;
+ Material::numAllocated = 0;
+ Atomic::numAllocated = 0;
+ Light::numAllocated = 0;
+ Camera::numAllocated = 0;
+ Clump::numAllocated = 0;
+ World::numAllocated = 0;
+
+ // driver plugin attach
+ ps2::registerPlatformPlugins();
+ xbox::registerPlatformPlugins();
+ d3d8::registerPlatformPlugins();
+ d3d9::registerPlatformPlugins();
+ wdgl::registerPlatformPlugins();
+ gl3::registerPlatformPlugins();
+
+ Engine::state = Initialized;
+ return 1;
+}
+
+// This is where RW allocates the engine and e.g. opens d3d
+bool32
+Engine::open(EngineOpenParams *p)
+{
+ if(engine || Engine::state != Initialized){
+ RWERROR((ERR_ENGINEOPEN));
+ return 0;
+ }
+
+ // Allocate engine
+ engine = (Engine*)rwNew(Engine::s_plglist.size, MEMDUR_GLOBAL);
+ engine->currentCamera = nil;
+ engine->currentWorld = nil;
+ engine->filefuncs.rwfopen = (void *(*)(const char*, const char*))fopen;
+ engine->filefuncs.rwfclose = (int (*)(void*))fclose;
+ engine->filefuncs.rwfseek = (int (*)(void*, long, int))fseek;
+ engine->filefuncs.rwftell = (long (*)(void*))ftell;
+ engine->filefuncs.rwfread = (size_t (*)(void*, size_t, size_t, void*))fread;
+ engine->filefuncs.rwfwrite = (size_t (*)(const void*, size_t, size_t, void*))fwrite;
+ engine->filefuncs.rwfeof = (int (*)(void*))feof;
+
+ // Initialize device
+ // Device and possibly OS specific!
+#ifdef RW_PS2
+ engine->device = ps2::renderdevice;
+#elif RW_GL3
+ engine->device = gl3::renderdevice;
+#elif RW_D3D9
+ engine->device = d3d::renderdevice;
+#else
+ engine->device = null::renderdevice;
+#endif
+
+ engine->device.system(DEVICEOPEN, (void*)p, 0);
+
+ engine->dummyDefaultPipeline = ObjPipeline::create();
+ for(uint i = 0; i < NUM_PLATFORMS; i++){
+ rw::engine->driver[i] = (Driver*)rwNew(Driver::s_plglist[i].size,
+ MEMDUR_GLOBAL);
+
+ engine->driver[i]->defaultPipeline = engine->dummyDefaultPipeline;
+
+ engine->driver[i]->rasterCreate = null::rasterCreate;
+ engine->driver[i]->rasterLock = null::rasterLock;
+ engine->driver[i]->rasterUnlock = null::rasterUnlock;
+ engine->driver[i]->rasterLockPalette = null::rasterLockPalette;
+ engine->driver[i]->rasterUnlockPalette = null::rasterUnlockPalette;
+ engine->driver[i]->rasterNumLevels = null::rasterNumLevels;
+ engine->driver[i]->imageFindRasterFormat = null::imageFindRasterFormat;
+ engine->driver[i]->rasterFromImage = null::rasterFromImage;
+ engine->driver[i]->rasterToImage = null::rasterToImage;
+ }
+
+ Engine::state = Opened;
+ return 1;
+}
+
+// This is where RW creates the actual rendering device
+// and calls the engine plugin ctors
+bool32
+Engine::start(void)
+{
+ if(engine == nil || Engine::state != Opened){
+ RWERROR((ERR_ENGINESTART));
+ return 0;
+ }
+
+ engine->device.system(DEVICEINIT, nil, 0);
+
+ Engine::s_plglist.construct(engine);
+ for(uint i = 0; i < NUM_PLATFORMS; i++)
+ Driver::s_plglist[i].construct(rw::engine->driver[i]);
+
+ engine->device.system(DEVICEFINALIZE, nil, 0);
+
+ // Register some image formats. Or should we leave that to the user?
+ Image::registerFileFormat("tga", readTGA, writeTGA);
+ Image::registerFileFormat("bmp", readBMP, writeBMP);
+ Image::registerFileFormat("png", readPNG, writePNG);
+
+ Engine::state = Started;
+ return 1;
+}
+
+void
+Engine::term(void)
+{
+ if(engine || Engine::state != Initialized){
+ RWERROR((ERR_GENERAL));
+ return;
+ }
+
+ PluginList::close();
+
+ // This has to be reset because it won't be opened again otherwise
+ // TODO: maybe reset more stuff here?
+ d3d::nativeRasterOffset = 0;
+
+ Engine::state = Dead;
+}
+
+void
+Engine::close(void)
+{
+ if(engine == nil || Engine::state != Opened){
+ RWERROR((ERR_GENERAL));
+ return;
+ }
+
+ engine->device.system(DEVICECLOSE, nil, 0);
+ for(uint i = 0; i < NUM_PLATFORMS; i++)
+ rwFree(rw::engine->driver[i]);
+ engine->dummyDefaultPipeline->destroy();
+ rwFree(engine);
+ engine = nil;
+ Engine::state = Initialized;
+}
+
+void
+Engine::stop(void)
+{
+ if(engine == nil || Engine::state != Started){
+ RWERROR((ERR_GENERAL));
+ return;
+ }
+
+ for(uint i = 0; i < NUM_PLATFORMS; i++)
+ Driver::s_plglist[i].destruct(rw::engine->driver[i]);
+ Engine::s_plglist.destruct(engine);
+
+ engine->device.system(DEVICETERM, nil, 0);
+
+ Engine::state = Opened;
+}
+
+
+int32
+Engine::getNumSubSystems(void)
+{
+ return engine->device.system(DEVICEGETNUMSUBSYSTEMS, nil, 0);
+}
+
+int32
+Engine::getCurrentSubSystem(void)
+{
+ return engine->device.system(DEVICEGETCURRENTSUBSYSTEM, nil, 0);
+}
+
+bool32
+Engine::setSubSystem(int32 subsys)
+{
+ return engine->device.system(DEVICESETSUBSYSTEM, nil, subsys);
+}
+
+SubSystemInfo*
+Engine::getSubSystemInfo(SubSystemInfo *info, int32 subsys)
+{
+ if(engine->device.system(DEVICEGETSUBSSYSTEMINFO, info, subsys))
+ return info;
+ return nil;
+}
+
+
+int32
+Engine::getNumVideoModes(void)
+{
+ return engine->device.system(DEVICEGETNUMVIDEOMODES, nil, 0);
+}
+
+int32
+Engine::getCurrentVideoMode(void)
+{
+ return engine->device.system(DEVICEGETCURRENTVIDEOMODE, nil, 0);
+}
+
+bool32
+Engine::setVideoMode(int32 mode)
+{
+ return engine->device.system(DEVICESETVIDEOMODE, nil, mode);
+}
+
+VideoMode*
+Engine::getVideoModeInfo(VideoMode *info, int32 mode)
+{
+ if(engine->device.system(DEVICEGETVIDEOMODEINFO, info, mode))
+ return info;
+ return nil;
+}
+
+
+uint32
+Engine::getMaxMultiSamplingLevels(void)
+{
+ return engine->device.system(DEVICEGETMAXMULTISAMPLINGLEVELS, nil, 0);
+}
+
+uint32
+Engine::getMultiSamplingLevels(void)
+{
+ return engine->device.system(DEVICEGETMULTISAMPLINGLEVELS, nil, 0);
+}
+
+bool32
+Engine::setMultiSamplingLevels(uint32 levels)
+{
+ return engine->device.system(DEVICESETMULTISAMPLINGLEVELS, nil, levels);
+}
+
+
+namespace null {
+
+void beginUpdate(Camera*) { }
+void endUpdate(Camera*) { }
+void clearCamera(Camera*,RGBA*,uint32) { }
+void showRaster(Raster*,uint32) { }
+
+void setRenderState(int32, void*) { }
+void *getRenderState(int32) { return 0; }
+
+bool32 rasterRenderFast(Raster *raster, int32 x, int32 y) { return 0; }
+
+void im2DRenderLine(void*, int32, int32, int32) { }
+void im2DRenderTriangle(void*, int32, int32, int32, int32) { }
+void im2DRenderPrimitive(PrimitiveType, void*, int32) { }
+void im2DRenderIndexedPrimitive(PrimitiveType, void*, int32, void*, int32) { }
+
+void im3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags) { }
+void im3DRenderPrimitive(PrimitiveType primType) { }
+void im3DRenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices) { }
+void im3DEnd(void) { }
+
+Raster*
+rasterCreate(Raster*)
+{
+ assert(0 && "rasterCreate not implemented");
+ return nil;
+}
+
+uint8*
+rasterLock(Raster*, int32, int32)
+{
+ assert(0 && "lockRaster not implemented");
+ return nil;
+}
+
+void
+rasterUnlock(Raster*, int32)
+{
+ assert(0 && "unlockRaster not implemented");
+}
+
+uint8*
+rasterLockPalette(Raster*, int32)
+{
+ assert(0 && "rasterLockPalette not implemented");
+ return nil;
+}
+
+void
+rasterUnlockPalette(Raster*)
+{
+ assert(0 && "rasterUnlockPalette not implemented");
+}
+
+int32
+rasterNumLevels(Raster*)
+{
+ assert(0 && "rasterNumLevels not implemented");
+ return 0;
+}
+
+bool32
+imageFindRasterFormat(Image *img, int32 type,
+ int32 *width, int32 *height, int32 *depth, int32 *format)
+{
+ assert(0 && "imageFindRasterFormat not implemented");
+ return 0;
+}
+
+bool32
+rasterFromImage(Raster*, Image*)
+{
+ assert(0 && "rasterFromImage not implemented");
+ return 0;
+}
+
+Image*
+rasterToImage(Raster*)
+{
+ assert(0 && "rasterToImage not implemented");
+ return nil;
+}
+
+int
+deviceSystem(DeviceReq req, void *arg0, int32 n)
+{
+ switch(req){
+ case DEVICEGETNUMSUBSYSTEMS:
+ return 0;
+ case DEVICEGETCURRENTSUBSYSTEM:
+ return 0;
+ case DEVICEGETSUBSSYSTEMINFO:
+ return 0;
+ default: break;
+ }
+ return 1;
+}
+
+Device renderdevice = {
+ 0.0f, 1.0f,
+ null::beginUpdate,
+ null::endUpdate,
+ null::clearCamera,
+ null::showRaster,
+ null::rasterRenderFast,
+ null::setRenderState,
+ null::getRenderState,
+ null::im2DRenderLine,
+ null::im2DRenderTriangle,
+ null::im2DRenderPrimitive,
+ null::im2DRenderIndexedPrimitive,
+ null::im3DTransform,
+ null::im3DRenderPrimitive,
+ null::im3DRenderIndexedPrimitive,
+ null::im3DEnd,
+ null::deviceSystem
+};
+
+}
+}
diff --git a/src/error.cpp b/src/error.cpp
new file mode 100644
index 0000000..bdf4080
--- /dev/null
+++ b/src/error.cpp
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+
+namespace rw {
+
+static Error error;
+
+void
+setError(Error *e)
+{
+ error = *e;
+}
+
+Error*
+getError(Error *e)
+{
+ *e = error;
+ error.plugin = 0;
+ error.code = 0;
+ return e;
+}
+
+#define ECODE(c, s) s
+
+const char *errstrs[] = {
+ "No error",
+#include "base.err"
+};
+
+#undef ECODE
+
+char*
+dbgsprint(uint32 code, ...)
+{
+ va_list ap;
+ static char strbuf[512];
+
+ if(code & 0x80000000)
+ code &= ~0x80000000;
+ va_start(ap, code);
+ vsprintf(strbuf, errstrs[code], ap);
+ va_end(ap);
+ return strbuf;
+}
+
+}
diff --git a/src/frame.cpp b/src/frame.cpp
new file mode 100644
index 0000000..1832f00
--- /dev/null
+++ b/src/frame.cpp
@@ -0,0 +1,470 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID ID_FRAMELIST
+
+namespace rw {
+
+int32 Frame::numAllocated;
+
+PluginList Frame::s_plglist(sizeof(Frame));
+static void *frameOpen(void *object, int32 offset, int32 size) { engine->frameDirtyList.init(); return object; }
+static void *frameClose(void *object, int32 offset, int32 size) { return object; }
+
+void
+Frame::registerModule(void)
+{
+ Engine::registerPlugin(0, ID_FRAMEMODULE, frameOpen, frameClose);
+}
+
+Frame*
+Frame::create(void)
+{
+ Frame *f = (Frame*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_FRAMELIST);
+ if(f == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ f->object.init(Frame::ID, 0);
+ f->objectList.init();
+ f->child = nil;
+ f->next = nil;
+ f->root = f;
+ f->matrix.setIdentity();
+ f->ltm.setIdentity();
+ s_plglist.construct(f);
+ return f;
+}
+
+Frame*
+Frame::cloneHierarchy(void)
+{
+ Frame *frame = this->cloneAndLink();
+ this->purgeClone();
+ return frame;
+}
+void
+Frame::destroy(void)
+{
+ FORLIST(lnk, this->objectList)
+ ObjectWithFrame::fromFrame(lnk)->setFrame(nil);
+ s_plglist.destruct(this);
+ if(this->getParent())
+ this->removeChild();
+ if(this->object.privateFlags & Frame::HIERARCHYSYNC)
+ this->inDirtyList.remove();
+ for(Frame *f = this->child; f; f = f->next)
+ f->object.parent = nil;
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+Frame::destroyHierarchy(void)
+{
+ Frame *next;
+ for(Frame *child = this->child; child; child = next){
+ next = child->next;
+ child->destroyHierarchy();
+ }
+ assert(this->objectList.isEmpty());
+ s_plglist.destruct(this);
+ if(this->object.privateFlags & Frame::HIERARCHYSYNC)
+ this->inDirtyList.remove();
+ rwFree(this);
+}
+
+Frame*
+Frame::addChild(Frame *child, bool32 append)
+{
+ Frame *c;
+ if(child->getParent())
+ child->removeChild();
+ if(append){
+ if(this->child == nil)
+ this->child = child;
+ else{
+ for(c = this->child; c->next; c = c->next);
+ c->next = child;
+ }
+ child->next = nil;
+ }else{
+ child->next = this->child;
+ this->child = child;
+ }
+ child->object.parent = this;
+ child->root = this->root;
+ for(c = child->child; c; c = c->next)
+ c->setHierarchyRoot(this);
+ // If the child was a root, remove from dirty list
+ if(child->object.privateFlags & Frame::HIERARCHYSYNC){
+ child->inDirtyList.remove();
+ child->object.privateFlags &= ~Frame::HIERARCHYSYNC;
+ }
+ this->updateObjects();
+ return this;
+}
+
+Frame*
+Frame::removeChild(void)
+{
+ Frame *parent = this->getParent();
+ Frame *child = parent->child;
+ if(child == this)
+ parent->child = this->next;
+ else{
+ while(child->next != this)
+ child = child->next;
+ child->next = this->next;
+ }
+ this->object.parent = this->next = nil;
+ // give the hierarchy a new root
+ this->setHierarchyRoot(this);
+ this->updateObjects();
+ return this;
+}
+
+Frame*
+Frame::forAllChildren(Callback cb, void *data)
+{
+ Frame *next;
+ for(Frame *f = this->child; f; f = next){
+ next = f->next;
+ if(cb(f, data) == nil)
+ return this;
+ }
+ return this;
+}
+
+static Frame*
+countCB(Frame *f, void *count)
+{
+ (*(int32*)count)++;
+ f->forAllChildren(countCB, count);
+ return f;
+}
+
+int32
+Frame::count(void)
+{
+ int32 count = 1;
+ this->forAllChildren(countCB, (void*)&count);
+ return count;
+}
+
+/*
+ * Synching is a bit complicated. If anything in the hierarchy is not synched,
+ * the root of the hierarchy is marked with the HIERARCHYSYNC flags.
+ * Every unsynched frame is marked with the SUBTREESYNC flags.
+ * If the LTM is not synched, the LTM flags are set.
+ * If attached objects need synching, the OBJ flags are set.
+ */
+
+/* Synch just LTM matrices in a hierarchy */
+static void
+syncLTMRecurse(Frame *frame, uint8 hierarchyFlags)
+{
+ for(; frame; frame = frame->next){
+ // If frame is dirty or any parent was dirty, update LTM
+ hierarchyFlags |= frame->object.privateFlags;
+ if(hierarchyFlags & Frame::SUBTREESYNCLTM){
+ Matrix::mult(&frame->ltm, &frame->matrix,
+ &frame->getParent()->ltm);
+ frame->object.privateFlags &= ~Frame::SUBTREESYNCLTM;
+ }
+ // And synch all children
+ syncLTMRecurse(frame->child, hierarchyFlags);
+ }
+}
+
+/* Synch just objects in a hierarchy */
+static void
+syncObjRecurse(Frame *frame)
+{
+ for(; frame; frame = frame->next){
+ // Synch attached objects
+ FORLIST(lnk, frame->objectList)
+ ObjectWithFrame::fromFrame(lnk)->sync();
+ frame->object.privateFlags &= ~Frame::SUBTREESYNCOBJ;
+ // And synch all children
+ syncObjRecurse(frame->child);
+ }
+}
+
+/* Synch LTM and objects */
+static void
+syncRecurse(Frame *frame, uint8 hierarchyFlags)
+{
+ for(; frame; frame = frame->next){
+ // If frame is dirty or any parent was dirty, update LTM
+ hierarchyFlags |= frame->object.privateFlags;
+ if(hierarchyFlags & Frame::SUBTREESYNCLTM)
+ Matrix::mult(&frame->ltm, &frame->matrix,
+ &frame->getParent()->ltm);
+ // Synch attached objects
+ FORLIST(lnk, frame->objectList)
+ ObjectWithFrame::fromFrame(lnk)->sync();
+ frame->object.privateFlags &= ~Frame::SUBTREESYNC;
+ // And synch all children
+ syncRecurse(frame->child, hierarchyFlags);
+ }
+}
+
+/* Sync the LTMs of the hierarchy of which 'this' is the root */
+void
+Frame::syncHierarchyLTM(void)
+{
+ // Sync root's LTM
+ if(this->object.privateFlags & Frame::SUBTREESYNCLTM)
+ this->ltm = this->matrix;
+ // ...and children
+ syncLTMRecurse(this->child, this->object.privateFlags);
+ // all clean now
+ this->object.privateFlags &= ~Frame::SYNCLTM;
+}
+
+Matrix*
+Frame::getLTM(void)
+{
+ if(this->root->object.privateFlags & Frame::HIERARCHYSYNCLTM)
+ this->root->syncHierarchyLTM();
+ return &this->ltm;
+}
+
+/* Synch all dirty frames; LTMs and objects */
+void
+Frame::syncDirty(void)
+{
+ Frame *frame;
+ FORLIST(lnk, engine->frameDirtyList){
+ frame = LLLinkGetData(lnk, Frame, inDirtyList);
+ if(frame->object.privateFlags & Frame::HIERARCHYSYNCLTM){
+ // Sync root's LTM
+ if(frame->object.privateFlags & Frame::SUBTREESYNCLTM)
+ frame->ltm = frame->matrix;
+ // Synch attached objects
+ FORLIST(lnk, frame->objectList)
+ ObjectWithFrame::fromFrame(lnk)->sync();
+ // ...and children
+ syncRecurse(frame->child, frame->object.privateFlags);
+ }else{
+ // LTMs are clean, just synch objects
+ FORLIST(lnk, frame->objectList)
+ ObjectWithFrame::fromFrame(lnk)->sync();
+ syncObjRecurse(frame->child);
+ }
+ // all clean now
+ frame->object.privateFlags &= ~(Frame::SYNCLTM | Frame::SYNCOBJ);
+ }
+ engine->frameDirtyList.init();
+}
+
+void
+Frame::rotate(const V3d *axis, float32 angle, CombineOp op)
+{
+ this->matrix.rotate(axis, angle, op);
+ updateObjects();
+}
+
+void
+Frame::rotate(const Quat *q, CombineOp op)
+{
+ this->matrix.rotate(*q, op);
+ updateObjects();
+}
+
+void
+Frame::translate(const V3d *trans, CombineOp op)
+{
+ this->matrix.translate(trans, op);
+ updateObjects();
+}
+
+void
+Frame::scale(const V3d *scl, CombineOp op)
+{
+ this->matrix.scale(scl, op);
+ updateObjects();
+}
+
+void
+Frame::transform(const Matrix *mat, CombineOp op)
+{
+ this->matrix.transform(mat, op);
+ updateObjects();
+}
+
+void
+Frame::updateObjects(void)
+{
+ // Mark root as dirty and insert into dirty list if necessary
+ if((this->root->object.privateFlags & HIERARCHYSYNC) == 0)
+ engine->frameDirtyList.add(&this->root->inDirtyList);
+ this->root->object.privateFlags |= HIERARCHYSYNC;
+ // Mark subtree as dirty as well
+ this->object.privateFlags |= SUBTREESYNC;
+}
+
+void
+Frame::setHierarchyRoot(Frame *root)
+{
+ this->root = root;
+ for(Frame *child = this->child; child; child = child->next)
+ child->setHierarchyRoot(root);
+}
+
+static Frame*
+cloneRecurse(Frame *old, Frame *newroot)
+{
+ Frame *frame = Frame::create();
+ if(newroot == nil)
+ newroot = frame;
+ frame->object.copy(&old->object);
+ frame->matrix = old->matrix;
+ frame->root = newroot;
+ old->root = frame; // Remember cloned frame
+ for(Frame *child = old->child; child; child = child->next){
+ Frame *clonedchild = cloneRecurse(child, newroot);
+ clonedchild->next = frame->child;
+ frame->child = clonedchild;
+ clonedchild->object.parent = frame;
+ }
+ Frame::s_plglist.copy(frame, old);
+ return frame;
+}
+
+// Clone a frame hierarchy. Link cloned frames into Frame::root of the originals.
+Frame*
+Frame::cloneAndLink(void)
+{
+ Frame *newhier = cloneRecurse(this, nil);
+ if(newhier){
+ // frame is not in dirty list so important to get this flag right
+ newhier->object.privateFlags &= ~HIERARCHYSYNC;
+ newhier->updateObjects();
+ }
+ return newhier;
+}
+
+// Remove links to cloned frames from hierarchy.
+void
+Frame::purgeClone(void)
+{
+ Frame *parent = this->getParent();
+ this->setHierarchyRoot(parent ? parent->root : this);
+}
+
+struct FrameStreamData
+{
+ V3d right, up, at, pos;
+ int32 parent;
+ int32 matflag;
+};
+
+FrameList_*
+FrameList_::streamRead(Stream *stream)
+{
+ FrameStreamData buf;
+ this->numFrames = 0;
+ this->frames = nil;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ this->numFrames = stream->readI32();
+ this->frames = (Frame**)rwMalloc(this->numFrames*sizeof(Frame*), MEMDUR_EVENT | ID_FRAMELIST);
+ if(this->frames == nil){
+ RWERROR((ERR_ALLOC, this->numFrames*sizeof(Frame*)));
+ return nil;
+ }
+ for(int32 i = 0; i < this->numFrames; i++){
+ Frame *f;
+ stream->read32(&buf, sizeof(buf));
+ this->frames[i] = f = Frame::create();
+ if(f == nil){
+ // TODO: clean up frames?
+ rwFree(this->frames);
+ return nil;
+ }
+ f->matrix.right = buf.right;
+ f->matrix.up = buf.up;
+ f->matrix.at = buf.at;
+ f->matrix.pos = buf.pos;
+ f->matrix.optimize();
+ // RW always removes identity flag
+ f->matrix.flags &= ~Matrix::IDENTITY;
+ if(buf.parent >= 0)
+ this->frames[buf.parent]->addChild(f, rw::streamAppendFrames);
+ }
+ for(int32 i = 0; i < this->numFrames; i++)
+ Frame::s_plglist.streamRead(stream, this->frames[i]);
+ return this;
+}
+
+void
+FrameList_::streamWrite(Stream *stream)
+{
+ FrameStreamData buf;
+
+ int size = 0, structsize = 0;
+ structsize = 4 + this->numFrames*sizeof(FrameStreamData);
+ size += 12 + structsize;
+ for(int32 i = 0; i < this->numFrames; i++)
+ size += 12 + Frame::s_plglist.streamGetSize(this->frames[i]);
+
+ writeChunkHeader(stream, ID_FRAMELIST, size);
+ writeChunkHeader(stream, ID_STRUCT, structsize);
+ stream->writeU32(this->numFrames);
+ for(int32 i = 0; i < this->numFrames; i++){
+ Frame *f = this->frames[i];
+ buf.right = f->matrix.right;
+ buf.up = f->matrix.up;
+ buf.at = f->matrix.at;
+ buf.pos = f->matrix.pos;
+ buf.parent = findPointer(f->getParent(), (void**)this->frames,
+ this->numFrames);
+ buf.matflag = 0; //f->matflag;
+ stream->write32(&buf, sizeof(buf));
+ }
+ for(int32 i = 0; i < this->numFrames; i++)
+ Frame::s_plglist.streamWrite(stream, this->frames[i]);
+}
+
+static Frame*
+sizeCB(Frame *f, void *size)
+{
+ *(int32*)size += Frame::s_plglist.streamGetSize(f);
+ f->forAllChildren(sizeCB, size);
+ return f;
+}
+
+uint32
+FrameList_::streamGetSize(Frame *f)
+{
+ int32 numFrames = f->count();
+ uint32 size = 12 + 12 + 4 + numFrames*(sizeof(FrameStreamData)+12);
+ sizeCB(f, (void*)&size);
+ return size;
+}
+
+Frame**
+makeFrameList(Frame *frame, Frame **flist)
+{
+ *flist++ = frame;
+ if(frame->next)
+ flist = makeFrameList(frame->next, flist);
+ if(frame->child)
+ flist = makeFrameList(frame->child, flist);
+ return flist;
+}
+
+}
diff --git a/src/geometry.cpp b/src/geometry.cpp
new file mode 100644
index 0000000..882b685
--- /dev/null
+++ b/src/geometry.cpp
@@ -0,0 +1,1053 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID ID_GEOMETRY
+
+namespace rw {
+
+int32 Geometry::numAllocated;
+int32 Material::numAllocated;
+
+PluginList Geometry::s_plglist(sizeof(Geometry));
+PluginList Material::s_plglist(sizeof(Material));
+
+static SurfaceProperties defaultSurfaceProps = { 1.0f, 1.0f, 1.0f };
+
+// We allocate twice because we have to allocate the data separately for uninstancing
+Geometry*
+Geometry::create(int32 numVerts, int32 numTris, uint32 flags)
+{
+ Geometry *geo = (Geometry*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_GEOMETRY);
+ if(geo == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ geo->object.init(Geometry::ID, 0);
+ geo->flags = flags & 0xFF00FFFF;
+ geo->numTexCoordSets = (flags & 0xFF0000) >> 16;
+ if(geo->numTexCoordSets == 0)
+ geo->numTexCoordSets = (geo->flags & TEXTURED) ? 1 :
+ (geo->flags & TEXTURED2) ? 2 : 0;
+ geo->numTriangles = numTris;
+ geo->numVertices = numVerts;
+
+ geo->colors = nil;
+ for(int32 i = 0; i < 8; i++)
+ geo->texCoords[i] = nil;
+ geo->triangles = nil;
+ // Allocate all attributes at once. The triangle pointer
+ // will hold the first address (even when there are no triangles)
+ // so we can free easily.
+ if(!(geo->flags & NATIVE)){
+ int32 sz = geo->numTriangles*sizeof(Triangle);
+ if(geo->flags & PRELIT)
+ sz += geo->numVertices*sizeof(RGBA);
+ sz += geo->numTexCoordSets*geo->numVertices*sizeof(TexCoords);
+
+ uint8 *data = (uint8*)rwNew(sz, MEMDUR_EVENT | ID_GEOMETRY);
+ geo->triangles = (Triangle*)data;
+ data += geo->numTriangles*sizeof(Triangle);
+ if(geo->flags & PRELIT && geo->numVertices){
+ geo->colors = (RGBA*)data;
+ data += geo->numVertices*sizeof(RGBA);
+ }
+ if(geo->numVertices)
+ for(int32 i = 0; i < geo->numTexCoordSets; i++){
+ geo->texCoords[i] = (TexCoords*)data;
+ data += geo->numVertices*sizeof(TexCoords);
+ }
+
+ // init triangles
+ for(int32 i = 0; i < geo->numTriangles; i++)
+ geo->triangles[i].matId = 0xFFFF;
+ }
+ geo->numMorphTargets = 0;
+ geo->morphTargets = nil;
+ geo->addMorphTargets(1);
+
+ geo->matList.init();
+ geo->lockedSinceInst = 0;
+ geo->meshHeader = nil;
+ geo->instData = nil;
+ geo->refCount = 1;
+
+ s_plglist.construct(geo);
+ return geo;
+}
+
+void
+Geometry::destroy(void)
+{
+ this->refCount--;
+ if(this->refCount <= 0){
+ s_plglist.destruct(this);
+ // Also frees colors and tex coords
+ rwFree(this->triangles);
+ // Also frees their data
+ rwFree(this->morphTargets);
+ // Also frees indices
+ rwFree(this->meshHeader);
+ this->matList.deinit();
+ rwFree(this);
+ numAllocated--;
+ }
+}
+
+void
+Geometry::lock(int32 lockFlags)
+{
+ lockedSinceInst |= lockFlags;
+ if(lockFlags & LOCKPOLYGONS){
+ rwFree(this->meshHeader);
+ this->meshHeader = nil;
+ }
+}
+
+void
+Geometry::unlock(void)
+{
+ if(this->meshHeader == nil)
+ this->buildMeshes();
+
+}
+
+struct GeoStreamData
+{
+ uint32 flags;
+ int32 numTriangles;
+ int32 numVertices;
+ int32 numMorphTargets;
+};
+
+Geometry*
+Geometry::streamRead(Stream *stream)
+{
+ uint32 version;
+ GeoStreamData buf;
+ SurfaceProperties surfProps;
+ MaterialList *ret;
+ static SurfaceProperties reset = { 1.0f, 1.0f, 1.0f };
+
+ if(!findChunk(stream, ID_STRUCT, nil, &version)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ stream->read32(&buf, sizeof(buf));
+ Geometry *geo = Geometry::create(buf.numVertices,
+ buf.numTriangles, buf.flags);
+ if(geo == nil)
+ return nil;
+ geo->addMorphTargets(buf.numMorphTargets-1);
+ if(version < 0x34000)
+ stream->read32(&surfProps, 12);
+
+ if(!(geo->flags & NATIVE)){
+ if(geo->flags & PRELIT)
+ stream->read8(geo->colors, 4*geo->numVertices);
+ for(int32 i = 0; i < geo->numTexCoordSets; i++)
+ stream->read32(geo->texCoords[i],
+ 2*geo->numVertices*4);
+ for(int32 i = 0; i < geo->numTriangles; i++){
+ uint32 tribuf[2];
+ stream->read32(tribuf, 8);
+ geo->triangles[i].v[0] = tribuf[0] >> 16;
+ geo->triangles[i].v[1] = tribuf[0];
+ geo->triangles[i].v[2] = tribuf[1] >> 16;
+ geo->triangles[i].matId = tribuf[1];
+ }
+ }
+
+ for(int32 i = 0; i < geo->numMorphTargets; i++){
+ MorphTarget *m = &geo->morphTargets[i];
+ stream->read32(&m->boundingSphere, 4*4);
+ int32 hasVertices = stream->readI32();
+ int32 hasNormals = stream->readI32();
+ if(hasVertices)
+ stream->read32(m->vertices, 3*geo->numVertices*4);
+ if(hasNormals)
+ stream->read32(m->normals, 3*geo->numVertices*4);
+ }
+
+ if(!findChunk(stream, ID_MATLIST, nil, nil)){
+ RWERROR((ERR_CHUNK, "MATLIST"));
+ goto fail;
+ }
+ if(version < 0x34000)
+ defaultSurfaceProps = surfProps;
+
+ ret = MaterialList::streamRead(stream, &geo->matList);
+ if(version < 0x34000)
+ defaultSurfaceProps = reset;
+ if(ret == nil)
+ goto fail;
+ if(s_plglist.streamRead(stream, geo))
+ return geo;
+
+fail:
+ geo->destroy();
+ return nil;
+}
+
+static uint32
+geoStructSize(Geometry *geo)
+{
+ uint32 size = 0;
+ size += sizeof(GeoStreamData);
+ if(version < 0x34000)
+ size += 12; // surface properties
+ if(!(geo->flags & Geometry::NATIVE)){
+ if(geo->flags&geo->PRELIT)
+ size += 4*geo->numVertices;
+ for(int32 i = 0; i < geo->numTexCoordSets; i++)
+ size += 2*geo->numVertices*4;
+ size += 4*geo->numTriangles*2;
+ }
+ for(int32 i = 0; i < geo->numMorphTargets; i++){
+ MorphTarget *m = &geo->morphTargets[i];
+ size += 4*4 + 2*4; // bounding sphere and bools
+ if(!(geo->flags & Geometry::NATIVE)){
+ if(m->vertices)
+ size += 3*geo->numVertices*4;
+ if(m->normals)
+ size += 3*geo->numVertices*4;
+ }
+ }
+ return size;
+}
+
+bool
+Geometry::streamWrite(Stream *stream)
+{
+ GeoStreamData buf;
+ static float32 fbuf[3] = { 1.0f, 1.0f, 1.0f };
+
+ writeChunkHeader(stream, ID_GEOMETRY, this->streamGetSize());
+ writeChunkHeader(stream, ID_STRUCT, geoStructSize(this));
+
+ buf.flags = this->flags | this->numTexCoordSets << 16;
+ buf.numTriangles = this->numTriangles;
+ buf.numVertices = this->numVertices;
+ buf.numMorphTargets = this->numMorphTargets;
+ stream->write32(&buf, sizeof(buf));
+ if(version < 0x34000)
+ stream->write32(fbuf, sizeof(fbuf));
+
+ if(!(this->flags & NATIVE)){
+ if(this->flags & PRELIT)
+ stream->write8(this->colors, 4*this->numVertices);
+ for(int32 i = 0; i < this->numTexCoordSets; i++)
+ stream->write32(this->texCoords[i],
+ 2*this->numVertices*4);
+ for(int32 i = 0; i < this->numTriangles; i++){
+ uint32 tribuf[2];
+ tribuf[0] = this->triangles[i].v[0] << 16 |
+ this->triangles[i].v[1];
+ tribuf[1] = this->triangles[i].v[2] << 16 |
+ this->triangles[i].matId;
+ stream->write32(tribuf, 8);
+ }
+ }
+
+ for(int32 i = 0; i < this->numMorphTargets; i++){
+ MorphTarget *m = &this->morphTargets[i];
+ stream->write32(&m->boundingSphere, 4*4);
+ if(!(this->flags & NATIVE)){
+ stream->writeI32(m->vertices != nil);
+ stream->writeI32(m->normals != nil);
+ if(m->vertices)
+ stream->write32(m->vertices,
+ 3*this->numVertices*4);
+ if(m->normals)
+ stream->write32(m->normals,
+ 3*this->numVertices*4);
+ }else{
+ stream->writeI32(0);
+ stream->writeI32(0);
+ }
+ }
+
+ this->matList.streamWrite(stream);
+
+ s_plglist.streamWrite(stream, this);
+ return true;
+}
+
+uint32
+Geometry::streamGetSize(void)
+{
+ uint32 size = 0;
+ size += 12 + geoStructSize(this);
+ size += 12 + this->matList.streamGetSize();
+ size += 12 + s_plglist.streamGetSize(this);
+ return size;
+}
+
+void
+Geometry::addMorphTargets(int32 n)
+{
+ if(n == 0)
+ return;
+ n += this->numMorphTargets;
+
+ int32 sz;
+ sz = sizeof(MorphTarget);
+ if(!(this->flags & NATIVE)){
+ sz += this->numVertices*sizeof(V3d);
+ if(this->flags & NORMALS)
+ sz += this->numVertices*sizeof(V3d);
+ }
+
+ // Memory layout: MorphTarget[n]; (vertices and normals)[n]
+ MorphTarget *mts;
+ if(this->numMorphTargets){
+ mts = (MorphTarget*)rwResize(this->morphTargets, n*sz, MEMDUR_EVENT | ID_GEOMETRY);
+ this->morphTargets = mts;
+ // Since we now have more morph targets than before, move the vertex data up
+ uint8 *src = (uint8*)mts + sz*this->numMorphTargets;
+ uint8 *dst = (uint8*)mts + sz*n;
+ uint32 len = (sz-sizeof(MorphTarget))*this->numMorphTargets;
+ while(len--)
+ *--dst = *--src;
+ }else{
+ mts = (MorphTarget*)rwNew(n*sz, MEMDUR_EVENT | ID_GEOMETRY);
+ this->morphTargets = mts;
+ }
+
+ // Set up everything and initialize the bounding sphere for new morph targets
+ V3d *data = (V3d*)&mts[n];
+ for(int32 i = 0; i < n; i++){
+ mts->parent = this;
+ mts->vertices = nil;
+ mts->normals = nil;
+ if(i >= this->numMorphTargets){
+ mts->boundingSphere.center.x = 0.0f;
+ mts->boundingSphere.center.y = 0.0f;
+ mts->boundingSphere.center.z = 0.0f;
+ mts->boundingSphere.radius = 0.0f;
+ }
+ if(!(this->flags & NATIVE) && this->numVertices){
+ mts->vertices = data;
+ data += this->numVertices;
+ if(this->flags & NORMALS){
+ mts->normals = data;
+ data += this->numVertices;
+ }
+ }
+ mts++;
+ }
+ this->numMorphTargets = n;
+}
+
+void
+Geometry::calculateBoundingSphere(void)
+{
+ for(int32 i = 0; i < this->numMorphTargets; i++){
+ MorphTarget *m = &this->morphTargets[i];
+ m->boundingSphere = m->calculateBoundingSphere();
+ }
+}
+
+bool32
+Geometry::hasColoredMaterial(void)
+{
+ for(int32 i = 0; i < this->matList.numMaterials; i++)
+ if(this->matList.materials[i]->color.red != 255 ||
+ this->matList.materials[i]->color.green != 255 ||
+ this->matList.materials[i]->color.blue != 255 ||
+ this->matList.materials[i]->color.alpha != 255)
+ return 1;
+ return 0;
+}
+
+// Force allocate data, even when native flag is set
+void
+Geometry::allocateData(void)
+{
+ // Geometry data
+ // Pretty much copy pasted from ::create above
+ int32 sz = this->numTriangles*sizeof(Triangle);
+ if(this->flags & PRELIT)
+ sz += this->numVertices*sizeof(RGBA);
+ sz += this->numTexCoordSets*this->numVertices*sizeof(TexCoords);
+
+ uint8 *data = (uint8*)rwNew(sz, MEMDUR_EVENT | ID_GEOMETRY);
+ this->triangles = (Triangle*)data;
+ data += this->numTriangles*sizeof(Triangle);
+ for(int32 i = 0; i < this->numTriangles; i++)
+ this->triangles[i].matId = 0xFFFF;
+ if(this->flags & PRELIT){
+ this->colors = (RGBA*)data;
+ data += this->numVertices*sizeof(RGBA);
+ }
+ for(int32 i = 0; i < this->numTexCoordSets; i++){
+ this->texCoords[i] = (TexCoords*)data;
+ data += this->numVertices*sizeof(TexCoords);
+ }
+
+ // MorphTarget data
+ // Bounding sphere is copied by realloc.
+ sz = sizeof(MorphTarget) + this->numVertices*sizeof(V3d);
+ if(this->flags & NORMALS)
+ sz += this->numVertices*sizeof(V3d);
+
+ MorphTarget *mt = (MorphTarget*)rwResize(this->morphTargets,
+ sz*this->numMorphTargets, MEMDUR_EVENT | ID_GEOMETRY);
+ this->morphTargets = mt;
+ V3d *vdata = (V3d*)&mt[this->numMorphTargets];
+ for(int32 i = 0; i < this->numMorphTargets; i++){
+ mt->parent = this;
+ mt->vertices = nil;
+ mt->normals = nil;
+ if(this->numVertices){
+ mt->vertices = vdata;
+ vdata += this->numVertices;
+ if(this->flags & NORMALS){
+ mt->normals = vdata;
+ vdata += this->numVertices;
+ }
+ }
+ mt++;
+ }
+}
+
+static int
+isDegenerate(uint16 *idx)
+{
+ return idx[0] == idx[1] ||
+ idx[0] == idx[2] ||
+ idx[1] == idx[2];
+}
+
+// This functions assumes there is enough space allocated
+// for triangles. Use MeshHeader::guessNumTriangles() and
+// Geometry::allocateData()
+void
+Geometry::generateTriangles(int8 *adc)
+{
+ MeshHeader *header = this->meshHeader;
+ assert(header != nil);
+
+ this->numTriangles = 0;
+ Mesh *m = header->getMeshes();
+ int8 *adcbits = adc;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ if(m->numIndices < 3){
+ // shouldn't happen but it does
+ adcbits += m->numIndices;
+ m++;
+ continue;
+ }
+ if(header->flags == MeshHeader::TRISTRIP){
+ for(uint32 j = 0; j < m->numIndices-2; j++){
+ if(!(adc && adcbits[j+2]) &&
+ !isDegenerate(&m->indices[j]))
+ this->numTriangles++;
+ }
+ }else
+ this->numTriangles += m->numIndices/3;
+ adcbits += m->numIndices;
+ m++;
+ }
+
+ Triangle *tri = this->triangles;
+ m = header->getMeshes();
+ adcbits = adc;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ if(m->numIndices < 3){
+ adcbits += m->numIndices;
+ m++;
+ continue;
+ }
+ int32 matid = this->matList.findIndex(m->material);
+ if(header->flags == MeshHeader::TRISTRIP)
+ for(uint32 j = 0; j < m->numIndices-2; j++){
+ if((adc && adcbits[j+2]) ||
+ isDegenerate(&m->indices[j]))
+ continue;
+ tri->v[0] = m->indices[j+0];
+ tri->v[1] = m->indices[j+1 + (j%2)];
+ tri->v[2] = m->indices[j+2 - (j%2)];
+ tri->matId = matid;
+ tri++;
+ }
+ else
+ for(uint32 j = 0; j < m->numIndices-2; j+=3){
+ tri->v[0] = m->indices[j+0];
+ tri->v[1] = m->indices[j+1];
+ tri->v[2] = m->indices[j+2];
+ tri->matId = matid;
+ tri++;
+ }
+ adcbits += m->numIndices;
+ m++;
+ }
+}
+
+static void
+dumpMesh(Mesh *m)
+{
+ for(uint32 i = 0; i < m->numIndices-2; i++)
+// if(i % 2)
+// printf("%3d %3d %3d\n",
+// m->indices[i+1],
+// m->indices[i],
+// m->indices[i+2]);
+// else
+ printf("%d %d %d\n",
+ m->indices[i],
+ m->indices[i+1],
+ m->indices[i+2]);
+}
+
+void
+Geometry::buildMeshes(void)
+{
+ Triangle *tri;
+ Mesh *mesh;
+
+ if(this->flags & Geometry::NATIVE){
+ fprintf(stderr, "WARNING: trying Geometry::buildMeshes() on pre-instanced geometry\n");
+ return;
+ }
+
+ rwFree(this->meshHeader);
+ this->meshHeader = nil;
+ int32 numMeshes = this->matList.numMaterials;
+ if((this->flags & Geometry::TRISTRIP) == 0){
+ int32 *numIndices = rwNewT(int32, numMeshes,
+ MEMDUR_FUNCTION | ID_GEOMETRY);
+ memset(numIndices, 0, numMeshes*sizeof(int32));
+
+ // count indices per mesh
+ tri = this->triangles;
+ for(int32 i = 0; i < this->numTriangles; i++){
+ assert(tri->matId < numMeshes);
+ numIndices[tri->matId] += 3;
+ tri++;
+ }
+ // setup meshes
+ this->allocateMeshes(numMeshes, this->numTriangles*3, 0);
+ mesh = this->meshHeader->getMeshes();
+ for(int32 i = 0; i < numMeshes; i++){
+ mesh[i].material = this->matList.materials[i];
+ mesh[i].numIndices = numIndices[i];
+ }
+ this->meshHeader->setupIndices();
+ rwFree(numIndices);
+
+ // now fill in the indices
+ for(int32 i = 0; i < numMeshes; i++)
+ mesh[i].numIndices = 0;
+ tri = this->triangles;
+ for(int32 i = 0; i < this->numTriangles; i++){
+ uint32 idx = mesh[tri->matId].numIndices;
+ mesh[tri->matId].indices[idx++] = tri->v[0];
+ mesh[tri->matId].indices[idx++] = tri->v[1];
+ mesh[tri->matId].indices[idx++] = tri->v[2];
+ mesh[tri->matId].numIndices = idx;
+ tri++;
+ }
+ }else
+ this->buildTristrips();
+}
+
+/* The idea is that even in meshes where winding is not preserved
+ * every tristrip starts at an even vertex. So find the start of
+ * strips and insert duplicate vertices if necessary. */
+void
+Geometry::correctTristripWinding(void)
+{
+ MeshHeader *header = this->meshHeader;
+ if(this->flags & NATIVE || header == nil ||
+ header->flags != MeshHeader::TRISTRIP)
+ return;
+ this->meshHeader = nil;
+ // Allocate no indices, we realloc later
+ MeshHeader *newhead = this->allocateMeshes(header->numMeshes, 0, 1);
+ newhead->flags = header->flags;
+ /* get a temporary working buffer */
+ uint16 *indices = rwNewT(uint16, header->totalIndices*2,
+ MEMDUR_FUNCTION | ID_GEOMETRY);
+
+ Mesh *mesh = header->getMeshes();
+ Mesh *newmesh = newhead->getMeshes();
+ for(uint16 i = 0; i < header->numMeshes; i++){
+ newmesh->numIndices = 0;
+ newmesh->indices = &indices[newhead->totalIndices];
+ newmesh->material = mesh->material;
+
+ bool inStrip = 0;
+ uint32 j;
+ for(j = 0; j < mesh->numIndices-2; j++){
+ /* Duplicate vertices indicate end of strip */
+ if(mesh->indices[j] == mesh->indices[j+1] ||
+ mesh->indices[j+1] == mesh->indices[j+2])
+ inStrip = 0;
+ else if(!inStrip){
+ /* Entering strip now,
+ * make sure winding is correct */
+ inStrip = 1;
+ if(newmesh->numIndices % 2){
+ newmesh->indices[newmesh->numIndices] =
+ newmesh->indices[newmesh->numIndices-1];
+ newmesh->numIndices++;
+ }
+ }
+ newmesh->indices[newmesh->numIndices++] = mesh->indices[j];
+ }
+ for(; j < mesh->numIndices; j++)
+ newmesh->indices[newmesh->numIndices++] = mesh->indices[j];
+ newhead->totalIndices += newmesh->numIndices;
+
+ mesh++;
+ newmesh++;
+ }
+ rwFree(header);
+ // Now allocate indices and copy them
+ this->allocateMeshes(newhead->numMeshes, newhead->totalIndices, 0);
+ memcpy(this->meshHeader->getMeshes()->indices, indices, this->meshHeader->totalIndices*2);
+ rwFree(indices);
+}
+
+void
+Geometry::removeUnusedMaterials(void)
+{
+ if(this->meshHeader == nil)
+ return;
+ MeshHeader *mh = this->meshHeader;
+ Mesh *m = mh->getMeshes();
+ for(uint32 i = 0; i < mh->numMeshes; i++)
+ if(m[i].indices == nil)
+ return;
+
+ int32 *map = rwNewT(int32, this->matList.numMaterials,
+ MEMDUR_FUNCTION | ID_GEOMETRY);
+ Material **materials = rwNewT(Material*,this->matList.numMaterials,
+ MEMDUR_EVENT | ID_MATERIAL);
+ int32 numMaterials = 0;
+ /* Build new material list and map */
+ for(uint32 i = 0; i < mh->numMeshes; i++){
+ if(m[i].numIndices <= 0)
+ continue;
+ materials[numMaterials] = m[i].material;
+ m[i].material->addRef();
+ int32 oldid = this->matList.findIndex(m[i].material);
+ map[oldid] = numMaterials;
+ numMaterials++;
+ }
+ for(int32 i = 0; i < this->matList.numMaterials; i++)
+ this->matList.materials[i]->destroy();
+ rwFree(this->matList.materials);
+ this->matList.materials = materials;
+ this->matList.space = this->matList.numMaterials;
+ this->matList.numMaterials = numMaterials;
+
+ /* Build new meshes */
+ this->meshHeader = nil;
+ MeshHeader *newmh = this->allocateMeshes(numMaterials, mh->totalIndices, 0);
+ newmh->flags = mh->flags;
+ Mesh *newm = newmh->getMeshes();
+ for(uint32 i = 0; i < mh->numMeshes; i++){
+ if(m[i].numIndices <= 0)
+ continue;
+ newm->numIndices = m[i].numIndices;
+ newm->material = m[i].material;
+ newm++;
+ }
+ newmh->setupIndices();
+ /* Copy indices */
+ newm = newmh->getMeshes();;
+ for(uint32 i = 0; i < mh->numMeshes; i++){
+ if(m[i].numIndices <= 0)
+ continue;
+ memcpy(newm->indices, m[i].indices,
+ m[i].numIndices*sizeof(*m[i].indices));
+ newm++;
+ }
+ rwFree(mh);
+
+ /* Remap triangle material IDs */
+ for(int32 i = 0; i < this->numTriangles; i++)
+ this->triangles[i].matId = map[this->triangles[i].matId];
+ rwFree(map);
+}
+
+Sphere
+MorphTarget::calculateBoundingSphere(void) const
+{
+ Sphere sphere;
+ V3d min = { 1000000.0f, 1000000.0f, 1000000.0f };
+ V3d max = { -1000000.0f, -1000000.0f, -1000000.0f };
+ V3d *v = this->vertices;
+ for(int32 j = 0; j < this->parent->numVertices; j++){
+ if(v->x > max.x) max.x = v->x;
+ if(v->x < min.x) min.x = v->x;
+ if(v->y > max.y) max.y = v->y;
+ if(v->y < min.y) min.y = v->y;
+ if(v->z > max.z) max.z = v->z;
+ if(v->z < min.z) min.z = v->z;
+ v++;
+ }
+ sphere.center = scale(add(min, max), 1/2.0f);
+ max = sub(max, sphere.center);
+ sphere.radius = length(max);
+ return sphere;
+}
+
+
+//
+// MaterialList
+//
+#undef PLUGIN_ID
+#define PLUGIN_ID ID_MATERIAL
+
+void
+MaterialList::init(void)
+{
+ this->materials = nil;
+ this->numMaterials = 0;
+ this->space = 0;
+}
+
+void
+MaterialList::deinit(void)
+{
+ if(this->materials){
+ for(int32 i = 0; i < this->numMaterials; i++)
+ this->materials[i]->destroy();
+ rwFree(this->materials);
+ this->materials = nil;
+ }
+}
+
+int32
+MaterialList::appendMaterial(Material *mat)
+{
+ Material **ml;
+ int32 space;
+ if(this->numMaterials >= this->space){
+ space = this->space + 20;
+ if(this->materials)
+ ml = rwReallocT(Material*, this->materials, space,
+ MEMDUR_EVENT | ID_MATERIAL);
+ else
+ ml = rwMallocT(Material*, space, MEMDUR_EVENT | ID_MATERIAL);
+ if(ml == nil)
+ return -1;
+ this->space = space;
+ this->materials = ml;
+ }
+ this->materials[this->numMaterials++] = mat;
+ mat->addRef();
+ return this->numMaterials-1;
+}
+
+int32
+MaterialList::findIndex(Material *mat)
+{
+ for(int32 i = 0; i < this->numMaterials; i++)
+ if(this->materials[i] == mat)
+ return i;
+ return -1;
+}
+
+MaterialList*
+MaterialList::streamRead(Stream *stream, MaterialList *matlist)
+{
+ int32 *indices = nil;
+ int32 numMat;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ matlist->init();
+ numMat = stream->readI32();
+ if(numMat == 0)
+ return matlist;
+ matlist->materials = rwMallocT(Material*,numMat, MEMDUR_EVENT | ID_MATERIAL);
+ if(matlist->materials == nil)
+ goto fail;
+ matlist->space = numMat;
+
+ indices = (int32*)rwMalloc(numMat*4, MEMDUR_FUNCTION | ID_MATERIAL);
+ stream->read32(indices, numMat*4);
+
+ Material *m;
+ for(int32 i = 0; i < numMat; i++){
+ if(indices[i] >= 0){
+ m = matlist->materials[indices[i]];
+ m->addRef();
+ }else{
+ if(!findChunk(stream, ID_MATERIAL, nil, nil)){
+ RWERROR((ERR_CHUNK, "MATERIAL"));
+ goto fail;
+ }
+ m = Material::streamRead(stream);
+ if(m == nil)
+ goto fail;
+ }
+ matlist->appendMaterial(m);
+ m->destroy();
+ }
+ rwFree(indices);
+ return matlist;
+fail:
+ rwFree(indices);
+ matlist->deinit();
+ return nil;
+}
+
+bool
+MaterialList::streamWrite(Stream *stream)
+{
+ uint32 size = this->streamGetSize();
+ writeChunkHeader(stream, ID_MATLIST, size);
+ writeChunkHeader(stream, ID_STRUCT, 4 + this->numMaterials*4);
+ stream->writeI32(this->numMaterials);
+
+ int32 idx;
+ for(int32 i = 0; i < this->numMaterials; i++){
+ idx = -1;
+ for(int32 j = i-1; j >= 0; j--)
+ if(this->materials[i] == this->materials[j]){
+ idx = j;
+ break;
+ }
+ stream->writeI32(idx);
+ }
+ for(int32 i = 0; i < this->numMaterials; i++){
+ for(int32 j = i-1; j >= 0; j--)
+ if(this->materials[i] == this->materials[j])
+ goto found;
+ this->materials[i]->streamWrite(stream);
+ found:;
+ }
+ return true;
+}
+
+uint32
+MaterialList::streamGetSize(void)
+{
+ uint32 size = 12 + 4 + this->numMaterials*4;
+ for(int32 i = 0; i < this->numMaterials; i++){
+ for(int32 j = i-1; j >= 0; j--)
+ if(this->materials[i] == this->materials[j])
+ goto found;
+ size += 12 + this->materials[i]->streamGetSize();
+ found:;
+ }
+ return size;
+}
+
+//
+// Material
+//
+
+Material*
+Material::create(void)
+{
+ Material *mat = (Material*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_MATERIAL);
+ if(mat == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ mat->texture = nil;
+ memset(&mat->color, 0xFF, 4);
+ mat->surfaceProps = defaultSurfaceProps;
+ mat->pipeline = nil;
+ mat->refCount = 1;
+ s_plglist.construct(mat);
+ return mat;
+}
+
+Material*
+Material::clone(void)
+{
+ Material *mat = Material::create();
+ if(mat == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ mat->color = this->color;
+ mat->surfaceProps = this->surfaceProps;
+ if(this->texture)
+ mat->setTexture(this->texture);
+ mat->pipeline = this->pipeline;
+ s_plglist.copy(mat, this);
+ return mat;
+}
+
+void
+Material::destroy(void)
+{
+ this->refCount--;
+ if(this->refCount <= 0){
+ s_plglist.destruct(this);
+ if(this->texture)
+ this->texture->destroy();
+ rwFree(this);
+ numAllocated--;
+ }
+}
+
+void
+Material::setTexture(Texture *tex)
+{
+ if(this->texture)
+ this->texture->destroy();
+ if(tex)
+ tex->addRef();
+ this->texture = tex;
+}
+
+struct MatStreamData
+{
+ int32 flags; // unused according to RW
+ RGBA color;
+ int32 unused;
+ int32 textured;
+};
+
+static uint32 materialRights[2];
+
+Material*
+Material::streamRead(Stream *stream)
+{
+ uint32 length, version;
+ MatStreamData buf;
+
+ if(!findChunk(stream, ID_STRUCT, nil, &version)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ stream->read8(&buf, sizeof(buf));
+ RGBA col = buf.color;
+ memNative32(&buf, sizeof(buf));
+ buf.color = col;
+ Material *mat = Material::create();
+ if(mat == nil)
+ return nil;
+ mat->color = buf.color;
+ if(version < 0x30400)
+ mat->surfaceProps = defaultSurfaceProps;
+ else
+ stream->read32(&mat->surfaceProps, sizeof(SurfaceProperties));
+ if(buf.textured){
+ if(!findChunk(stream, ID_TEXTURE, &length, nil)){
+ RWERROR((ERR_CHUNK, "TEXTURE"));
+ goto fail;
+ }
+ mat->texture = Texture::streamRead(stream);
+ }
+
+ materialRights[0] = 0;
+ if(!s_plglist.streamRead(stream, mat))
+ goto fail;
+ if(materialRights[0])
+ s_plglist.assertRights(mat, materialRights[0], materialRights[1]);
+ return mat;
+
+fail:
+ mat->destroy();
+ return nil;
+}
+
+bool
+Material::streamWrite(Stream *stream)
+{
+ MatStreamData buf;
+
+ writeChunkHeader(stream, ID_MATERIAL, this->streamGetSize());
+ writeChunkHeader(stream, ID_STRUCT, sizeof(MatStreamData)
+ + (rw::version >= 0x30400 ? 12 : 0));
+
+ buf.color = this->color;
+ buf.flags = 0;
+ buf.unused = 0;
+ buf.textured = this->texture != nil;
+ memLittle32(&buf, sizeof(buf));
+ buf.color = this->color;
+ stream->write8(&buf, sizeof(buf));
+
+ if(rw::version >= 0x30400){
+ float32 surfaceProps[3];
+ surfaceProps[0] = this->surfaceProps.ambient;
+ surfaceProps[1] = this->surfaceProps.specular;
+ surfaceProps[2] = this->surfaceProps.diffuse;
+ stream->write32(surfaceProps, sizeof(surfaceProps));
+ }
+
+ if(this->texture)
+ this->texture->streamWrite(stream);
+
+ s_plglist.streamWrite(stream, this);
+ return true;
+}
+
+uint32
+Material::streamGetSize(void)
+{
+ uint32 size = 0;
+ size += 12 + sizeof(MatStreamData);
+ if(rw::version >= 0x30400)
+ size += 12;
+ if(this->texture)
+ size += 12 + this->texture->streamGetSize();
+ size += 12 + s_plglist.streamGetSize(this);
+ return size;
+}
+
+// Material Rights plugin
+
+static Stream*
+readMaterialRights(Stream *stream, int32, void *, int32, int32)
+{
+ stream->read32(materialRights, 8);
+// printf("materialrights: %X %X\n", materialRights[0], materialRights[1]);
+ return stream;
+}
+
+static Stream*
+writeMaterialRights(Stream *stream, int32, void *object, int32, int32)
+{
+ Material *material = (Material*)object;
+ uint32 buffer[2];
+ buffer[0] = material->pipeline->pluginID;
+ buffer[1] = material->pipeline->pluginData;
+ stream->write32(buffer, 8);
+ return stream;
+}
+
+static int32
+getSizeMaterialRights(void *object, int32, int32)
+{
+ Material *material = (Material*)object;
+ if(material->pipeline == nil || material->pipeline->pluginID == 0)
+ return 0;
+ return 8;
+}
+
+void
+registerMaterialRightsPlugin(void)
+{
+ Material::registerPlugin(0, ID_RIGHTTORENDER, nil, nil, nil);
+ Material::registerPluginStream(ID_RIGHTTORENDER,
+ readMaterialRights,
+ writeMaterialRights,
+ getSizeMaterialRights);
+}
+
+
+}
diff --git a/src/geoplg.cpp b/src/geoplg.cpp
new file mode 100644
index 0000000..c1e1356
--- /dev/null
+++ b/src/geoplg.cpp
@@ -0,0 +1,339 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwanim.h"
+#include "rwplugins.h"
+#include "ps2/rwps2.h"
+#include "ps2/rwps2plg.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+#include "gl/rwwdgl.h"
+#include "gl/rwgl3.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+
+static uint16 nextSerialNum = 1;
+
+// Mesh
+
+// Allocate a mesh header, meshes and optionally indices.
+// If existing meshes already exist, retain their information.
+MeshHeader*
+Geometry::allocateMeshes(int32 numMeshes, uint32 numIndices, bool32 noIndices)
+{
+ uint32 sz;
+ MeshHeader *mh;
+ Mesh *m;
+ uint16 *indices;
+ int32 oldNumMeshes;
+ int32 i;
+ sz = sizeof(MeshHeader) + numMeshes*sizeof(Mesh);
+ if(!noIndices)
+ sz += numIndices*sizeof(uint16);
+ if(this->meshHeader){
+ oldNumMeshes = this->meshHeader->numMeshes;
+ mh = (MeshHeader*)rwResize(this->meshHeader, sz, MEMDUR_EVENT | ID_GEOMETRY);
+ this->meshHeader = mh;
+ }else{
+ oldNumMeshes = 0;
+ mh = (MeshHeader*)rwNew(sz, MEMDUR_EVENT | ID_GEOMETRY);
+ mh->flags = 0;
+ this->meshHeader = mh;
+ }
+ mh->numMeshes = numMeshes;
+ mh->serialNum = nextSerialNum++;
+ mh->totalIndices = numIndices;
+ m = mh->getMeshes();
+ indices = (uint16*)&m[numMeshes];
+ for(i = 0; i < mh->numMeshes; i++){
+ // keep these
+ if(i >= oldNumMeshes){
+ m->material = nil;
+ m->numIndices = 0;
+ }
+ // always init indices
+ if(noIndices)
+ m->indices = nil;
+ else{
+ m->indices = indices;
+ indices += m->numIndices;
+ }
+ m++;
+ }
+ return mh;
+}
+
+void
+MeshHeader::setupIndices(void)
+{
+ int32 i;
+ uint16 *indices;
+ Mesh *m;
+ m = this->getMeshes();
+ indices = m->indices;
+ // return if native
+ if(indices == nil)
+ return;
+ for(i = 0; i < this->numMeshes; i++){
+ m->indices = indices;
+ indices += m->numIndices;
+ m++;
+ }
+}
+
+struct MeshHeaderStream
+{
+ uint32 flags;
+ uint32 numMeshes;
+ uint32 totalIndices;
+};
+
+struct MeshStream
+{
+ uint32 numIndices;
+ int32 matIndex;
+};
+
+static Stream*
+readMesh(Stream *stream, int32 len, void *object, int32, int32)
+{
+ MeshHeaderStream mhs;
+ MeshStream ms;
+ MeshHeader *mh;
+ Mesh *mesh;
+ int32 indbuf[256];
+ uint16 *indices;
+ Geometry *geo = (Geometry*)object;
+
+ stream->read32(&mhs, sizeof(MeshHeaderStream));
+ // Have to do this dance for War Drum's meshes
+ bool32 hasData = len > int32(sizeof(MeshHeaderStream)+mhs.numMeshes*sizeof(MeshStream));
+ assert(geo->meshHeader == nil);
+ geo->meshHeader = nil;
+ mh = geo->allocateMeshes(mhs.numMeshes, mhs.totalIndices,
+ geo->flags & Geometry::NATIVE && !hasData);
+ mh->flags = mhs.flags;
+
+ mesh = mh->getMeshes();
+ indices = mesh->indices;
+ for(uint32 i = 0; i < mh->numMeshes; i++){
+ stream->read32(&ms, sizeof(MeshStream));
+ mesh->numIndices = ms.numIndices;
+ mesh->material = geo->matList.materials[ms.matIndex];
+ if(geo->flags & Geometry::NATIVE){
+ // War Drum OpenGL stores uint16 indices here
+ if(hasData){
+ mesh->indices = indices;
+ indices += mesh->numIndices;
+ stream->read16(mesh->indices,
+ mesh->numIndices*2);
+ }
+ }else{
+ mesh->indices = indices;
+ indices += mesh->numIndices;
+ uint16 *ind = mesh->indices;
+ int32 numIndices = mesh->numIndices;
+ for(; numIndices > 0; numIndices -= 256){
+ int32 n = numIndices < 256 ? numIndices : 256;
+ stream->read32(indbuf, n*4);
+ for(int32 j = 0; j < n; j++)
+ ind[j] = indbuf[j];
+ ind += n;
+ }
+ }
+ mesh++;
+ }
+ return stream;
+}
+
+static Stream*
+writeMesh(Stream *stream, int32, void *object, int32, int32)
+{
+ MeshHeaderStream mhs;
+ MeshStream ms;
+ int32 indbuf[256];
+ Geometry *geo = (Geometry*)object;
+ mhs.flags = geo->meshHeader->flags;
+ mhs.numMeshes = geo->meshHeader->numMeshes;
+ mhs.totalIndices = geo->meshHeader->totalIndices;
+ stream->write32(&mhs, sizeof(MeshHeaderStream));
+ Mesh *mesh = geo->meshHeader->getMeshes();
+ for(uint32 i = 0; i < geo->meshHeader->numMeshes; i++){
+ ms.numIndices = mesh->numIndices;
+ ms.matIndex = geo->matList.findIndex(mesh->material);
+ stream->write32(&ms, sizeof(MeshStream));
+ if(geo->flags & Geometry::NATIVE){
+ assert(geo->instData != nil);
+ if(geo->instData->platform == PLATFORM_WDGL)
+ stream->write16(mesh->indices,
+ mesh->numIndices*2);
+ }else{
+ uint16 *ind = mesh->indices;
+ int32 numIndices = mesh->numIndices;
+ for(; numIndices > 0; numIndices -= 256){
+ int32 n = numIndices < 256 ? numIndices : 256;
+ for(int32 j = 0; j < n; j++)
+ indbuf[j] = ind[j];
+ stream->write32(indbuf, n*4);
+ ind += n;
+ }
+ }
+ mesh++;
+ }
+ return stream;
+}
+
+static int32
+getSizeMesh(void *object, int32, int32)
+{
+ Geometry *geo = (Geometry*)object;
+ if(geo->meshHeader == nil)
+ return -1;
+ int32 size = 12 + geo->meshHeader->numMeshes*8;
+ if(geo->flags & Geometry::NATIVE){
+ assert(geo->instData != nil);
+ if(geo->instData->platform == PLATFORM_WDGL)
+ size += geo->meshHeader->totalIndices*2;
+ }else{
+ size += geo->meshHeader->totalIndices*4;
+ }
+ return size;
+}
+
+void
+registerMeshPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_MESH, nil, nil, nil);
+ Geometry::registerPluginStream(ID_MESH, readMesh, writeMesh, getSizeMesh);
+}
+
+// Returns the maximum number of triangles. Just so
+// we can allocate enough before instancing. This does not
+// take into account degerate triangles or ADC bits as
+// we don't look at the data.
+uint32
+MeshHeader::guessNumTriangles(void)
+{
+ if(this->flags == MeshHeader::TRISTRIP)
+ return this->totalIndices - 2*this->numMeshes;
+ else
+ return this->totalIndices/3;
+}
+
+// Native Data
+
+static void*
+destroyNativeData(void *object, int32 offset, int32 size)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil)
+ return object;
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_D3D8)
+ return d3d8::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_D3D9)
+ return d3d9::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_GL3)
+ return gl3::destroyNativeData(object, offset, size);
+ return object;
+}
+
+static Stream*
+readNativeData(Stream *stream, int32 len, void *object, int32 o, int32 s)
+{
+ ChunkHeaderInfo header;
+ uint32 libid;
+ uint32 platform;
+ // ugly hack to find out platform
+ stream->seek(-4);
+ libid = stream->readU32();
+ readChunkHeaderInfo(stream, &header);
+ if(header.type == ID_STRUCT &&
+ libraryIDPack(header.version, header.build) == libid){
+ platform = stream->readU32();
+ stream->seek(-16);
+ if(platform == PLATFORM_PS2)
+ return ps2::readNativeData(stream, len, object, o, s);
+ else if(platform == PLATFORM_XBOX)
+ return xbox::readNativeData(stream, len, object, o, s);
+ else if(platform == PLATFORM_D3D8)
+ return d3d8::readNativeData(stream, len, object, o, s);
+ else if(platform == PLATFORM_D3D9)
+ return d3d9::readNativeData(stream, len, object, o, s);
+ else{
+ fprintf(stderr, "unknown platform %d\n", platform);
+ stream->seek(len);
+ }
+ }else{
+ stream->seek(-12);
+ wdgl::readNativeData(stream, len, object, o, s);
+ }
+ return stream;
+}
+
+static Stream*
+writeNativeData(Stream *stream, int32 len, void *object, int32 o, int32 s)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil)
+ return stream;
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_D3D8)
+ return d3d8::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_D3D9)
+ return d3d9::writeNativeData(stream, len, object, o, s);
+ return stream;
+}
+
+static int32
+getSizeNativeData(void *object, int32 offset, int32 size)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil)
+ return 0;
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_D3D8)
+ return d3d8::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_D3D9)
+ return d3d9::getSizeNativeData(object, offset, size);
+ return 0;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+}
diff --git a/src/gl/gl3.cpp b/src/gl/gl3.cpp
new file mode 100644
index 0000000..b876fc9
--- /dev/null
+++ b/src/gl/gl3.cpp
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+
+#include "rwgl3impl.h"
+
+namespace rw {
+namespace gl3 {
+
+// TODO: make some of these things platform-independent
+
+static void*
+driverOpen(void *o, int32, int32)
+{
+#ifdef RW_OPENGL
+ engine->driver[PLATFORM_GL3]->defaultPipeline = makeDefaultPipeline();
+#endif
+ engine->driver[PLATFORM_GL3]->rasterNativeOffset = nativeRasterOffset;
+ engine->driver[PLATFORM_GL3]->rasterCreate = rasterCreate;
+ engine->driver[PLATFORM_GL3]->rasterLock = rasterLock;
+ engine->driver[PLATFORM_GL3]->rasterUnlock = rasterUnlock;
+ engine->driver[PLATFORM_GL3]->rasterNumLevels = rasterNumLevels;
+ engine->driver[PLATFORM_GL3]->imageFindRasterFormat = imageFindRasterFormat;
+ engine->driver[PLATFORM_GL3]->rasterFromImage = rasterFromImage;
+ engine->driver[PLATFORM_GL3]->rasterToImage = rasterToImage;
+
+ return o;
+}
+
+static void*
+driverClose(void *o, int32, int32)
+{
+ return o;
+}
+
+void
+registerPlatformPlugins(void)
+{
+ Driver::registerPlugin(PLATFORM_GL3, 0, PLATFORM_GL3,
+ driverOpen, driverClose);
+ registerNativeRaster();
+}
+
+}
+}
diff --git a/src/gl/gl3device.cpp b/src/gl/gl3device.cpp
new file mode 100644
index 0000000..be7863c
--- /dev/null
+++ b/src/gl/gl3device.cpp
@@ -0,0 +1,2411 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwrender.h"
+#include "../rwengine.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#ifdef RW_OPENGL
+
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+#include "rwgl3impl.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+namespace gl3 {
+
+GlGlobals glGlobals;
+
+Gl3Caps gl3Caps;
+// terrible hack for GLES
+bool32 needToReadBackTextures;
+
+int32 alphaFunc;
+float32 alphaRef;
+
+struct UniformState
+{
+ float32 alphaRefLow;
+ float32 alphaRefHigh;
+ int32 pad[2];
+
+ float32 fogStart;
+ float32 fogEnd;
+ float32 fogRange;
+ float32 fogDisable;
+
+ RGBAf fogColor;
+};
+
+struct UniformScene
+{
+ float32 proj[16];
+ float32 view[16];
+};
+
+#define MAX_LIGHTS 8
+
+struct UniformObject
+{
+ RawMatrix world;
+ RGBAf ambLight;
+ struct {
+ float type;
+ float radius;
+ float minusCosAngle;
+ float hardSpot;
+ } lightParams[MAX_LIGHTS];
+ V4d lightPosition[MAX_LIGHTS];
+ V4d lightDirection[MAX_LIGHTS];
+ RGBAf lightColor[MAX_LIGHTS];
+};
+
+const char *shaderDecl120 =
+"#version 120\n"
+"#define GL2\n"
+"#define texture texture2D\n"
+"#define VSIN(index) attribute\n"
+"#define VSOUT varying\n"
+"#define FSIN varying\n"
+"#define FRAGCOLOR(c) (gl_FragColor = c)\n";
+const char *shaderDecl330 =
+"#version 330\n"
+"#define VSIN(index) layout(location = index) in\n"
+"#define VSOUT out\n"
+"#define FSIN in\n"
+"#define FRAGCOLOR(c) (fragColor = c)\n";
+const char *shaderDecl100es =
+"#version 100\n"
+"#define GL2\n"
+"#define texture texture2D\n"
+"#define VSIN(index) attribute\n"
+"#define VSOUT varying\n"
+"#define FSIN varying\n"
+"#define FRAGCOLOR(c) (gl_FragColor = c)\n"
+"precision highp float;\n"
+"precision highp int;\n";
+const char *shaderDecl310es =
+"#version 310 es\n"
+"#define VSIN(index) layout(location = index) in\n"
+"#define VSOUT out\n"
+"#define FSIN in\n"
+"#define FRAGCOLOR(c) (fragColor = c)\n"
+"precision highp float;\n"
+"precision highp int;\n";
+
+const char *shaderDecl;
+
+// this needs a define in the shaders as well!
+//#define RW_GL_USE_UBOS
+
+static GLuint vao;
+#ifdef RW_GL_USE_UBOS
+static GLuint ubo_state, ubo_scene, ubo_object;
+#endif
+static GLuint whitetex;
+static UniformState uniformState;
+static UniformScene uniformScene;
+static UniformObject uniformObject;
+
+#ifndef RW_GL_USE_UBOS
+// State
+int32 u_alphaRef;
+int32 u_fogData;
+int32 u_fogColor;
+
+// Scene
+int32 u_proj;
+int32 u_view;
+
+// Object
+int32 u_world;
+int32 u_ambLight;
+int32 u_lightParams;
+int32 u_lightPosition;
+int32 u_lightDirection;
+int32 u_lightColor;
+#endif
+
+int32 u_matColor;
+int32 u_surfProps;
+
+Shader *defaultShader, *defaultShader_noAT;
+Shader *defaultShader_fullLight, *defaultShader_fullLight_noAT;
+
+static bool32 stateDirty = 1;
+static bool32 sceneDirty = 1;
+static bool32 objectDirty = 1;
+
+struct RwRasterStateCache {
+ Raster *raster;
+ Texture::Addressing addressingU;
+ Texture::Addressing addressingV;
+ Texture::FilterMode filter;
+};
+
+#define MAXNUMSTAGES 8
+
+// cached RW render states
+struct RwStateCache {
+ bool32 vertexAlpha;
+ uint32 alphaTestEnable;
+ uint32 alphaFunc;
+ bool32 textureAlpha;
+ bool32 blendEnable;
+ uint32 srcblend, destblend;
+ uint32 zwrite;
+ uint32 ztest;
+ uint32 cullmode;
+ uint32 stencilenable;
+ uint32 stencilpass;
+ uint32 stencilfail;
+ uint32 stencilzfail;
+ uint32 stencilfunc;
+ uint32 stencilref;
+ uint32 stencilmask;
+ uint32 stencilwritemask;
+ uint32 fogEnable;
+ float32 fogStart;
+ float32 fogEnd;
+
+ // emulation of PS2 GS
+ bool32 gsalpha;
+ uint32 gsalpharef;
+
+ RwRasterStateCache texstage[MAXNUMSTAGES];
+};
+static RwStateCache rwStateCache;
+
+enum
+{
+ // actual gl states
+ RWGL_BLEND,
+ RWGL_SRCBLEND,
+ RWGL_DESTBLEND,
+ RWGL_DEPTHTEST,
+ RWGL_DEPTHFUNC,
+ RWGL_DEPTHMASK,
+ RWGL_CULL,
+ RWGL_CULLFACE,
+ RWGL_STENCIL,
+ RWGL_STENCILFUNC,
+ RWGL_STENCILFAIL,
+ RWGL_STENCILZFAIL,
+ RWGL_STENCILPASS,
+ RWGL_STENCILREF,
+ RWGL_STENCILMASK,
+ RWGL_STENCILWRITEMASK,
+
+ // uniforms
+ RWGL_ALPHAFUNC,
+ RWGL_ALPHAREF,
+ RWGL_FOG,
+ RWGL_FOGSTART,
+ RWGL_FOGEND,
+ RWGL_FOGCOLOR,
+
+ RWGL_NUM_STATES
+};
+static bool uniformStateDirty[RWGL_NUM_STATES];
+
+struct GlState {
+ bool32 blendEnable;
+ uint32 srcblend, destblend;
+
+ bool32 depthTest;
+ uint32 depthFunc;
+
+ uint32 depthMask;
+
+ bool32 cullEnable;
+ uint32 cullFace;
+
+ bool32 stencilEnable;
+ // glStencilFunc
+ uint32 stencilFunc;
+ uint32 stencilRef;
+ uint32 stencilMask;
+ // glStencilOp
+ uint32 stencilPass;
+ uint32 stencilFail;
+ uint32 stencilZFail;
+ // glStencilMask
+ uint32 stencilWriteMask;
+};
+static GlState curGlState, oldGlState;
+
+static int32 activeTexture;
+static uint32 boundTexture[MAXNUMSTAGES];
+
+static uint32 currentFramebuffer;
+
+static uint32 blendMap[] = {
+ GL_ZERO, // actually invalid
+ GL_ZERO,
+ GL_ONE,
+ GL_SRC_COLOR,
+ GL_ONE_MINUS_SRC_COLOR,
+ GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA,
+ GL_DST_ALPHA,
+ GL_ONE_MINUS_DST_ALPHA,
+ GL_DST_COLOR,
+ GL_ONE_MINUS_DST_COLOR,
+ GL_SRC_ALPHA_SATURATE,
+};
+
+static uint32 stencilOpMap[] = {
+ GL_KEEP, // actually invalid
+ GL_KEEP,
+ GL_ZERO,
+ GL_REPLACE,
+ GL_INCR,
+ GL_DECR,
+ GL_INVERT,
+ GL_INCR_WRAP,
+ GL_DECR_WRAP
+};
+
+static uint32 stencilFuncMap[] = {
+ GL_NEVER, // actually invalid
+ GL_NEVER,
+ GL_LESS,
+ GL_EQUAL,
+ GL_LEQUAL,
+ GL_GREATER,
+ GL_NOTEQUAL,
+ GL_GEQUAL,
+ GL_ALWAYS
+};
+
+static float maxAnisotropy;
+
+/*
+ * GL state cache
+ */
+
+void
+setGlRenderState(uint32 state, uint32 value)
+{
+ switch(state){
+ case RWGL_BLEND: curGlState.blendEnable = value; break;
+ case RWGL_SRCBLEND: curGlState.srcblend = value; break;
+ case RWGL_DESTBLEND: curGlState.destblend = value; break;
+ case RWGL_DEPTHTEST: curGlState.depthTest = value; break;
+ case RWGL_DEPTHFUNC: curGlState.depthFunc = value; break;
+ case RWGL_DEPTHMASK: curGlState.depthMask = value; break;
+ case RWGL_CULL: curGlState.cullEnable = value; break;
+ case RWGL_CULLFACE: curGlState.cullFace = value; break;
+ case RWGL_STENCIL: curGlState.stencilEnable = value; break;
+ case RWGL_STENCILFUNC: curGlState.stencilFunc = value; break;
+ case RWGL_STENCILFAIL: curGlState.stencilFail = value; break;
+ case RWGL_STENCILZFAIL: curGlState.stencilZFail = value; break;
+ case RWGL_STENCILPASS: curGlState.stencilPass = value; break;
+ case RWGL_STENCILREF: curGlState.stencilRef = value; break;
+ case RWGL_STENCILMASK: curGlState.stencilMask = value; break;
+ case RWGL_STENCILWRITEMASK: curGlState.stencilWriteMask = value; break;
+ }
+}
+
+void
+flushGlRenderState(void)
+{
+ if(oldGlState.blendEnable != curGlState.blendEnable){
+ oldGlState.blendEnable = curGlState.blendEnable;
+ (oldGlState.blendEnable ? glEnable : glDisable)(GL_BLEND);
+ }
+
+ if(oldGlState.srcblend != curGlState.srcblend ||
+ oldGlState.destblend != curGlState.destblend){
+ oldGlState.srcblend = curGlState.srcblend;
+ oldGlState.destblend = curGlState.destblend;
+ glBlendFunc(oldGlState.srcblend, oldGlState.destblend);
+ }
+
+ if(oldGlState.depthTest != curGlState.depthTest){
+ oldGlState.depthTest = curGlState.depthTest;
+ (oldGlState.depthTest ? glEnable : glDisable)(GL_DEPTH_TEST);
+ }
+ if(oldGlState.depthFunc != curGlState.depthFunc){
+ oldGlState.depthFunc = curGlState.depthFunc;
+ glDepthFunc(oldGlState.depthFunc);
+ }
+ if(oldGlState.depthMask != curGlState.depthMask){
+ oldGlState.depthMask = curGlState.depthMask;
+ glDepthMask(oldGlState.depthMask);
+ }
+
+ if(oldGlState.stencilEnable != curGlState.stencilEnable){
+ oldGlState.stencilEnable = curGlState.stencilEnable;
+ (oldGlState.stencilEnable ? glEnable : glDisable)(GL_STENCIL_TEST);
+ }
+ if(oldGlState.stencilFunc != curGlState.stencilFunc ||
+ oldGlState.stencilRef != curGlState.stencilRef ||
+ oldGlState.stencilMask != curGlState.stencilMask){
+ oldGlState.stencilFunc = curGlState.stencilFunc;
+ oldGlState.stencilRef = curGlState.stencilRef;
+ oldGlState.stencilMask = curGlState.stencilMask;
+ glStencilFunc(oldGlState.stencilFunc, oldGlState.stencilRef, oldGlState.stencilMask);
+ }
+ if(oldGlState.stencilPass != curGlState.stencilPass ||
+ oldGlState.stencilFail != curGlState.stencilFail ||
+ oldGlState.stencilZFail != curGlState.stencilZFail){
+ oldGlState.stencilPass = curGlState.stencilPass;
+ oldGlState.stencilFail = curGlState.stencilFail;
+ oldGlState.stencilZFail = curGlState.stencilZFail;
+ glStencilOp(oldGlState.stencilFail, oldGlState.stencilZFail, oldGlState.stencilPass);
+ }
+ if(oldGlState.stencilWriteMask != curGlState.stencilWriteMask){
+ oldGlState.stencilWriteMask = curGlState.stencilWriteMask;
+ glStencilMask(oldGlState.stencilWriteMask);
+ }
+
+ if(oldGlState.cullEnable != curGlState.cullEnable){
+ oldGlState.cullEnable = curGlState.cullEnable;
+ (oldGlState.cullEnable ? glEnable : glDisable)(GL_CULL_FACE);
+ }
+ if(oldGlState.cullFace != curGlState.cullFace){
+ oldGlState.cullFace = curGlState.cullFace;
+ glCullFace(oldGlState.cullFace);
+ }
+}
+
+
+
+void
+setAlphaBlend(bool32 enable)
+{
+ if(rwStateCache.blendEnable != enable){
+ rwStateCache.blendEnable = enable;
+ setGlRenderState(RWGL_BLEND, enable);
+ }
+}
+
+bool32
+getAlphaBlend(void)
+{
+ return rwStateCache.blendEnable;
+}
+
+bool32 getAlphaTest(void) { return rwStateCache.alphaTestEnable; }
+
+static void
+setDepthTest(bool32 enable)
+{
+ if(rwStateCache.ztest != enable){
+ rwStateCache.ztest = enable;
+ if(rwStateCache.zwrite && !enable){
+ // If we still want to write, enable but set mode to always
+ setGlRenderState(RWGL_DEPTHTEST, true);
+ setGlRenderState(RWGL_DEPTHFUNC, GL_ALWAYS);
+ }else{
+ setGlRenderState(RWGL_DEPTHTEST, rwStateCache.ztest);
+ setGlRenderState(RWGL_DEPTHFUNC, GL_LEQUAL);
+ }
+ }
+}
+
+static void
+setDepthWrite(bool32 enable)
+{
+ enable = enable ? GL_TRUE : GL_FALSE;
+ if(rwStateCache.zwrite != enable){
+ rwStateCache.zwrite = enable;
+ if(enable && !rwStateCache.ztest){
+ // Have to switch on ztest so writing can work
+ setGlRenderState(RWGL_DEPTHTEST, true);
+ setGlRenderState(RWGL_DEPTHFUNC, GL_ALWAYS);
+ }
+ setGlRenderState(RWGL_DEPTHMASK, rwStateCache.zwrite);
+ }
+}
+
+static void
+setAlphaTest(bool32 enable)
+{
+ uint32 shaderfunc;
+ if(rwStateCache.alphaTestEnable != enable){
+ rwStateCache.alphaTestEnable = enable;
+ shaderfunc = rwStateCache.alphaTestEnable ? rwStateCache.alphaFunc : ALPHAALWAYS;
+ if(alphaFunc != shaderfunc){
+ alphaFunc = shaderfunc;
+ uniformStateDirty[RWGL_ALPHAFUNC] = true;
+ stateDirty = 1;
+ }
+ }
+}
+
+static void
+setAlphaTestFunction(uint32 function)
+{
+ uint32 shaderfunc;
+ if(rwStateCache.alphaFunc != function){
+ rwStateCache.alphaFunc = function;
+ shaderfunc = rwStateCache.alphaTestEnable ? rwStateCache.alphaFunc : ALPHAALWAYS;
+ if(alphaFunc != shaderfunc){
+ alphaFunc = shaderfunc;
+ uniformStateDirty[RWGL_ALPHAFUNC] = true;
+ stateDirty = 1;
+ }
+ }
+}
+
+static void
+setVertexAlpha(bool32 enable)
+{
+ if(rwStateCache.vertexAlpha != enable){
+ if(!rwStateCache.textureAlpha){
+ setAlphaBlend(enable);
+ setAlphaTest(enable);
+ }
+ rwStateCache.vertexAlpha = enable;
+ }
+}
+
+static void
+setActiveTexture(int32 n)
+{
+ if(activeTexture != n){
+ activeTexture = n;
+ glActiveTexture(GL_TEXTURE0+n);
+ }
+}
+
+uint32
+bindTexture(uint32 texid)
+{
+ uint32 prev = boundTexture[activeTexture];
+ if(prev != texid){
+ boundTexture[activeTexture] = texid;
+ glBindTexture(GL_TEXTURE_2D, texid);
+ }
+ return prev;
+}
+
+void
+bindFramebuffer(uint32 fbo)
+{
+ if(currentFramebuffer != fbo){
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ currentFramebuffer = fbo;
+ }
+}
+
+static GLint filterConvMap_NoMIP[] = {
+ 0, GL_NEAREST, GL_LINEAR,
+ GL_NEAREST, GL_LINEAR,
+ GL_NEAREST, GL_LINEAR
+};
+static GLint filterConvMap_MIP[] = {
+ 0, GL_NEAREST, GL_LINEAR,
+ GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST,
+ GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR
+};
+
+static GLint addressConvMap[] = {
+ 0, GL_REPEAT, GL_MIRRORED_REPEAT,
+ GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER
+};
+
+static void
+setFilterMode(uint32 stage, int32 filter, int32 maxAniso = 1)
+{
+ if(rwStateCache.texstage[stage].filter != (Texture::FilterMode)filter){
+ rwStateCache.texstage[stage].filter = (Texture::FilterMode)filter;
+ Raster *raster = rwStateCache.texstage[stage].raster;
+ if(raster){
+ Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, rwStateCache.texstage[stage].raster, nativeRasterOffset);
+ if(natras->filterMode != filter){
+ setActiveTexture(stage);
+ if(natras->autogenMipmap || natras->numLevels > 1){
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_MIP[filter]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_NoMIP[filter]);
+ }else{
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_NoMIP[filter]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_NoMIP[filter]);
+ }
+ natras->filterMode = filter;
+ }
+ if(natras->maxAnisotropy != maxAniso){
+ setActiveTexture(stage);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)maxAniso);
+ natras->maxAnisotropy = maxAniso;
+ }
+ }
+ }
+}
+
+static void
+setAddressU(uint32 stage, int32 addressing)
+{
+ if(rwStateCache.texstage[stage].addressingU != (Texture::Addressing)addressing){
+ rwStateCache.texstage[stage].addressingU = (Texture::Addressing)addressing;
+ Raster *raster = rwStateCache.texstage[stage].raster;
+ if(raster){
+ Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+ if(natras->addressU == addressing){
+ setActiveTexture(stage);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, addressConvMap[addressing]);
+ natras->addressU = addressing;
+ }
+ }
+ }
+}
+
+static void
+setAddressV(uint32 stage, int32 addressing)
+{
+ if(rwStateCache.texstage[stage].addressingV != (Texture::Addressing)addressing){
+ rwStateCache.texstage[stage].addressingV = (Texture::Addressing)addressing;
+ Raster *raster = rwStateCache.texstage[stage].raster;
+ if(raster){
+ Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, rwStateCache.texstage[stage].raster, nativeRasterOffset);
+ if(natras->addressV == addressing){
+ setActiveTexture(stage);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, addressConvMap[addressing]);
+ natras->addressV = addressing;
+ }
+ }
+ }
+}
+
+static void
+setRasterStageOnly(uint32 stage, Raster *raster)
+{
+ bool32 alpha;
+ if(raster != rwStateCache.texstage[stage].raster){
+ rwStateCache.texstage[stage].raster = raster;
+ setActiveTexture(stage);
+ if(raster){
+ assert(raster->platform == PLATFORM_GL3);
+ Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+ bindTexture(natras->texid);
+
+ rwStateCache.texstage[stage].filter = (rw::Texture::FilterMode)natras->filterMode;
+ rwStateCache.texstage[stage].addressingU = (rw::Texture::Addressing)natras->addressU;
+ rwStateCache.texstage[stage].addressingV = (rw::Texture::Addressing)natras->addressV;
+
+ alpha = natras->hasAlpha;
+ }else{
+ bindTexture(whitetex);
+ alpha = 0;
+ }
+
+ if(stage == 0){
+ if(alpha != rwStateCache.textureAlpha){
+ rwStateCache.textureAlpha = alpha;
+ if(!rwStateCache.vertexAlpha){
+ setAlphaBlend(alpha);
+ setAlphaTest(alpha);
+ }
+ }
+ }
+ }
+}
+
+static void
+setRasterStage(uint32 stage, Raster *raster)
+{
+ bool32 alpha;
+ if(raster != rwStateCache.texstage[stage].raster){
+ rwStateCache.texstage[stage].raster = raster;
+ setActiveTexture(stage);
+ if(raster){
+ assert(raster->platform == PLATFORM_GL3);
+ Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, raster, nativeRasterOffset);
+ bindTexture(natras->texid);
+ uint32 filter = rwStateCache.texstage[stage].filter;
+ uint32 addrU = rwStateCache.texstage[stage].addressingU;
+ uint32 addrV = rwStateCache.texstage[stage].addressingV;
+ if(natras->filterMode != filter){
+ if(natras->autogenMipmap || natras->numLevels > 1){
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_MIP[filter]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_NoMIP[filter]);
+ }else{
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterConvMap_NoMIP[filter]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterConvMap_NoMIP[filter]);
+ }
+ natras->filterMode = filter;
+ }
+ if(natras->addressU != addrU){
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, addressConvMap[addrU]);
+ natras->addressU = addrU;
+ }
+ if(natras->addressV != addrV){
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, addressConvMap[addrV]);
+ natras->addressV = addrV;
+ }
+ alpha = natras->hasAlpha;
+ }else{
+ bindTexture(whitetex);
+ alpha = 0;
+ }
+
+ if(stage == 0){
+ if(alpha != rwStateCache.textureAlpha){
+ rwStateCache.textureAlpha = alpha;
+ if(!rwStateCache.vertexAlpha){
+ setAlphaBlend(alpha);
+ setAlphaTest(alpha);
+ }
+ }
+ }
+ }
+}
+
+void
+evictRaster(Raster *raster)
+{
+ int i;
+ for(i = 0; i < MAXNUMSTAGES; i++){
+ //assert(rwStateCache.texstage[i].raster != raster);
+ if(rwStateCache.texstage[i].raster != raster)
+ continue;
+ setRasterStage(i, nil);
+ }
+}
+
+void
+setTexture(int32 stage, Texture *tex)
+{
+ if(tex == nil || tex->raster == nil){
+ setRasterStage(stage, nil);
+ return;
+ }
+ setRasterStageOnly(stage, tex->raster);
+ setFilterMode(stage, tex->getFilter(), tex->getMaxAnisotropy());
+ setAddressU(stage, tex->getAddressU());
+ setAddressV(stage, tex->getAddressV());
+}
+
+static void
+setRenderState(int32 state, void *pvalue)
+{
+ uint32 value = (uint32)(uintptr)pvalue;
+ switch(state){
+ case TEXTURERASTER:
+ setRasterStage(0, (Raster*)pvalue);
+ break;
+ case TEXTUREADDRESS:
+ setAddressU(0, value);
+ setAddressV(0, value);
+ break;
+ case TEXTUREADDRESSU:
+ setAddressU(0, value);
+ break;
+ case TEXTUREADDRESSV:
+ setAddressV(0, value);
+ break;
+ case TEXTUREFILTER:
+ setFilterMode(0, value);
+ break;
+ case VERTEXALPHA:
+ setVertexAlpha(value);
+ break;
+ case SRCBLEND:
+ if(rwStateCache.srcblend != value){
+ rwStateCache.srcblend = value;
+ setGlRenderState(RWGL_SRCBLEND, blendMap[rwStateCache.srcblend]);
+ }
+ break;
+ case DESTBLEND:
+ if(rwStateCache.destblend != value){
+ rwStateCache.destblend = value;
+ setGlRenderState(RWGL_DESTBLEND, blendMap[rwStateCache.destblend]);
+ }
+ break;
+ case ZTESTENABLE:
+ setDepthTest(value);
+ break;
+ case ZWRITEENABLE:
+ setDepthWrite(value);
+ break;
+ case FOGENABLE:
+ if(rwStateCache.fogEnable != value){
+ rwStateCache.fogEnable = value;
+ uniformStateDirty[RWGL_FOG] = true;
+ stateDirty = 1;
+ }
+ break;
+ case FOGCOLOR:
+ // no cache check here...too lazy
+ RGBA c;
+ c.red = value;
+ c.green = value>>8;
+ c.blue = value>>16;
+ c.alpha = value>>24;
+ convColor(&uniformState.fogColor, &c);
+ uniformStateDirty[RWGL_FOGCOLOR] = true;
+ stateDirty = 1;
+ break;
+ case CULLMODE:
+ if(rwStateCache.cullmode != value){
+ rwStateCache.cullmode = value;
+ if(rwStateCache.cullmode == CULLNONE)
+ setGlRenderState(RWGL_CULL, false);
+ else{
+ setGlRenderState(RWGL_CULL, true);
+ setGlRenderState(RWGL_CULLFACE, rwStateCache.cullmode == CULLBACK ? GL_BACK : GL_FRONT);
+ }
+ }
+ break;
+
+ case STENCILENABLE:
+ if(rwStateCache.stencilenable != value){
+ rwStateCache.stencilenable = value;
+ setGlRenderState(RWGL_STENCIL, value);
+ }
+ break;
+ case STENCILFAIL:
+ if(rwStateCache.stencilfail != value){
+ rwStateCache.stencilfail = value;
+ setGlRenderState(RWGL_STENCILFAIL, stencilOpMap[value]);
+ }
+ break;
+ case STENCILZFAIL:
+ if(rwStateCache.stencilzfail != value){
+ rwStateCache.stencilzfail = value;
+ setGlRenderState(RWGL_STENCILZFAIL, stencilOpMap[value]);
+ }
+ break;
+ case STENCILPASS:
+ if(rwStateCache.stencilpass != value){
+ rwStateCache.stencilpass = value;
+ setGlRenderState(RWGL_STENCILPASS, stencilOpMap[value]);
+ }
+ break;
+ case STENCILFUNCTION:
+ if(rwStateCache.stencilfunc != value){
+ rwStateCache.stencilfunc = value;
+ setGlRenderState(RWGL_STENCILFUNC, stencilFuncMap[value]);
+ }
+ break;
+ case STENCILFUNCTIONREF:
+ if(rwStateCache.stencilref != value){
+ rwStateCache.stencilref = value;
+ setGlRenderState(RWGL_STENCILREF, value);
+ }
+ break;
+ case STENCILFUNCTIONMASK:
+ if(rwStateCache.stencilmask != value){
+ rwStateCache.stencilmask = value;
+ setGlRenderState(RWGL_STENCILMASK, value);
+ }
+ break;
+ case STENCILFUNCTIONWRITEMASK:
+ if(rwStateCache.stencilwritemask != value){
+ rwStateCache.stencilwritemask = value;
+ setGlRenderState(RWGL_STENCILWRITEMASK, value);
+ }
+ break;
+
+ case ALPHATESTFUNC:
+ setAlphaTestFunction(value);
+ break;
+ case ALPHATESTREF:
+ if(alphaRef != value/255.0f){
+ alphaRef = value/255.0f;
+ uniformStateDirty[RWGL_ALPHAREF] = true;
+ stateDirty = 1;
+ }
+ break;
+ case GSALPHATEST:
+ rwStateCache.gsalpha = value;
+ break;
+ case GSALPHATESTREF:
+ rwStateCache.gsalpharef = value;
+ }
+}
+
+static void*
+getRenderState(int32 state)
+{
+ uint32 val;
+ RGBA rgba;
+ switch(state){
+ case TEXTURERASTER:
+ return rwStateCache.texstage[0].raster;
+ case TEXTUREADDRESS:
+ if(rwStateCache.texstage[0].addressingU == rwStateCache.texstage[0].addressingV)
+ val = rwStateCache.texstage[0].addressingU;
+ else
+ val = 0; // invalid
+ break;
+ case TEXTUREADDRESSU:
+ val = rwStateCache.texstage[0].addressingU;
+ break;
+ case TEXTUREADDRESSV:
+ val = rwStateCache.texstage[0].addressingV;
+ break;
+ case TEXTUREFILTER:
+ val = rwStateCache.texstage[0].filter;
+ break;
+
+ case VERTEXALPHA:
+ val = rwStateCache.vertexAlpha;
+ break;
+ case SRCBLEND:
+ val = rwStateCache.srcblend;
+ break;
+ case DESTBLEND:
+ val = rwStateCache.destblend;
+ break;
+ case ZTESTENABLE:
+ val = rwStateCache.ztest;
+ break;
+ case ZWRITEENABLE:
+ val = rwStateCache.zwrite;
+ break;
+ case FOGENABLE:
+ val = rwStateCache.fogEnable;
+ break;
+ case FOGCOLOR:
+ convColor(&rgba, &uniformState.fogColor);
+ val = RWRGBAINT(rgba.red, rgba.green, rgba.blue, rgba.alpha);
+ break;
+ case CULLMODE:
+ val = rwStateCache.cullmode;
+ break;
+
+ case STENCILENABLE:
+ val = rwStateCache.stencilenable;
+ break;
+ case STENCILFAIL:
+ val = rwStateCache.stencilfail;
+ break;
+ case STENCILZFAIL:
+ val = rwStateCache.stencilzfail;
+ break;
+ case STENCILPASS:
+ val = rwStateCache.stencilpass;
+ break;
+ case STENCILFUNCTION:
+ val = rwStateCache.stencilfunc;
+ break;
+ case STENCILFUNCTIONREF:
+ val = rwStateCache.stencilref;
+ break;
+ case STENCILFUNCTIONMASK:
+ val = rwStateCache.stencilmask;
+ break;
+ case STENCILFUNCTIONWRITEMASK:
+ val = rwStateCache.stencilwritemask;
+ break;
+
+ case ALPHATESTFUNC:
+ val = rwStateCache.alphaFunc;
+ break;
+ case ALPHATESTREF:
+ val = (uint32)(alphaRef*255.0f);
+ break;
+ case GSALPHATEST:
+ val = rwStateCache.gsalpha;
+ break;
+ case GSALPHATESTREF:
+ val = rwStateCache.gsalpharef;
+ break;
+ default:
+ val = 0;
+ }
+ return (void*)(uintptr)val;
+}
+
+static void
+resetRenderState(void)
+{
+ rwStateCache.alphaFunc = ALPHAGREATEREQUAL;
+ alphaFunc = 0;
+ alphaRef = 10.0f/255.0f;
+ uniformStateDirty[RWGL_ALPHAREF] = true;
+ uniformState.fogDisable = 1.0f;
+ uniformState.fogStart = 0.0f;
+ uniformState.fogEnd = 0.0f;
+ uniformState.fogRange = 0.0f;
+ uniformState.fogColor = { 1.0f, 1.0f, 1.0f, 1.0f };
+ rwStateCache.gsalpha = 0;
+ rwStateCache.gsalpharef = 128;
+ stateDirty = 1;
+
+ rwStateCache.vertexAlpha = 0;
+ rwStateCache.textureAlpha = 0;
+ rwStateCache.alphaTestEnable = 0;
+
+ memset(&oldGlState, 0xFE, sizeof(oldGlState));
+
+ rwStateCache.blendEnable = 0;
+ setGlRenderState(RWGL_BLEND, false);
+ rwStateCache.srcblend = BLENDSRCALPHA;
+ rwStateCache.destblend = BLENDINVSRCALPHA;
+ setGlRenderState(RWGL_SRCBLEND, blendMap[rwStateCache.srcblend]);
+ setGlRenderState(RWGL_DESTBLEND, blendMap[rwStateCache.destblend]);
+
+ rwStateCache.zwrite = GL_TRUE;
+ setGlRenderState(RWGL_DEPTHMASK, rwStateCache.zwrite);
+
+ rwStateCache.ztest = 1;
+ setGlRenderState(RWGL_DEPTHTEST, true);
+ setGlRenderState(RWGL_DEPTHFUNC, GL_LEQUAL);
+
+ rwStateCache.cullmode = CULLNONE;
+ setGlRenderState(RWGL_CULL, false);
+ setGlRenderState(RWGL_CULLFACE, GL_BACK);
+
+ rwStateCache.stencilenable = 0;
+ setGlRenderState(RWGL_STENCIL, GL_FALSE);
+ rwStateCache.stencilfail = STENCILKEEP;
+ setGlRenderState(RWGL_STENCILFAIL, GL_KEEP);
+ rwStateCache.stencilzfail = STENCILKEEP;
+ setGlRenderState(RWGL_STENCILZFAIL, GL_KEEP);
+ rwStateCache.stencilpass = STENCILKEEP;
+ setGlRenderState(RWGL_STENCILPASS, GL_KEEP);
+ rwStateCache.stencilfunc = STENCILALWAYS;
+ setGlRenderState(RWGL_STENCILFUNC, GL_ALWAYS);
+ rwStateCache.stencilref = 0;
+ setGlRenderState(RWGL_STENCILREF, 0);
+ rwStateCache.stencilmask = 0xFFFFFFFF;
+ setGlRenderState(RWGL_STENCILMASK, 0xFFFFFFFF);
+ rwStateCache.stencilwritemask = 0xFFFFFFFF;
+ setGlRenderState(RWGL_STENCILWRITEMASK, 0xFFFFFFFF);
+
+ activeTexture = -1;
+ for(int i = 0; i < MAXNUMSTAGES; i++){
+ setActiveTexture(i);
+ bindTexture(whitetex);
+ }
+ setActiveTexture(0);
+}
+
+void
+setWorldMatrix(Matrix *mat)
+{
+ convMatrix(&uniformObject.world, mat);
+ setUniform(u_world, &uniformObject.world);
+ objectDirty = 1;
+}
+
+int32
+setLights(WorldLights *lightData)
+{
+ int i, n;
+ Light *l;
+ int32 bits;
+
+ uniformObject.ambLight = lightData->ambient;
+
+ bits = 0;
+
+ if(lightData->numAmbients)
+ bits |= VSLIGHT_AMBIENT;
+
+ n = 0;
+ for(i = 0; i < lightData->numDirectionals && i < 8; i++){
+ l = lightData->directionals[i];
+ uniformObject.lightParams[n].type = 1.0f;
+ uniformObject.lightColor[n] = l->color;
+ memcpy(&uniformObject.lightDirection[n], &l->getFrame()->getLTM()->at, sizeof(V3d));
+ bits |= VSLIGHT_DIRECT;
+ n++;
+ if(n >= MAX_LIGHTS)
+ goto out;
+ }
+
+ for(i = 0; i < lightData->numLocals; i++){
+ Light *l = lightData->locals[i];
+
+ switch(l->getType()){
+ case Light::POINT:
+ uniformObject.lightParams[n].type = 2.0f;
+ uniformObject.lightParams[n].radius = l->radius;
+ uniformObject.lightColor[n] = l->color;
+ memcpy(&uniformObject.lightPosition[n], &l->getFrame()->getLTM()->pos, sizeof(V3d));
+ bits |= VSLIGHT_POINT;
+ n++;
+ if(n >= MAX_LIGHTS)
+ goto out;
+ break;
+ case Light::SPOT:
+ case Light::SOFTSPOT:
+ uniformObject.lightParams[n].type = 3.0f;
+ uniformObject.lightParams[n].minusCosAngle = l->minusCosAngle;
+ uniformObject.lightParams[n].radius = l->radius;
+ uniformObject.lightColor[n] = l->color;
+ memcpy(&uniformObject.lightPosition[n], &l->getFrame()->getLTM()->pos, sizeof(V3d));
+ memcpy(&uniformObject.lightDirection[n], &l->getFrame()->getLTM()->at, sizeof(V3d));
+ // lower bound of falloff
+ if(l->getType() == Light::SOFTSPOT)
+ uniformObject.lightParams[n].hardSpot = 0.0f;
+ else
+ uniformObject.lightParams[n].hardSpot = 1.0f;
+ bits |= VSLIGHT_SPOT;
+ n++;
+ if(n >= MAX_LIGHTS)
+ goto out;
+ break;
+ }
+ }
+
+ uniformObject.lightParams[n].type = 0.0f;
+
+ setUniform(u_ambLight, &uniformObject.ambLight);
+ setUniform(u_lightParams, uniformObject.lightParams);
+ setUniform(u_lightPosition, uniformObject.lightPosition);
+ setUniform(u_lightDirection, uniformObject.lightDirection);
+ setUniform(u_lightColor, uniformObject.lightColor);
+out:
+ objectDirty = 1;
+ return bits;
+}
+
+void
+setProjectionMatrix(float32 *mat)
+{
+ memcpy(&uniformScene.proj, mat, 64);
+ setUniform(u_proj, uniformScene.proj);
+ sceneDirty = 1;
+}
+
+void
+setViewMatrix(float32 *mat)
+{
+ memcpy(&uniformScene.view, mat, 64);
+ setUniform(u_view, uniformScene.view);
+ sceneDirty = 1;
+}
+
+Shader *lastShaderUploaded;
+
+void
+setMaterial(const RGBA &color, const SurfaceProperties &surfaceprops, float extraSurfProp)
+{
+ rw::RGBAf col;
+ convColor(&col, &color);
+ setUniform(u_matColor, &col);
+
+ float surfProps[4];
+ surfProps[0] = surfaceprops.ambient;
+ surfProps[1] = surfaceprops.specular;
+ surfProps[2] = surfaceprops.diffuse;
+ surfProps[3] = extraSurfProp;
+ setUniform(u_surfProps, surfProps);
+}
+
+void
+flushCache(void)
+{
+ flushGlRenderState();
+
+#ifndef RW_GL_USE_UBOS
+
+ // what's this doing here??
+ uniformState.fogDisable = rwStateCache.fogEnable ? 0.0f : 1.0f;
+ uniformState.fogStart = rwStateCache.fogStart;
+ uniformState.fogEnd = rwStateCache.fogEnd;
+ uniformState.fogRange = 1.0f/(rwStateCache.fogStart - rwStateCache.fogEnd);
+
+ if(uniformStateDirty[RWGL_ALPHAFUNC] || uniformStateDirty[RWGL_ALPHAREF]){
+ float alphaTest[4] = {};
+ switch(alphaFunc){
+ case ALPHAALWAYS:
+ default:
+ alphaTest[0] = -1000.0f;
+ alphaTest[1] = 1000.0f;
+ break;
+ case ALPHAGREATEREQUAL:
+ alphaTest[0] = alphaRef;
+ alphaTest[1] = 1000.0f;
+ break;
+ case ALPHALESS:
+ alphaTest[0] = -1000.0f;
+ alphaTest[1] = alphaRef;
+ break;
+ }
+ setUniform(u_alphaRef, alphaTest);
+ uniformStateDirty[RWGL_ALPHAFUNC] = false;
+ uniformStateDirty[RWGL_ALPHAREF] = false;
+ }
+
+ if(uniformStateDirty[RWGL_FOG] ||
+ uniformStateDirty[RWGL_FOGSTART] ||
+ uniformStateDirty[RWGL_FOGEND]){
+ float fogData[4] = {
+ uniformState.fogStart,
+ uniformState.fogEnd,
+ uniformState.fogRange,
+ uniformState.fogDisable
+ };
+ setUniform(u_fogData, fogData);
+ uniformStateDirty[RWGL_FOG] = false;
+ uniformStateDirty[RWGL_FOGSTART] = false;
+ uniformStateDirty[RWGL_FOGEND] = false;
+ }
+
+ if(uniformStateDirty[RWGL_FOGCOLOR]){
+ setUniform(u_fogColor, &uniformState.fogColor);
+ uniformStateDirty[RWGL_FOGCOLOR] = false;
+ }
+
+#else
+ if(objectDirty){
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_object);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformObject), nil, GL_STREAM_DRAW);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformObject), &uniformObject, GL_STREAM_DRAW);
+ objectDirty = 0;
+ }
+ if(sceneDirty){
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_scene);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformScene), nil, GL_STREAM_DRAW);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformScene), &uniformScene, GL_STREAM_DRAW);
+ sceneDirty = 0;
+ }
+ if(stateDirty){
+ switch(alphaFunc){
+ case ALPHAALWAYS:
+ default:
+ uniformState.alphaRefLow = -1000.0f;
+ uniformState.alphaRefHigh = 1000.0f;
+ break;
+ case ALPHAGREATEREQUAL:
+ uniformState.alphaRefLow = alphaRef;
+ uniformState.alphaRefHigh = 1000.0f;
+ break;
+ case ALPHALESS:
+ uniformState.alphaRefLow = -1000.0f;
+ uniformState.alphaRefHigh = alphaRef;
+ break;
+ }
+ uniformState.fogDisable = rwStateCache.fogEnable ? 0.0f : 1.0f;
+ uniformState.fogStart = rwStateCache.fogStart;
+ uniformState.fogEnd = rwStateCache.fogEnd;
+ uniformState.fogRange = 1.0f/(rwStateCache.fogStart - rwStateCache.fogEnd);
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_state);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformState), nil, GL_STREAM_DRAW);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformState), &uniformState, GL_STREAM_DRAW);
+ stateDirty = 0;
+ }
+#endif
+ flushUniforms();
+}
+
+static void
+setFrameBuffer(Camera *cam)
+{
+ Raster *fbuf = cam->frameBuffer->parent;
+ Raster *zbuf = cam->zBuffer->parent;
+ assert(fbuf);
+
+ Gl3Raster *natfb = PLUGINOFFSET(Gl3Raster, fbuf, nativeRasterOffset);
+ Gl3Raster *natzb = PLUGINOFFSET(Gl3Raster, zbuf, nativeRasterOffset);
+ assert(fbuf->type == Raster::CAMERA || fbuf->type == Raster::CAMERATEXTURE);
+
+ // Have to make sure depth buffer is attached to FB's fbo
+ bindFramebuffer(natfb->fbo);
+ if(zbuf){
+ if(natfb->fboMate == zbuf){
+ // all good
+ assert(natzb->fboMate == fbuf);
+ }else{
+ if(natzb->fboMate){
+ // have to detatch from fbo first!
+ Gl3Raster *oldfb = PLUGINOFFSET(Gl3Raster, natzb->fboMate, nativeRasterOffset);
+ if(oldfb->fbo){
+ bindFramebuffer(oldfb->fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+ bindFramebuffer(natfb->fbo);
+ }
+ oldfb->fboMate = nil;
+ }
+ natfb->fboMate = zbuf;
+ natzb->fboMate = fbuf;
+ if(natfb->fbo){
+ if(gl3Caps.gles)
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, natzb->texid);
+ else
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, natzb->texid, 0);
+ }
+ }
+ }else{
+ // remove z-buffer
+ if(natfb->fboMate && natfb->fbo)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+ natfb->fboMate = nil;
+ }
+}
+
+static Rect
+getFramebufferRect(Raster *frameBuffer)
+{
+ Rect r;
+ Raster *fb = frameBuffer->parent;
+ if(fb->type == Raster::CAMERA){
+#ifdef LIBRW_SDL2
+ SDL_GetWindowSize(glGlobals.window, &r.w, &r.h);
+#elif defined(LIBRW_SDL3)
+ SDL_GetWindowSize(glGlobals.window, &r.w, &r.h);
+#elif defined(LIBRW_GLFW)
+ glfwGetFramebufferSize(glGlobals.window, &r.w, &r.h);
+#else
+ missing implementation
+#endif
+ }else{
+ r.w = fb->width;
+ r.h = fb->height;
+ }
+ r.x = 0;
+ r.y = 0;
+
+ // Got a subraster
+ if(frameBuffer != fb){
+ r.x = frameBuffer->offsetX;
+ // GL y offset is from bottom
+ r.y = r.h - frameBuffer->height - frameBuffer->offsetY;
+ r.w = frameBuffer->width;
+ r.h = frameBuffer->height;
+ }
+
+ return r;
+}
+
+static void
+setViewport(Raster *frameBuffer)
+{
+ Rect r = getFramebufferRect(frameBuffer);
+ if(r.w != glGlobals.presentWidth || r.h != glGlobals.presentHeight ||
+ r.x != glGlobals.presentOffX || r.y != glGlobals.presentOffY){
+ glViewport(r.x, r.y, r.w, r.h);
+ glGlobals.presentWidth = r.w;
+ glGlobals.presentHeight = r.h;
+ glGlobals.presentOffX = r.x;
+ glGlobals.presentOffY = r.y;
+ }
+}
+
+static void
+beginUpdate(Camera *cam)
+{
+ float view[16], proj[16];
+ // View Matrix
+ Matrix inv;
+ Matrix::invert(&inv, cam->getFrame()->getLTM());
+ // Since we're looking into positive Z,
+ // flip X to ge a left handed view space.
+ view[0] = -inv.right.x;
+ view[1] = inv.right.y;
+ view[2] = inv.right.z;
+ view[3] = 0.0f;
+ view[4] = -inv.up.x;
+ view[5] = inv.up.y;
+ view[6] = inv.up.z;
+ view[7] = 0.0f;
+ view[8] = -inv.at.x;
+ view[9] = inv.at.y;
+ view[10] = inv.at.z;
+ view[11] = 0.0f;
+ view[12] = -inv.pos.x;
+ view[13] = inv.pos.y;
+ view[14] = inv.pos.z;
+ view[15] = 1.0f;
+ memcpy(&cam->devView, &view, sizeof(RawMatrix));
+ setViewMatrix(view);
+
+ // Projection Matrix
+ float32 invwx = 1.0f/cam->viewWindow.x;
+ float32 invwy = 1.0f/cam->viewWindow.y;
+ float32 invz = 1.0f/(cam->farPlane-cam->nearPlane);
+
+ proj[0] = invwx;
+ proj[1] = 0.0f;
+ proj[2] = 0.0f;
+ proj[3] = 0.0f;
+
+ proj[4] = 0.0f;
+ proj[5] = invwy;
+ proj[6] = 0.0f;
+ proj[7] = 0.0f;
+
+ proj[8] = cam->viewOffset.x*invwx;
+ proj[9] = cam->viewOffset.y*invwy;
+ proj[12] = -proj[8];
+ proj[13] = -proj[9];
+ if(cam->projection == Camera::PERSPECTIVE){
+ proj[10] = (cam->farPlane+cam->nearPlane)*invz;
+ proj[11] = 1.0f;
+
+ proj[14] = -2.0f*cam->nearPlane*cam->farPlane*invz;
+ proj[15] = 0.0f;
+ }else{
+ proj[10] = 2.0f*invz;
+ proj[11] = 0.0f;
+
+ proj[14] = -(cam->farPlane+cam->nearPlane)*invz;
+ proj[15] = 1.0f;
+ }
+ memcpy(&cam->devProj, &proj, sizeof(RawMatrix));
+ setProjectionMatrix(proj);
+
+ if(rwStateCache.fogStart != cam->fogPlane){
+ rwStateCache.fogStart = cam->fogPlane;
+ uniformStateDirty[RWGL_FOGSTART] = true;
+ stateDirty = 1;
+ }
+ if(rwStateCache.fogEnd != cam->farPlane){
+ rwStateCache.fogEnd = cam->farPlane;
+ uniformStateDirty[RWGL_FOGEND] = true;
+ stateDirty = 1;
+ }
+
+ setFrameBuffer(cam);
+
+ setViewport(cam->frameBuffer);
+}
+
+static void
+endUpdate(Camera *cam)
+{
+}
+
+static void
+clearCamera(Camera *cam, RGBA *col, uint32 mode)
+{
+ RGBAf colf;
+ GLbitfield mask;
+
+ setFrameBuffer(cam);
+
+ // make sure we're only clearing the part of the framebuffer
+ // that is subrastered
+ bool setScissor = cam->frameBuffer != cam->frameBuffer->parent;
+ if(setScissor){
+ Rect r = getFramebufferRect(cam->frameBuffer);
+ glScissor(r.x, r.y, r.w, r.h);
+ glEnable(GL_SCISSOR_TEST);
+ }
+
+ convColor(&colf, col);
+ glClearColor(colf.red, colf.green, colf.blue, colf.alpha);
+ mask = 0;
+ if(mode & Camera::CLEARIMAGE)
+ mask |= GL_COLOR_BUFFER_BIT;
+ if(mode & Camera::CLEARZ)
+ mask |= GL_DEPTH_BUFFER_BIT;
+ if(mode & Camera::CLEARSTENCIL)
+ mask |= GL_STENCIL_BUFFER_BIT;
+ glDepthMask(GL_TRUE);
+ glClear(mask);
+ glDepthMask(rwStateCache.zwrite);
+
+ if(setScissor)
+ glDisable(GL_SCISSOR_TEST);
+}
+
+static void
+showRaster(Raster *raster, uint32 flags)
+{
+// glViewport(raster->offsetX, raster->offsetY,
+// raster->width, raster->height);
+
+#ifdef LIBRW_SDL2
+ if(flags & Raster::FLIPWAITVSYNCH)
+ SDL_GL_SetSwapInterval(1);
+ else
+ SDL_GL_SetSwapInterval(0);
+ SDL_GL_SwapWindow(glGlobals.window);
+#elif defined(LIBRW_SDL3)
+ if(flags & Raster::FLIPWAITVSYNCH)
+ SDL_GL_SetSwapInterval(1);
+ else
+ SDL_GL_SetSwapInterval(0);
+ SDL_GL_SwapWindow(glGlobals.window);
+#elif defined(LIBRW_GLFW)
+ if(flags & Raster::FLIPWAITVSYNCH)
+ glfwSwapInterval(1);
+ else
+ glfwSwapInterval(0);
+ glfwSwapBuffers(glGlobals.window);
+#else
+ not implemented
+#endif
+}
+
+static bool32
+rasterRenderFast(Raster *raster, int32 x, int32 y)
+{
+ Raster *src = raster;
+ Raster *dst = Raster::getCurrentContext();
+ Gl3Raster *natdst = PLUGINOFFSET(Gl3Raster, dst, nativeRasterOffset);
+ Gl3Raster *natsrc = PLUGINOFFSET(Gl3Raster, src, nativeRasterOffset);
+
+ switch(dst->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ case Raster::CAMERATEXTURE:
+ switch(src->type){
+ case Raster::CAMERA:
+ setActiveTexture(0);
+ glBindTexture(GL_TEXTURE_2D, natdst->texid);
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, (dst->height-src->height)-y,
+ 0, 0, src->width, src->height);
+ glBindTexture(GL_TEXTURE_2D, boundTexture[0]);
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+#ifdef LIBRW_SDL2
+
+static void
+addVideoMode(int displayIndex, int modeIndex)
+{
+ int i;
+ SDL_DisplayMode mode;
+
+ SDL_GetDisplayMode(displayIndex, modeIndex, &mode);
+
+ for(i = 1; i < glGlobals.numModes; i++){
+ if(glGlobals.modes[i].mode.w == mode.w &&
+ glGlobals.modes[i].mode.h == mode.h &&
+ glGlobals.modes[i].mode.format == mode.format){
+ // had this mode already, remember highest refresh rate
+ if(mode.refresh_rate > glGlobals.modes[i].mode.refresh_rate)
+ glGlobals.modes[i].mode.refresh_rate = mode.refresh_rate;
+ return;
+ }
+ }
+
+ // none found, add
+ glGlobals.modes[glGlobals.numModes].mode = mode;
+ glGlobals.modes[glGlobals.numModes].flags = VIDEOMODEEXCLUSIVE;
+ glGlobals.numModes++;
+}
+
+static void
+makeVideoModeList(int displayIndex)
+{
+ int i, num, depth;
+
+ num = SDL_GetNumDisplayModes(displayIndex);
+ rwFree(glGlobals.modes);
+ glGlobals.modes = rwNewT(DisplayMode, num+1, ID_DRIVER | MEMDUR_EVENT);
+
+ SDL_GetCurrentDisplayMode(displayIndex, &glGlobals.modes[0].mode);
+ glGlobals.modes[0].flags = 0;
+ glGlobals.numModes = 1;
+
+ for(i = 0; i < num; i++)
+ addVideoMode(displayIndex, i);
+
+ for(i = 0; i < glGlobals.numModes; i++){
+ depth = SDL_BITSPERPIXEL(glGlobals.modes[i].mode.format);
+ // set depth to power of two
+ for(glGlobals.modes[i].depth = 1; glGlobals.modes[i].depth < depth; glGlobals.modes[i].depth <<= 1);
+ }
+}
+
+static int
+openSDL2(EngineOpenParams *openparams)
+{
+ glGlobals.winWidth = openparams->width;
+ glGlobals.winHeight = openparams->height;
+ glGlobals.winTitle = openparams->windowtitle;
+ glGlobals.pWindow = openparams->window;
+
+ memset(&gl3Caps, 0, sizeof(gl3Caps));
+
+ /* Init SDL */
+ if(SDL_InitSubSystem(SDL_INIT_VIDEO)){
+ RWERROR((ERR_GENERAL, SDL_GetError()));
+ return 0;
+ }
+
+ glGlobals.currentDisplay = 0;
+ glGlobals.numDisplays = SDL_GetNumVideoDisplays();
+
+ makeVideoModeList(glGlobals.currentDisplay);
+
+ return 1;
+}
+
+static int
+closeSDL2(void)
+{
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return 1;
+}
+
+static struct {
+ int gl;
+ int major, minor;
+} profiles[] = {
+ { SDL_GL_CONTEXT_PROFILE_CORE, 3, 3 },
+ { SDL_GL_CONTEXT_PROFILE_CORE, 2, 1 },
+ { SDL_GL_CONTEXT_PROFILE_ES, 3, 1 },
+ { SDL_GL_CONTEXT_PROFILE_ES, 2, 0 },
+ { 0, 0, 0 },
+};
+
+static int
+startSDL2(void)
+{
+ SDL_Window *win;
+ SDL_GLContext ctx;
+ DisplayMode *mode;
+
+ mode = &glGlobals.modes[glGlobals.currentMode];
+
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, glGlobals.numSamples);
+
+ int i;
+ for(i = 0; profiles[i].gl; i++){
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profiles[i].gl);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, profiles[i].major);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, profiles[i].minor);
+
+ if(mode->flags & VIDEOMODEEXCLUSIVE) {
+ win = SDL_CreateWindow(glGlobals.winTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, mode->mode.w, mode->mode.h, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
+ if (win)
+ SDL_SetWindowDisplayMode(win, &mode->mode);
+ } else {
+ win = SDL_CreateWindow(glGlobals.winTitle, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, glGlobals.winWidth, glGlobals.winHeight, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
+ if (win)
+ SDL_SetWindowDisplayMode(win, NULL);
+ }
+ if(win){
+ gl3Caps.gles = profiles[i].gl == SDL_GL_CONTEXT_PROFILE_ES;
+ gl3Caps.glversion = profiles[i].major*10 + profiles[i].minor;
+ break;
+ }
+ }
+ if(win == nil){
+ RWERROR((ERR_GENERAL, SDL_GetError()));
+ return 0;
+ }
+ ctx = SDL_GL_CreateContext(win);
+
+ if (!((gl3Caps.gles ? gladLoadGLES2Loader : gladLoadGLLoader) ((GLADloadproc) SDL_GL_GetProcAddress, gl3Caps.glversion)) ) {
+ RWERROR((ERR_GENERAL, "gladLoadGLLoader failed"));
+ SDL_GL_DeleteContext(ctx);
+ SDL_DestroyWindow(win);
+ return 0;
+ }
+
+// printf("OpenGL version: %s\n", glGetString(GL_VERSION));
+
+ glGlobals.window = win;
+ glGlobals.glcontext = ctx;
+ *glGlobals.pWindow = win;
+ glGlobals.presentWidth = 0;
+ glGlobals.presentHeight = 0;
+ glGlobals.presentOffX = 0;
+ glGlobals.presentOffY = 0;
+ return 1;
+}
+
+static int
+stopSDL2(void)
+{
+ SDL_GL_DeleteContext(glGlobals.glcontext);
+ SDL_DestroyWindow(glGlobals.window);
+ return 1;
+}
+
+#elif defined(LIBRW_SDL3)
+
+static void
+addVideoMode(const SDL_DisplayMode *mode)
+{
+ int i;
+
+ for(i = 1; i < glGlobals.numModes; i++){
+ if(glGlobals.modes[i].mode.w == mode->w &&
+ glGlobals.modes[i].mode.h == mode->h &&
+ glGlobals.modes[i].mode.format == mode->format){
+ // had this mode already, remember highest refresh rate
+ if(mode->refresh_rate > glGlobals.modes[i].mode.refresh_rate)
+ glGlobals.modes[i].mode.refresh_rate = mode->refresh_rate;
+ return;
+ }
+ }
+
+ // none found, add
+ glGlobals.modes[glGlobals.numModes].mode = *mode;
+ glGlobals.modes[glGlobals.numModes].flags = VIDEOMODEEXCLUSIVE;
+ glGlobals.numModes++;
+}
+
+static void
+makeVideoModeList(SDL_DisplayID displayIndex, SDL_DisplayID *displays)
+{
+ int i, num, depth;
+ const SDL_DisplayMode *currentMode;
+ SDL_DisplayMode **modes;
+
+ currentMode = SDL_GetCurrentDisplayMode(displays[displayIndex]);
+ modes = SDL_GetFullscreenDisplayModes(displayIndex, &num);
+
+ rwFree(glGlobals.modes);
+ glGlobals.modes = rwNewT(DisplayMode, num+(currentMode != NULL ? 1 : 0), ID_DRIVER | MEMDUR_EVENT);
+
+ if (currentMode) {
+ glGlobals.modes[0].mode = *currentMode;
+ glGlobals.modes[0].flags = 0;
+ glGlobals.numModes = 1;
+ }
+
+ for(i = 0; i < num; i++)
+ addVideoMode(modes[i]);
+
+ for(i = 0; i < glGlobals.numModes; i++){
+ depth = SDL_BITSPERPIXEL(glGlobals.modes[i].mode.format);
+ // set depth to power of two
+ for(glGlobals.modes[i].depth = 1; glGlobals.modes[i].depth < depth; glGlobals.modes[i].depth <<= 1);
+ }
+ SDL_free(modes);
+}
+
+static int
+openSDL3(EngineOpenParams *openparams)
+{
+ glGlobals.winWidth = openparams->width;
+ glGlobals.winHeight = openparams->height;
+ glGlobals.winTitle = openparams->windowtitle;
+ glGlobals.pWindow = openparams->window;
+
+ memset(&gl3Caps, 0, sizeof(gl3Caps));
+
+ /* Init SDL */
+ if (!SDL_InitSubSystem(SDL_INIT_VIDEO)){
+ RWERROR((ERR_GENERAL, SDL_GetError()));
+ return 0;
+ }
+
+ glGlobals.currentDisplay = 0;
+ SDL_DisplayID *displays = SDL_GetDisplays(&glGlobals.numDisplays);
+
+ if (glGlobals.currentDisplay >= glGlobals.numDisplays) {
+ RWERROR((ERR_GENERAL, SDL_GetError()));
+ return 0;
+ }
+
+ makeVideoModeList(glGlobals.currentDisplay, displays);
+ SDL_free(displays);
+
+ return 1;
+}
+
+static int
+closeSDL3(void)
+{
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return 1;
+}
+
+static struct {
+ int gl;
+ int major, minor;
+} profiles[] = {
+ { SDL_GL_CONTEXT_PROFILE_CORE, 3, 3 },
+ { SDL_GL_CONTEXT_PROFILE_CORE, 2, 1 },
+ { SDL_GL_CONTEXT_PROFILE_ES, 3, 1 },
+ { SDL_GL_CONTEXT_PROFILE_ES, 2, 0 },
+ { 0, 0, 0 },
+};
+
+static int
+startSDL3(void)
+{
+ SDL_Window *win;
+ SDL_GLContext ctx;
+ DisplayMode *mode;
+
+ mode = &glGlobals.modes[glGlobals.currentMode];
+
+ SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, glGlobals.numSamples);
+
+ int i;
+ for(i = 0; profiles[i].gl; i++){
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profiles[i].gl);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, profiles[i].major);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, profiles[i].minor);
+
+ if(mode->flags & VIDEOMODEEXCLUSIVE) {
+ win = SDL_CreateWindow(glGlobals.winTitle, mode->mode.w, mode->mode.h, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
+ if (win)
+ SDL_SetWindowFullscreenMode(win, &mode->mode);
+ } else {
+ win = SDL_CreateWindow(glGlobals.winTitle, glGlobals.winWidth, glGlobals.winHeight, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
+ if (win)
+ SDL_SetWindowFullscreenMode(win, NULL);
+ }
+ if(win){
+ gl3Caps.gles = profiles[i].gl == SDL_GL_CONTEXT_PROFILE_ES;
+ gl3Caps.glversion = profiles[i].major*10 + profiles[i].minor;
+ break;
+ }
+ }
+ if(win == nil){
+ RWERROR((ERR_GENERAL, SDL_GetError()));
+ return 0;
+ }
+ ctx = SDL_GL_CreateContext(win);
+
+ if (!((gl3Caps.gles ? gladLoadGLES2Loader : gladLoadGLLoader) ((GLADloadproc) SDL_GL_GetProcAddress, gl3Caps.glversion)) ) {
+ RWERROR((ERR_GENERAL, "gladLoadGLLoader failed"));
+ SDL_GL_DestroyContext(ctx);
+ SDL_DestroyWindow(win);
+ return 0;
+ }
+
+// printf("OpenGL version: %s\n", glGetString(GL_VERSION));
+
+ glGlobals.window = win;
+ glGlobals.glcontext = ctx;
+ *glGlobals.pWindow = win;
+ glGlobals.presentWidth = 0;
+ glGlobals.presentHeight = 0;
+ glGlobals.presentOffX = 0;
+ glGlobals.presentOffY = 0;
+ return 1;
+}
+
+static int
+stopSDL3(void)
+{
+ SDL_GL_DestroyContext(glGlobals.glcontext);
+ SDL_DestroyWindow(glGlobals.window);
+ return 1;
+}
+#elif defined(LIBRW_GLFW)
+
+static void
+addVideoMode(const GLFWvidmode *mode)
+{
+ int i;
+
+ for(i = 1; i < glGlobals.numModes; i++){
+ if(glGlobals.modes[i].mode.width == mode->width &&
+ glGlobals.modes[i].mode.height == mode->height &&
+ glGlobals.modes[i].mode.redBits == mode->redBits &&
+ glGlobals.modes[i].mode.greenBits == mode->greenBits &&
+ glGlobals.modes[i].mode.blueBits == mode->blueBits){
+ // had this mode already, remember highest refresh rate
+ if(mode->refreshRate > glGlobals.modes[i].mode.refreshRate)
+ glGlobals.modes[i].mode.refreshRate = mode->refreshRate;
+ return;
+ }
+ }
+
+ // none found, add
+ glGlobals.modes[glGlobals.numModes].mode = *mode;
+ glGlobals.modes[glGlobals.numModes].flags = VIDEOMODEEXCLUSIVE;
+ glGlobals.numModes++;
+}
+
+static void
+makeVideoModeList(GLFWmonitor *monitor)
+{
+ int i, num;
+ const GLFWvidmode *modes;
+
+ modes = glfwGetVideoModes(monitor, &num);
+ rwFree(glGlobals.modes);
+ glGlobals.modes = rwNewT(DisplayMode, num+1, ID_DRIVER | MEMDUR_EVENT);
+
+ glGlobals.modes[0].mode = *glfwGetVideoMode(monitor);
+ glGlobals.modes[0].flags = 0;
+ glGlobals.numModes = 1;
+
+ for(i = 0; i < num; i++)
+ addVideoMode(&modes[i]);
+
+ for(i = 0; i < glGlobals.numModes; i++){
+ num = glGlobals.modes[i].mode.redBits +
+ glGlobals.modes[i].mode.greenBits +
+ glGlobals.modes[i].mode.blueBits;
+ // set depth to power of two
+ for(glGlobals.modes[i].depth = 1; glGlobals.modes[i].depth < num; glGlobals.modes[i].depth <<= 1);
+ }
+}
+
+static int
+openGLFW(EngineOpenParams *openparams)
+{
+ glGlobals.winWidth = openparams->width;
+ glGlobals.winHeight = openparams->height;
+ glGlobals.winTitle = openparams->windowtitle;
+ glGlobals.pWindow = openparams->window;
+
+ memset(&gl3Caps, 0, sizeof(gl3Caps));
+
+ /* Init GLFW */
+ if(!glfwInit()){
+ RWERROR((ERR_GENERAL, "glfwInit() failed"));
+ return 0;
+ }
+
+ glGlobals.monitor = glfwGetMonitors(&glGlobals.numMonitors)[0];
+
+ makeVideoModeList(glGlobals.monitor);
+
+ return 1;
+}
+
+static int
+closeGLFW(void)
+{
+ glfwTerminate();
+ return 1;
+}
+
+static void
+glfwerr(int error, const char *desc)
+{
+ fprintf(stderr, "GLFW Error: %s\n", desc);
+}
+
+static struct {
+ int gl;
+ int major, minor;
+} profiles[] = {
+ { GLFW_OPENGL_API, 3, 3 },
+ { GLFW_OPENGL_API, 2, 1 },
+ { GLFW_OPENGL_ES_API, 3, 1 },
+ { GLFW_OPENGL_ES_API, 2, 0 },
+ { 0, 0, 0 },
+};
+
+static int
+startGLFW(void)
+{
+ GLFWwindow *win;
+ DisplayMode *mode;
+
+ mode = &glGlobals.modes[glGlobals.currentMode];
+
+ glfwSetErrorCallback(glfwerr);
+ glfwWindowHint(GLFW_RED_BITS, mode->mode.redBits);
+ glfwWindowHint(GLFW_GREEN_BITS, mode->mode.greenBits);
+ glfwWindowHint(GLFW_BLUE_BITS, mode->mode.blueBits);
+ glfwWindowHint(GLFW_REFRESH_RATE, mode->mode.refreshRate);
+
+ // GLX will round up to 2x or 4x if you ask for multisampling on with 1 sample
+ // So only apply the SAMPLES hint if we actually want multisampling
+ if (glGlobals.numSamples > 1)
+ glfwWindowHint(GLFW_SAMPLES, glGlobals.numSamples);
+
+ int i;
+ for(i = 0; profiles[i].gl; i++){
+ glfwWindowHint(GLFW_CLIENT_API, profiles[i].gl);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, profiles[i].major);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, profiles[i].minor);
+
+ if(mode->flags & VIDEOMODEEXCLUSIVE)
+ win = glfwCreateWindow(mode->mode.width, mode->mode.height, glGlobals.winTitle, glGlobals.monitor, nil);
+ else
+ win = glfwCreateWindow(glGlobals.winWidth, glGlobals.winHeight, glGlobals.winTitle, nil, nil);
+ if(win){
+ gl3Caps.gles = profiles[i].gl == GLFW_OPENGL_ES_API;
+ gl3Caps.glversion = profiles[i].major*10 + profiles[i].minor;
+ break;
+ }
+ }
+ if(win == nil){
+ RWERROR((ERR_GENERAL, "glfwCreateWindow() failed"));
+ return 0;
+ }
+ glfwMakeContextCurrent(win);
+
+ /* Init GLAD */
+ if (!((gl3Caps.gles ? gladLoadGLES2Loader : gladLoadGLLoader) ((GLADloadproc) glfwGetProcAddress, gl3Caps.glversion)) ) {
+ RWERROR((ERR_GENERAL, "gladLoadGLLoader failed"));
+ glfwDestroyWindow(win);
+ return 0;
+ }
+
+// printf("OpenGL version: %s\n", glGetString(GL_VERSION));
+
+ glGlobals.window = win;
+ *glGlobals.pWindow = win;
+ glGlobals.presentWidth = 0;
+ glGlobals.presentHeight = 0;
+ glGlobals.presentOffX = 0;
+ glGlobals.presentOffY = 0;
+ return 1;
+}
+
+static int
+stopGLFW(void)
+{
+ glfwDestroyWindow(glGlobals.window);
+ return 1;
+}
+#else
+not implemented
+#endif
+
+static int
+initOpenGL(void)
+{
+/*
+ // this only works from 3.0 onward,
+ // but luckily GLAD has already taken care of extensions for us
+ int numExt;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
+ for(int i = 0; i < numExt; i++){
+ const char *ext = (const char*)glGetStringi(GL_EXTENSIONS, i);
+ if(ext == nil)
+ continue; // apparently that can happen...
+ if(strcmp(ext, "GL_EXT_texture_compression_s3tc") == 0)
+ gl3Caps.dxtSupported = true;
+ else if(strcmp(ext, "GL_KHR_texture_compression_astc_ldr") == 0)
+ gl3Caps.astcSupported = true;
+// printf("%d %s\n", i, ext);
+ }
+*/
+ gl3Caps.dxtSupported = !!GLAD_GL_EXT_texture_compression_s3tc;
+ gl3Caps.astcSupported = !!GLAD_GL_KHR_texture_compression_astc_ldr;
+
+ glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl3Caps.maxAnisotropy);
+
+ if(gl3Caps.gles){
+ if(gl3Caps.glversion >= 30)
+ shaderDecl = shaderDecl310es;
+ else
+ shaderDecl = shaderDecl100es;
+ }else{
+ if(gl3Caps.glversion >= 30)
+ shaderDecl = shaderDecl330;
+ else
+ shaderDecl = shaderDecl120;
+ }
+
+#ifndef RW_GL_USE_UBOS
+ u_alphaRef = registerUniform("u_alphaRef", UNIFORM_VEC4);
+ u_fogData = registerUniform("u_fogData", UNIFORM_VEC4);
+ u_fogColor = registerUniform("u_fogColor", UNIFORM_VEC4);
+ u_proj = registerUniform("u_proj", UNIFORM_MAT4);
+ u_view = registerUniform("u_view", UNIFORM_MAT4);
+ u_world = registerUniform("u_world", UNIFORM_MAT4);
+ u_ambLight = registerUniform("u_ambLight", UNIFORM_VEC4);
+ u_lightParams = registerUniform("u_lightParams", UNIFORM_VEC4, MAX_LIGHTS);
+ u_lightPosition = registerUniform("u_lightPosition", UNIFORM_VEC4, MAX_LIGHTS);
+ u_lightDirection = registerUniform("u_lightDirection", UNIFORM_VEC4, MAX_LIGHTS);
+ u_lightColor = registerUniform("u_lightColor", UNIFORM_VEC4, MAX_LIGHTS);
+ lastShaderUploaded = nil;
+#else
+ registerBlock("Scene");
+ registerBlock("Object");
+ registerBlock("State");
+#endif
+ u_matColor = registerUniform("u_matColor", UNIFORM_VEC4);
+ u_surfProps = registerUniform("u_surfProps", UNIFORM_VEC4);
+
+ // for im2d
+ registerUniform("u_xform", UNIFORM_VEC4);
+
+ glClearColor(0.25, 0.25, 0.25, 1.0);
+
+ byte whitepixel[4] = {0xFF, 0xFF, 0xFF, 0xFF};
+ glGenTextures(1, &whitetex);
+ glBindTexture(GL_TEXTURE_2D, whitetex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, &whitepixel);
+
+ resetRenderState();
+
+ glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
+
+ if(gl3Caps.glversion >= 30){
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+ }
+
+#ifdef RW_GL_USE_UBOS
+ glGenBuffers(1, &ubo_state);
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_state);
+ glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("State"), ubo_state);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformState), &uniformState,
+ GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ glGenBuffers(1, &ubo_scene);
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_scene);
+ glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("Scene"), ubo_scene);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformScene), &uniformScene,
+ GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ glGenBuffers(1, &ubo_object);
+ glBindBuffer(GL_UNIFORM_BUFFER, ubo_object);
+ glBindBufferBase(GL_UNIFORM_BUFFER, gl3::findBlock("Object"), ubo_object);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformObject), &uniformObject,
+ GL_STREAM_DRAW);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+#endif
+
+#include "shaders/default_vs_gl.inc"
+#include "shaders/simple_fs_gl.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, default_vert_src, nil };
+ const char *vs_fullLight[] = { shaderDecl, "#define DIRECTIONALS\n#define POINTLIGHTS\n#define SPOTLIGHTS\n", header_vert_src, default_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil };
+ const char *fs_noAT[] = { shaderDecl, "#define NO_ALPHATEST\n", header_frag_src, simple_frag_src, nil };
+
+ defaultShader = Shader::create(vs, fs);
+ assert(defaultShader);
+ defaultShader_noAT = Shader::create(vs, fs_noAT);
+ assert(defaultShader_noAT);
+
+ defaultShader_fullLight = Shader::create(vs_fullLight, fs);
+ assert(defaultShader_fullLight);
+ defaultShader_fullLight_noAT = Shader::create(vs_fullLight, fs_noAT);
+ assert(defaultShader_fullLight_noAT);
+
+ openIm2D();
+ openIm3D();
+
+ return 1;
+}
+
+static int
+termOpenGL(void)
+{
+ closeIm3D();
+ closeIm2D();
+
+ defaultShader->destroy();
+ defaultShader = nil;
+ defaultShader_noAT->destroy();
+ defaultShader_noAT = nil;
+ defaultShader_fullLight->destroy();
+ defaultShader_fullLight = nil;
+ defaultShader_fullLight_noAT->destroy();
+ defaultShader_fullLight_noAT = nil;
+
+ glDeleteTextures(1, &whitetex);
+ whitetex = 0;
+
+ return 1;
+}
+
+static int
+finalizeOpenGL(void)
+{
+ return 1;
+}
+
+#ifdef LIBRW_SDL2
+static int
+deviceSystemSDL2(DeviceReq req, void *arg, int32 n)
+{
+ VideoMode *rwmode;
+
+ switch(req){
+ case DEVICEOPEN:
+ return openSDL2((EngineOpenParams*)arg);
+ case DEVICECLOSE:
+ return closeSDL2();
+
+ case DEVICEINIT:
+ return startSDL2() && initOpenGL();
+ case DEVICETERM:
+ return termOpenGL() && stopSDL2();
+
+ case DEVICEFINALIZE:
+ return finalizeOpenGL();
+
+
+ case DEVICEGETNUMSUBSYSTEMS:
+ return glGlobals.numDisplays;
+
+ case DEVICEGETCURRENTSUBSYSTEM:
+ return glGlobals.currentDisplay;
+
+ case DEVICESETSUBSYSTEM:
+ if (n >= SDL_GetNumVideoDisplays())
+ return 0;
+ glGlobals.currentDisplay = n;
+ return 1;
+
+ case DEVICEGETSUBSSYSTEMINFO: {
+ const char *display_name = SDL_GetDisplayName(n);
+ if (display_name == nil)
+ return 0;
+ strncpy(((SubSystemInfo*)arg)->name, display_name, sizeof(SubSystemInfo::name));
+ return 1;
+ }
+
+
+ case DEVICEGETNUMVIDEOMODES:
+ return glGlobals.numModes;
+
+ case DEVICEGETCURRENTVIDEOMODE:
+ return glGlobals.currentMode;
+
+ case DEVICESETVIDEOMODE:
+ if(n >= glGlobals.numModes)
+ return 0;
+ glGlobals.currentMode = n;
+ return 1;
+
+ case DEVICEGETVIDEOMODEINFO:
+ rwmode = (VideoMode*)arg;
+ rwmode->width = glGlobals.modes[n].mode.w;
+ rwmode->height = glGlobals.modes[n].mode.h;
+ rwmode->depth = glGlobals.modes[n].depth;
+ rwmode->flags = glGlobals.modes[n].flags;
+ return 1;
+
+ case DEVICEGETMAXMULTISAMPLINGLEVELS:
+ {
+ GLint maxSamples;
+ glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+ if(maxSamples == 0)
+ return 1;
+ return maxSamples;
+ }
+ case DEVICEGETMULTISAMPLINGLEVELS:
+ if(glGlobals.numSamples == 0)
+ return 1;
+ return glGlobals.numSamples;
+ case DEVICESETMULTISAMPLINGLEVELS:
+ glGlobals.numSamples = (uint32)n;
+ return 1;
+ default:
+ assert(0 && "not implemented");
+ return 0;
+ }
+ return 1;
+}
+
+#elif defined(LIBRW_SDL3)
+
+static int
+deviceSystemSDL3(DeviceReq req, void *arg, int32 n)
+{
+ VideoMode *rwmode;
+
+ switch(req){
+ case DEVICEOPEN:
+ return openSDL3((EngineOpenParams*)arg);
+ case DEVICECLOSE:
+ return closeSDL3();
+
+ case DEVICEINIT:
+ return startSDL3() && initOpenGL();
+ case DEVICETERM:
+ return termOpenGL() && stopSDL3();
+
+ case DEVICEFINALIZE:
+ return finalizeOpenGL();
+
+
+ case DEVICEGETNUMSUBSYSTEMS:
+ return glGlobals.numDisplays;
+
+ case DEVICEGETCURRENTSUBSYSTEM:
+ return glGlobals.currentDisplay;
+
+ case DEVICESETSUBSYSTEM:
+ if (n >= glGlobals.numDisplays)
+ return 0;
+ glGlobals.currentDisplay = n;
+ return 1;
+
+ case DEVICEGETSUBSSYSTEMINFO: {
+ const char *display_name = SDL_GetDisplayName(n);
+ if (display_name == nil)
+ return 0;
+ strncpy(((SubSystemInfo*)arg)->name, display_name, sizeof(SubSystemInfo::name));
+ return 1;
+ }
+
+
+ case DEVICEGETNUMVIDEOMODES:
+ return glGlobals.numModes;
+
+ case DEVICEGETCURRENTVIDEOMODE:
+ return glGlobals.currentMode;
+
+ case DEVICESETVIDEOMODE:
+ if(n >= glGlobals.numModes)
+ return 0;
+ glGlobals.currentMode = n;
+ return 1;
+
+ case DEVICEGETVIDEOMODEINFO:
+ if (n <= 0)
+ return 0;
+ rwmode = (VideoMode*)arg;
+ rwmode->width = glGlobals.modes[n].mode.w;
+ rwmode->height = glGlobals.modes[n].mode.h;
+ rwmode->depth = glGlobals.modes[n].depth;
+ rwmode->flags = glGlobals.modes[n].flags;
+ return 1;
+
+ case DEVICEGETMAXMULTISAMPLINGLEVELS:
+ {
+ GLint maxSamples;
+ glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+ if(maxSamples == 0)
+ return 1;
+ return maxSamples;
+ }
+ case DEVICEGETMULTISAMPLINGLEVELS:
+ if(glGlobals.numSamples == 0)
+ return 1;
+ return glGlobals.numSamples;
+ case DEVICESETMULTISAMPLINGLEVELS:
+ glGlobals.numSamples = (uint32)n;
+ return 1;
+ default:
+ assert(0 && "not implemented");
+ return 0;
+ }
+ return 1;
+}
+
+#elif defined(LIBRW_GLFW)
+
+static int
+deviceSystemGLFW(DeviceReq req, void *arg, int32 n)
+{
+ GLFWmonitor **monitors;
+ VideoMode *rwmode;
+
+ switch(req){
+ case DEVICEOPEN:
+ return openGLFW((EngineOpenParams*)arg);
+ case DEVICECLOSE:
+ return closeGLFW();
+
+ case DEVICEINIT:
+ return startGLFW() && initOpenGL();
+ case DEVICETERM:
+ return termOpenGL() && stopGLFW();
+
+ case DEVICEFINALIZE:
+ return finalizeOpenGL();
+
+
+ case DEVICEGETNUMSUBSYSTEMS:
+ return glGlobals.numMonitors;
+
+ case DEVICEGETCURRENTSUBSYSTEM:
+ return glGlobals.currentMonitor;
+
+ case DEVICESETSUBSYSTEM:
+ monitors = glfwGetMonitors(&glGlobals.numMonitors);
+ if(n >= glGlobals.numMonitors)
+ return 0;
+ glGlobals.currentMonitor = n;
+ glGlobals.monitor = monitors[glGlobals.currentMonitor];
+ return 1;
+
+ case DEVICEGETSUBSSYSTEMINFO:
+ monitors = glfwGetMonitors(&glGlobals.numMonitors);
+ if(n >= glGlobals.numMonitors)
+ return 0;
+ strncpy(((SubSystemInfo*)arg)->name, glfwGetMonitorName(monitors[n]), sizeof(SubSystemInfo::name));
+ return 1;
+
+
+ case DEVICEGETNUMVIDEOMODES:
+ return glGlobals.numModes;
+
+ case DEVICEGETCURRENTVIDEOMODE:
+ return glGlobals.currentMode;
+
+ case DEVICESETVIDEOMODE:
+ if(n >= glGlobals.numModes)
+ return 0;
+ glGlobals.currentMode = n;
+ return 1;
+
+ case DEVICEGETVIDEOMODEINFO:
+ rwmode = (VideoMode*)arg;
+ rwmode->width = glGlobals.modes[n].mode.width;
+ rwmode->height = glGlobals.modes[n].mode.height;
+ rwmode->depth = glGlobals.modes[n].depth;
+ rwmode->flags = glGlobals.modes[n].flags;
+ return 1;
+
+ case DEVICEGETMAXMULTISAMPLINGLEVELS:
+ {
+ GLint maxSamples;
+ glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+ if(maxSamples == 0)
+ return 1;
+ return maxSamples;
+ }
+ case DEVICEGETMULTISAMPLINGLEVELS:
+ if(glGlobals.numSamples == 0)
+ return 1;
+ return glGlobals.numSamples;
+ case DEVICESETMULTISAMPLINGLEVELS:
+ glGlobals.numSamples = (uint32)n;
+ return 1;
+ default:
+ assert(0 && "not implemented");
+ return 0;
+ }
+ return 1;
+}
+
+#else
+
+not implemented
+
+#endif
+
+Device renderdevice = {
+ -1.0f, 1.0f,
+ gl3::beginUpdate,
+ gl3::endUpdate,
+ gl3::clearCamera,
+ gl3::showRaster,
+ gl3::rasterRenderFast,
+ gl3::setRenderState,
+ gl3::getRenderState,
+ gl3::im2DRenderLine,
+ gl3::im2DRenderTriangle,
+ gl3::im2DRenderPrimitive,
+ gl3::im2DRenderIndexedPrimitive,
+ gl3::im3DTransform,
+ gl3::im3DRenderPrimitive,
+ gl3::im3DRenderIndexedPrimitive,
+ gl3::im3DEnd,
+#ifdef LIBRW_SDL2
+ gl3::deviceSystemSDL2
+#elif defined(LIBRW_SDL3)
+ gl3::deviceSystemSDL3
+#elif defined(LIBRW_GLFW)
+ gl3::deviceSystemGLFW
+#else
+ not implemented
+#endif
+};
+
+}
+}
+
+#else
+// urgh, probably should get rid of that eventually
+#include "rwgl3.h"
+namespace rw {
+namespace gl3 {
+Gl3Caps gl3Caps;
+bool32 needToReadBackTextures;
+}
+}
+#endif
diff --git a/src/gl/gl3immed.cpp b/src/gl/gl3immed.cpp
new file mode 100644
index 0000000..b66b7e5
--- /dev/null
+++ b/src/gl/gl3immed.cpp
@@ -0,0 +1,317 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwrender.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#ifdef RW_OPENGL
+#include "rwgl3.h"
+#include "rwgl3impl.h"
+#include "rwgl3shader.h"
+
+namespace rw {
+namespace gl3 {
+
+uint32 im2DVbo, im2DIbo;
+#ifdef RW_GL_USE_VAOS
+uint32 im2DVao;
+#endif
+
+Shader *im2dOverrideShader;
+
+static int32 u_xform;
+
+#define STARTINDICES 10000
+#define STARTVERTICES 10000
+
+static Shader *im2dShader;
+static AttribDesc im2dattribDesc[3] = {
+ { ATTRIB_POS, GL_FLOAT, GL_FALSE, 4,
+ sizeof(Im2DVertex), 0 },
+ { ATTRIB_COLOR, GL_UNSIGNED_BYTE, GL_TRUE, 4,
+ sizeof(Im2DVertex), offsetof(Im2DVertex, r) },
+ { ATTRIB_TEXCOORDS0, GL_FLOAT, GL_FALSE, 2,
+ sizeof(Im2DVertex), offsetof(Im2DVertex, u) },
+};
+
+static int primTypeMap[] = {
+ GL_POINTS, // invalid
+ GL_LINES,
+ GL_LINE_STRIP,
+ GL_TRIANGLES,
+ GL_TRIANGLE_STRIP,
+ GL_TRIANGLE_FAN,
+ GL_POINTS
+};
+
+void
+openIm2D(void)
+{
+ // must already be registered by device. we just need the value
+ u_xform = registerUniform("u_xform", UNIFORM_VEC4);
+
+#include "shaders/im2d_gl.inc"
+#include "shaders/simple_fs_gl.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, im2d_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil };
+ im2dShader = Shader::create(vs, fs);
+ assert(im2dShader);
+
+ glGenBuffers(1, &im2DIbo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2DIbo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, STARTINDICES*2, nil, GL_STREAM_DRAW);
+
+ glGenBuffers(1, &im2DVbo);
+ glBindBuffer(GL_ARRAY_BUFFER, im2DVbo);
+ glBufferData(GL_ARRAY_BUFFER, STARTVERTICES*sizeof(Im2DVertex), nil, GL_STREAM_DRAW);
+
+#ifdef RW_GL_USE_VAOS
+ glGenVertexArrays(1, &im2DVao);
+ glBindVertexArray(im2DVao);
+ setAttribPointers(im2dattribDesc, 3);
+#endif
+}
+
+void
+closeIm2D(void)
+{
+ glDeleteBuffers(1, &im2DIbo);
+ glDeleteBuffers(1, &im2DVbo);
+#ifdef RW_GL_USE_VAOS
+ glDeleteVertexArrays(1, &im2DVao);
+#endif
+ im2dShader->destroy();
+ im2dShader = nil;
+}
+
+static Im2DVertex tmpprimbuf[3];
+
+void
+im2DRenderLine(void *vertices, int32 numVertices, int32 vert1, int32 vert2)
+{
+ Im2DVertex *verts = (Im2DVertex*)vertices;
+ tmpprimbuf[0] = verts[vert1];
+ tmpprimbuf[1] = verts[vert2];
+ im2DRenderPrimitive(PRIMTYPELINELIST, tmpprimbuf, 2);
+}
+
+void
+im2DRenderTriangle(void *vertices, int32 numVertices, int32 vert1, int32 vert2, int32 vert3)
+{
+ Im2DVertex *verts = (Im2DVertex*)vertices;
+ tmpprimbuf[0] = verts[vert1];
+ tmpprimbuf[1] = verts[vert2];
+ tmpprimbuf[2] = verts[vert3];
+ im2DRenderPrimitive(PRIMTYPETRILIST, tmpprimbuf, 3);
+}
+
+void
+im2DSetXform(void)
+{
+ GLfloat xform[4];
+ Camera *cam;
+ cam = (Camera*)engine->currentCamera;
+ xform[0] = 2.0f/cam->frameBuffer->width;
+ xform[1] = -2.0f/cam->frameBuffer->height;
+ xform[2] = -1.0f;
+ xform[3] = 1.0f;
+ setUniform(u_xform, xform);
+// glUniform4fv(currentShader->uniformLocations[u_xform], 1, xform);
+}
+
+void
+im2DRenderPrimitive(PrimitiveType primType, void *vertices, int32 numVertices)
+{
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(im2DVao);
+#endif
+
+ glBindBuffer(GL_ARRAY_BUFFER, im2DVbo);
+ glBufferData(GL_ARRAY_BUFFER, STARTVERTICES*sizeof(Im2DVertex), nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im2DVertex), vertices);
+
+ if(im2dOverrideShader)
+ im2dOverrideShader->use();
+ else
+ im2dShader->use();
+#ifndef RW_GL_USE_VAOS
+ setAttribPointers(im2dattribDesc, 3);
+#endif
+
+ im2DSetXform();
+
+ flushCache();
+ glDrawArrays(primTypeMap[primType], 0, numVertices);
+#ifndef RW_GL_USE_VAOS
+ disableAttribPointers(im2dattribDesc, 3);
+#endif
+}
+
+void
+im2DRenderIndexedPrimitive(PrimitiveType primType,
+ void *vertices, int32 numVertices,
+ void *indices, int32 numIndices)
+{
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(im2DVao);
+#endif
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im2DIbo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, STARTINDICES*2, nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*2, indices);
+
+ glBindBuffer(GL_ARRAY_BUFFER, im2DVbo);
+ glBufferData(GL_ARRAY_BUFFER, STARTVERTICES*sizeof(Im2DVertex), nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im2DVertex), vertices);
+
+ if(im2dOverrideShader)
+ im2dOverrideShader->use();
+ else
+ im2dShader->use();
+#ifndef RW_GL_USE_VAOS
+ setAttribPointers(im2dattribDesc, 3);
+#endif
+
+ im2DSetXform();
+
+ flushCache();
+ glDrawElements(primTypeMap[primType], numIndices,
+ GL_UNSIGNED_SHORT, nil);
+#ifndef RW_GL_USE_VAOS
+ disableAttribPointers(im2dattribDesc, 3);
+#endif
+}
+
+
+// Im3D
+
+
+static Shader *im3dShader;
+static AttribDesc im3dattribDesc[4] = {
+ { ATTRIB_POS, GL_FLOAT, GL_FALSE, 3,
+ sizeof(Im3DVertex), 0 },
+ { ATTRIB_NORMAL, GL_FLOAT, GL_FALSE, 3,
+ sizeof(Im3DVertex), offsetof(Im3DVertex, normal) },
+ { ATTRIB_COLOR, GL_UNSIGNED_BYTE, GL_TRUE, 4,
+ sizeof(Im3DVertex), offsetof(Im3DVertex, r) },
+ { ATTRIB_TEXCOORDS0, GL_FLOAT, GL_FALSE, 2,
+ sizeof(Im3DVertex), offsetof(Im3DVertex, u) },
+};
+static uint32 im3DVbo, im3DIbo;
+#ifdef RW_GL_USE_VAOS
+static uint32 im3DVao;
+#endif
+static int32 num3DVertices; // not actually needed here
+
+void
+openIm3D(void)
+{
+#include "shaders/im3d_gl.inc"
+#include "shaders/simple_fs_gl.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, im3d_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil };
+ im3dShader = Shader::create(vs, fs);
+ assert(im3dShader);
+
+ glGenBuffers(1, &im3DIbo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im3DIbo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, STARTINDICES*2, nil, GL_STREAM_DRAW);
+
+ glGenBuffers(1, &im3DVbo);
+ glBindBuffer(GL_ARRAY_BUFFER, im3DVbo);
+ glBufferData(GL_ARRAY_BUFFER, STARTVERTICES*sizeof(Im3DVertex), nil, GL_STREAM_DRAW);
+
+#ifdef RW_GL_USE_VAOS
+ glGenVertexArrays(1, &im3DVao);
+ glBindVertexArray(im3DVao);
+ setAttribPointers(im3dattribDesc, 4);
+#endif
+}
+
+void
+closeIm3D(void)
+{
+ glDeleteBuffers(1, &im3DIbo);
+ glDeleteBuffers(1, &im3DVbo);
+#ifdef RW_GL_USE_VAOS
+ glDeleteVertexArrays(1, &im3DVao);
+#endif
+ im3dShader->destroy();
+ im3dShader = nil;
+}
+
+// settable by user - TOOD: make this less shit
+RGBA im3dMaterialColor = { 255, 255, 255, 255 };
+SurfaceProperties im3dSurfaceProps = { 1.0f, 1.0f, 1.0f };
+
+void
+im3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags)
+{
+ if(world == nil){
+ static Matrix ident;
+ ident.setIdentity();
+ world = &ident;
+ }
+ setWorldMatrix(world);
+ if(flags & im3d::LIGHTING){
+ setMaterial(im3dMaterialColor, im3dSurfaceProps);
+ int32 vsBits = lightingCB();
+ defaultShader_fullLight->use();
+ }else
+ im3dShader->use();
+
+ if((flags & im3d::VERTEXUV) == 0)
+ SetRenderStatePtr(TEXTURERASTER, nil);
+
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(im2DVao);
+#endif
+
+ glBindBuffer(GL_ARRAY_BUFFER, im3DVbo);
+ glBufferData(GL_ARRAY_BUFFER, STARTVERTICES*sizeof(Im3DVertex), nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices*sizeof(Im3DVertex), vertices);
+#ifndef RW_GL_USE_VAOS
+ setAttribPointers(im3dattribDesc, 4);
+#endif
+ num3DVertices = numVertices;
+}
+
+void
+im3DRenderPrimitive(PrimitiveType primType)
+{
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im3DIbo);
+
+ flushCache();
+ glDrawArrays(primTypeMap[primType], 0, num3DVertices);
+}
+
+void
+im3DRenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices)
+{
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, im3DIbo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, STARTINDICES*2, nil, GL_STREAM_DRAW);
+ glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numIndices*2, indices);
+
+ flushCache();
+ glDrawElements(primTypeMap[primType], numIndices,
+ GL_UNSIGNED_SHORT, nil);
+}
+
+void
+im3DEnd(void)
+{
+#ifndef RW_GL_USE_VAOS
+ disableAttribPointers(im3dattribDesc, 4);
+#endif
+}
+
+}
+}
+
+#endif
diff --git a/src/gl/gl3matfx.cpp b/src/gl/gl3matfx.cpp
new file mode 100644
index 0000000..06b01c1
--- /dev/null
+++ b/src/gl/gl3matfx.cpp
@@ -0,0 +1,258 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwrender.h"
+#include "../rwengine.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+#include "rwgl3plg.h"
+
+#include "rwgl3impl.h"
+
+namespace rw {
+namespace gl3 {
+
+#ifdef RW_OPENGL
+
+static Shader *envShader, *envShader_noAT;
+static Shader *envShader_fullLight, *envShader_fullLight_noAT;
+static int32 u_texMatrix;
+static int32 u_fxparams;
+static int32 u_colorClamp;
+static int32 u_envColor;
+
+void
+matfxDefaultRender(InstanceDataHeader *header, InstanceData *inst, int32 vsBits, uint32 flags)
+{
+ Material *m;
+ m = inst->material;
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+ if((vsBits & VSLIGHT_MASK) == 0){
+ if(getAlphaTest())
+ defaultShader->use();
+ else
+ defaultShader_noAT->use();
+ }else{
+ if(getAlphaTest())
+ defaultShader_fullLight->use();
+ else
+ defaultShader_fullLight_noAT->use();
+ }
+
+ drawInst(header, inst);
+}
+
+static Frame *lastEnvFrame;
+
+static RawMatrix normal2texcoord = {
+ { 0.5f, 0.0f, 0.0f }, 0.0f,
+ { 0.0f, -0.5f, 0.0f }, 0.0f,
+ { 0.0f, 0.0f, 1.0f }, 0.0f,
+ { 0.5f, 0.5f, 0.0f }, 1.0f
+};
+
+void
+uploadEnvMatrix(Frame *frame)
+{
+ Matrix invMat;
+ if(frame == nil)
+ frame = engine->currentCamera->getFrame();
+
+ // cache the matrix across multiple meshes
+ static RawMatrix envMtx;
+// can't do it, frame matrix may change
+// if(frame != lastEnvFrame){
+// lastEnvFrame = frame;
+ {
+
+ RawMatrix invMtx;
+ Matrix::invert(&invMat, frame->getLTM());
+ convMatrix(&invMtx, &invMat);
+ invMtx.pos.set(0.0f, 0.0f, 0.0f);
+ float uscale = fabs(normal2texcoord.right.x);
+ normal2texcoord.right.x = MatFX::envMapFlipU ? -uscale : uscale;
+ RawMatrix::mult(&envMtx, &invMtx, &normal2texcoord);
+ }
+ setUniform(u_texMatrix, &envMtx);
+}
+
+void
+matfxEnvRender(InstanceDataHeader *header, InstanceData *inst, int32 vsBits, uint32 flags, MatFX::Env *env)
+{
+ Material *m;
+ m = inst->material;
+
+ if(env->tex == nil || env->coefficient == 0.0f){
+ matfxDefaultRender(header, inst, vsBits, flags);
+ return;
+ }
+
+ setTexture(0, m->texture);
+ setTexture(1, env->tex);
+ uploadEnvMatrix(env->frame);
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ float fxparams[4];
+ fxparams[0] = env->coefficient;
+ fxparams[1] = env->fbAlpha ? 0.0f : 1.0f;
+ fxparams[2] = fxparams[3] = 0.0f;
+
+ setUniform(u_fxparams, fxparams);
+ static float zero[4];
+ static float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ // This clamps the vertex color below. With it we can achieve both PC and PS2 style matfx
+ if(MatFX::envMapApplyLight)
+ setUniform(u_colorClamp, zero);
+ else
+ setUniform(u_colorClamp, one);
+ RGBAf envcol[4];
+ if(MatFX::envMapUseMatColor)
+ convColor(envcol, &m->color);
+ else
+ convColor(envcol, &MatFX::envMapColor);
+ setUniform(u_envColor, envcol);
+
+ rw::SetRenderState(VERTEXALPHA, 1);
+ rw::SetRenderState(SRCBLEND, BLENDONE);
+
+ if((vsBits & VSLIGHT_MASK) == 0){
+ if(getAlphaTest())
+ envShader->use();
+ else
+ envShader_noAT->use();
+ }else{
+ if(getAlphaTest())
+ envShader_fullLight->use();
+ else
+ envShader_fullLight_noAT->use();
+ }
+
+ drawInst(header, inst);
+
+ rw::SetRenderState(SRCBLEND, BLENDSRCALPHA);
+}
+
+void
+matfxRenderCB(Atomic *atomic, InstanceDataHeader *header)
+{
+ uint32 flags = atomic->geometry->flags;
+ setWorldMatrix(atomic->getFrame()->getLTM());
+ int32 vsBits = lightingCB(atomic);
+
+ setupVertexInput(header);
+
+ lastEnvFrame = nil;
+
+ InstanceData *inst = header->inst;
+ int32 n = header->numMeshes;
+
+ while(n--){
+ MatFX *matfx = MatFX::get(inst->material);
+
+ if(matfx == nil)
+ matfxDefaultRender(header, inst, vsBits, flags);
+ else switch(matfx->type){
+ case MatFX::ENVMAP:
+ matfxEnvRender(header, inst, vsBits, flags, &matfx->fx[0].env);
+ break;
+ default:
+ matfxDefaultRender(header, inst, vsBits, flags);
+ break;
+ }
+ inst++;
+ }
+ teardownVertexInput(header);
+}
+
+ObjPipeline*
+makeMatFXPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->renderCB = matfxRenderCB;
+ pipe->pluginID = ID_MATFX;
+ pipe->pluginData = 0;
+ return pipe;
+}
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+ matFXGlobals.pipelines[PLATFORM_GL3] = makeMatFXPipeline();
+
+#include "shaders/matfx_gl.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, matfx_env_vert_src, nil };
+ const char *vs_fullLight[] = { shaderDecl, "#define DIRECTIONALS\n#define POINTLIGHTS\n#define SPOTLIGHTS\n", header_vert_src, matfx_env_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, matfx_env_frag_src, nil };
+ const char *fs_noAT[] = { shaderDecl, "#define NO_ALPHATEST\n", header_frag_src, matfx_env_frag_src, nil };
+
+ envShader = Shader::create(vs, fs);
+ assert(envShader);
+ envShader_noAT = Shader::create(vs, fs_noAT);
+ assert(envShader_noAT);
+
+ envShader_fullLight = Shader::create(vs_fullLight, fs);
+ assert(envShader_fullLight);
+ envShader_fullLight_noAT = Shader::create(vs_fullLight, fs_noAT);
+ assert(envShader_fullLight_noAT);
+
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_GL3])->destroy();
+ matFXGlobals.pipelines[PLATFORM_GL3] = nil;
+
+ envShader->destroy();
+ envShader = nil;
+ envShader_noAT->destroy();
+ envShader_noAT = nil;
+ envShader_fullLight->destroy();
+ envShader_fullLight = nil;
+ envShader_fullLight_noAT->destroy();
+ envShader_fullLight_noAT = nil;
+
+ return o;
+}
+
+void
+initMatFX(void)
+{
+ u_texMatrix = registerUniform("u_texMatrix", UNIFORM_MAT4);
+ u_fxparams = registerUniform("u_fxparams", UNIFORM_VEC4);
+ u_colorClamp = registerUniform("u_colorClamp", UNIFORM_VEC4);
+ u_envColor = registerUniform("u_envColor", UNIFORM_VEC4);
+
+ Driver::registerPlugin(PLATFORM_GL3, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+}
+
+#else
+
+void initMatFX(void) { }
+
+#endif
+
+}
+}
+
diff --git a/src/gl/gl3pipe.cpp b/src/gl/gl3pipe.cpp
new file mode 100644
index 0000000..4df0b7c
--- /dev/null
+++ b/src/gl/gl3pipe.cpp
@@ -0,0 +1,333 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+
+namespace rw {
+namespace gl3 {
+
+// TODO: make some of these things platform-independent
+
+#ifdef RW_OPENGL
+
+void
+freeInstanceData(Geometry *geometry)
+{
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_GL3)
+ return;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ geometry->instData = nil;
+ glDeleteBuffers(1, &header->ibo);
+ glDeleteBuffers(1, &header->vbo);
+#ifdef RW_GL_USE_VAOS
+ glDeleteBuffers(1, &header->vao);
+#endif
+ rwFree(header->indexBuffer);
+ rwFree(header->vertexBuffer);
+ rwFree(header->attribDesc);
+ rwFree(header->inst);
+ rwFree(header);
+}
+
+void*
+destroyNativeData(void *object, int32, int32)
+{
+ freeInstanceData((Geometry*)object);
+ return object;
+}
+
+static InstanceDataHeader*
+instanceMesh(rw::ObjPipeline *rwpipe, Geometry *geo)
+{
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ MeshHeader *meshh = geo->meshHeader;
+ geo->instData = header;
+ header->platform = PLATFORM_GL3;
+
+ header->serialNumber = meshh->serialNum;
+ header->numMeshes = meshh->numMeshes;
+ header->primType = meshh->flags == 1 ? GL_TRIANGLE_STRIP : GL_TRIANGLES;
+ header->totalNumVertex = geo->numVertices;
+ header->totalNumIndex = meshh->totalIndices;
+ header->inst = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+
+ header->indexBuffer = rwNewT(uint16, header->totalNumIndex, MEMDUR_EVENT | ID_GEOMETRY);
+ InstanceData *inst = header->inst;
+ Mesh *mesh = meshh->getMeshes();
+ uint32 offset = 0;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ findMinVertAndNumVertices(mesh->indices, mesh->numIndices,
+ &inst->minVert, &inst->numVertices);
+ assert(inst->minVert != 0xFFFFFFFF);
+ inst->numIndex = mesh->numIndices;
+ inst->material = mesh->material;
+ inst->vertexAlpha = 0;
+ inst->program = 0;
+ inst->offset = offset;
+ memcpy((uint8*)header->indexBuffer + inst->offset,
+ mesh->indices, inst->numIndex*2);
+ offset += inst->numIndex*2;
+ mesh++;
+ inst++;
+ }
+
+ header->vertexBuffer = nil;
+ header->numAttribs = 0;
+ header->attribDesc = nil;
+ header->ibo = 0;
+ header->vbo = 0;
+
+#ifdef RW_GL_USE_VAOS
+ glGenVertexArrays(1, &header->vao);
+ glBindVertexArray(header->vao);
+#endif
+ glGenBuffers(1, &header->ibo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, header->totalNumIndex*2,
+ header->indexBuffer, GL_STATIC_DRAW);
+
+ return header;
+}
+
+static void
+instance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // don't try to (re)instance native data
+ if(geo->flags & Geometry::NATIVE)
+ return;
+
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ if(geo->instData){
+ // Already have instanced data, so check if we have to reinstance
+ assert(header->platform == PLATFORM_GL3);
+ if(header->serialNumber != geo->meshHeader->serialNum){
+ // Mesh changed, so reinstance everything
+ freeInstanceData(geo);
+ }
+ }
+
+ // no instance or complete reinstance
+ if(geo->instData == nil){
+ geo->instData = instanceMesh(rwpipe, geo);
+ pipe->instanceCB(geo, (InstanceDataHeader*)geo->instData, 0);
+ }else if(geo->lockedSinceInst)
+ pipe->instanceCB(geo, (InstanceDataHeader*)geo->instData, 1);
+
+ geo->lockedSinceInst = 0;
+}
+
+static void
+uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ assert(0 && "can't uninstance");
+}
+
+static void
+render(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ pipe->instance(atomic);
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_GL3);
+ if(pipe->renderCB)
+ pipe->renderCB(atomic, (InstanceDataHeader*)geo->instData);
+}
+
+void
+ObjPipeline::init(void)
+{
+ this->rw::ObjPipeline::init(PLATFORM_GL3);
+ this->impl.instance = gl3::instance;
+ this->impl.uninstance = gl3::uninstance;
+ this->impl.render = gl3::render;
+ this->instanceCB = nil;
+ this->uninstanceCB = nil;
+ this->renderCB = nil;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+void
+defaultInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance)
+{
+ AttribDesc *attribs, *a;
+
+ bool isPrelit = !!(geo->flags & Geometry::PRELIT);
+ bool hasNormals = !!(geo->flags & Geometry::NORMALS);
+
+ if(!reinstance){
+ AttribDesc tmpAttribs[12];
+ uint32 stride;
+
+ //
+ // Create attribute descriptions
+ //
+ a = tmpAttribs;
+ stride = 0;
+
+ // Positions
+ a->index = ATTRIB_POS;
+ a->size = 3;
+ a->type = GL_FLOAT;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 12;
+ a++;
+
+ // Normals
+ // TODO: compress
+ if(hasNormals){
+ a->index = ATTRIB_NORMAL;
+ a->size = 3;
+ a->type = GL_FLOAT;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 12;
+ a++;
+ }
+
+ // Prelighting
+ if(isPrelit){
+ a->index = ATTRIB_COLOR;
+ a->size = 4;
+ a->type = GL_UNSIGNED_BYTE;
+ a->normalized = GL_TRUE;
+ a->offset = stride;
+ stride += 4;
+ a++;
+ }
+
+ // Texture coordinates
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ a->index = ATTRIB_TEXCOORDS0+n;
+ a->size = 2;
+ a->type = GL_FLOAT;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 8;
+ a++;
+ }
+
+ header->numAttribs = a - tmpAttribs;
+ for(a = tmpAttribs; a != &tmpAttribs[header->numAttribs]; a++)
+ a->stride = stride;
+ header->attribDesc = rwNewT(AttribDesc, header->numAttribs, MEMDUR_EVENT | ID_GEOMETRY);
+ memcpy(header->attribDesc, tmpAttribs,
+ header->numAttribs*sizeof(AttribDesc));
+
+ //
+ // Allocate vertex buffer
+ //
+ header->vertexBuffer = rwNewT(uint8, header->totalNumVertex*stride, MEMDUR_EVENT | ID_GEOMETRY);
+ assert(header->vbo == 0);
+ glGenBuffers(1, &header->vbo);
+ }
+
+ attribs = header->attribDesc;
+
+ //
+ // Fill vertex buffer
+ //
+
+ uint8 *verts = header->vertexBuffer;
+
+ // Positions
+ if(!reinstance || geo->lockedSinceInst&Geometry::LOCKVERTICES){
+ for(a = attribs; a->index != ATTRIB_POS; a++)
+ ;
+ instV3d(VERT_FLOAT3, verts + a->offset,
+ geo->morphTargets[0].vertices,
+ header->totalNumVertex, a->stride);
+ }
+
+ // Normals
+ if(hasNormals && (!reinstance || geo->lockedSinceInst&Geometry::LOCKNORMALS)){
+ for(a = attribs; a->index != ATTRIB_NORMAL; a++)
+ ;
+ instV3d(VERT_FLOAT3, verts + a->offset,
+ geo->morphTargets[0].normals,
+ header->totalNumVertex, a->stride);
+ }
+
+ // Prelighting
+ if(isPrelit && (!reinstance || geo->lockedSinceInst&Geometry::LOCKPRELIGHT)){
+ for(a = attribs; a->index != ATTRIB_COLOR; a++)
+ ;
+ int n = header->numMeshes;
+ InstanceData *inst = header->inst;
+ while(n--){
+ assert(inst->minVert != 0xFFFFFFFF);
+ inst->vertexAlpha = instColor(VERT_RGBA,
+ verts + a->offset + a->stride*inst->minVert,
+ geo->colors + inst->minVert,
+ inst->numVertices, a->stride);
+ inst++;
+ }
+ }
+
+ // Texture coordinates
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ if(!reinstance || geo->lockedSinceInst&(Geometry::LOCKTEXCOORDS<<n)){
+ for(a = attribs; a->index != ATTRIB_TEXCOORDS0+n; a++)
+ ;
+ instTexCoords(VERT_FLOAT2, verts + a->offset,
+ geo->texCoords[n],
+ header->totalNumVertex, a->stride);
+ }
+ }
+
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(header->vao);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+ glBufferData(GL_ARRAY_BUFFER, header->totalNumVertex*attribs[0].stride,
+ header->vertexBuffer, GL_STATIC_DRAW);
+#ifdef RW_GL_USE_VAOS
+ setAttribPointers(header->attribDesc, header->numAttribs);
+ glBindVertexArray(0);
+#endif
+}
+
+void
+defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header)
+{
+ assert(0 && "can't uninstance");
+}
+
+ObjPipeline*
+makeDefaultPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = defaultInstanceCB;
+ pipe->uninstanceCB = defaultUninstanceCB;
+ pipe->renderCB = defaultRenderCB;
+ return pipe;
+}
+
+#else
+void *destroyNativeData(void *object, int32, int32) { return object; }
+#endif
+
+}
+}
diff --git a/src/gl/gl3raster.cpp b/src/gl/gl3raster.cpp
new file mode 100644
index 0000000..b7c3941
--- /dev/null
+++ b/src/gl/gl3raster.cpp
@@ -0,0 +1,1045 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+#include "rwgl3impl.h"
+
+#define PLUGIN_ID ID_DRIVER
+
+namespace rw {
+namespace gl3 {
+
+int32 nativeRasterOffset;
+
+static uint32
+getLevelSize(Raster *raster, int32 level)
+{
+ int i;
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ int w = raster->originalWidth;
+ int h = raster->originalHeight;
+ int s = raster->originalStride;
+ int minDim = 1;
+
+#ifdef RW_OPENGL
+ switch(natras->internalFormat){
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ minDim = 4;
+ break;
+ }
+#endif
+
+ for(i = 0; i < level; i++){
+ if(w > minDim){
+ w /= 2;
+ s /= 2;
+ }
+ if(h > minDim)
+ h /= 2;
+ }
+
+ return s*h;
+}
+
+#ifdef RW_OPENGL
+
+static Raster*
+rasterCreateTexture(Raster *raster)
+{
+ if(raster->format & (Raster::PAL4 | Raster::PAL8)){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ switch(raster->format & 0xF00){
+ case Raster::C8888:
+ natras->internalFormat = GL_RGBA8;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 1;
+ natras->bpp = 4;
+ raster->depth = 32;
+ break;
+ case Raster::C888:
+ natras->internalFormat = GL_RGB8;
+ natras->format = GL_RGB;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 0;
+ natras->bpp = 3;
+ raster->depth = 24;
+ break;
+ case Raster::C1555:
+ natras->internalFormat = GL_RGB5_A1;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
+ natras->hasAlpha = 1;
+ natras->bpp = 2;
+ raster->depth = 16;
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ if(gl3Caps.gles){
+ // glReadPixels only supports GL_RGBA
+ natras->internalFormat = GL_RGBA8;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->bpp = 4;
+ }
+
+ raster->stride = raster->width*natras->bpp;
+
+ if(raster->format & Raster::MIPMAP){
+ int w = raster->width;
+ int h = raster->height;
+ natras->numLevels = 1;
+ while(w != 1 || h != 1){
+ natras->numLevels++;
+ if(w > 1) w /= 2;
+ if(h > 1) h /= 2;
+ }
+ }
+ natras->autogenMipmap = (raster->format & (Raster::MIPMAP|Raster::AUTOMIPMAP)) == (Raster::MIPMAP|Raster::AUTOMIPMAP);
+ if(natras->autogenMipmap)
+ natras->numLevels = 1;
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ // TODO: allocate other levels...probably
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, natras->numLevels-1);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+ return raster;
+}
+
+static Raster*
+rasterCreateCameraTexture(Raster *raster)
+{
+ if(raster->format & (Raster::PAL4 | Raster::PAL8)){
+ RWERROR((ERR_NOTEXTURE));
+ return nil;
+ }
+
+ // TODO: figure out what the backbuffer is and use that as a default
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ switch(raster->format & 0xF00){
+ case Raster::C8888:
+ natras->internalFormat = GL_RGBA8;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 1;
+ natras->bpp = 4;
+ break;
+ case Raster::C888:
+ default:
+ natras->internalFormat = GL_RGB8;
+ natras->format = GL_RGB;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 0;
+ natras->bpp = 3;
+ break;
+ case Raster::C1555:
+ natras->internalFormat = GL_RGB5_A1;
+ natras->format = GL_RGBA;
+ natras->type = GL_UNSIGNED_SHORT_5_5_5_1;
+ natras->hasAlpha = 1;
+ natras->bpp = 2;
+ break;
+ }
+
+ // i don't remember why this was once here...
+ if(gl3Caps.gles){
+ // glReadPixels only supports GL_RGBA
+// natras->internalFormat = GL_RGBA8;
+// natras->format = GL_RGBA;
+// natras->type = GL_UNSIGNED_BYTE;
+// natras->bpp = 4;
+ }
+
+ raster->stride = raster->width*natras->bpp;
+
+ natras->autogenMipmap = (raster->format & (Raster::MIPMAP|Raster::AUTOMIPMAP)) == (Raster::MIPMAP|Raster::AUTOMIPMAP);
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+
+
+ glGenFramebuffers(1, &natras->fbo);
+ bindFramebuffer(natras->fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, natras->texid, 0);
+ bindFramebuffer(0);
+ natras->fboMate = nil;
+
+ return raster;
+}
+
+static Raster*
+rasterCreateCamera(Raster *raster)
+{
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ // TODO: set/check width, height, depth, format?
+
+ // used for locking right now
+ raster->format = Raster::C888;
+ natras->internalFormat = GL_RGB8;
+ natras->format = GL_RGB;
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = 0;
+ natras->bpp = 3;
+
+ natras->autogenMipmap = 0;
+
+ natras->texid = 0;
+ natras->fbo = 0;
+ natras->fboMate = nil;
+
+ return raster;
+}
+
+static Raster*
+rasterCreateZbuffer(Raster *raster)
+{
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ if(gl3Caps.gles){
+ // have to use RBO on GLES!!
+ glGenRenderbuffers(1, &natras->texid);
+ glBindRenderbuffer(GL_RENDERBUFFER, natras->texid);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, raster->width, raster->height);
+ }else{
+ // TODO: set/check width, height, depth, format?
+ natras->internalFormat = GL_DEPTH_STENCIL;
+ natras->format = GL_DEPTH_STENCIL;
+ natras->type = GL_UNSIGNED_INT_24_8;
+
+ natras->autogenMipmap = 0;
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+ }
+ natras->fbo = 0;
+ natras->fboMate = nil;
+
+ return raster;
+}
+
+
+#endif
+
+
+void
+allocateDXT(Raster *raster, int32 dxt, int32 numLevels, bool32 hasAlpha)
+{
+#ifdef RW_OPENGL
+ assert(raster->type == Raster::TEXTURE);
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ switch(dxt){
+ case 1:
+ if(hasAlpha){
+ natras->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+ natras->format = GL_RGBA;
+ }else{
+ natras->internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+ natras->format = GL_RGB;
+ }
+ // bogus, but stride*height should be the size of the image
+ // 4x4 in 8 bytes
+ raster->stride = raster->width/2;
+ break;
+ case 3:
+ natras->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
+ natras->format = GL_RGBA;
+ // 4x4 in 16 bytes
+ raster->stride = raster->width;
+ break;
+ case 5:
+ natras->internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
+ natras->format = GL_RGBA;
+ // 4x4 in 16 bytes
+ raster->stride = raster->width;
+ break;
+ default:
+ assert(0 && "invalid DXT format");
+ }
+ natras->type = GL_UNSIGNED_BYTE;
+ natras->hasAlpha = hasAlpha;
+ natras->bpp = 2;
+ raster->depth = 16;
+
+ natras->isCompressed = 1;
+ if(raster->format & Raster::MIPMAP)
+ natras->numLevels = numLevels;
+ natras->autogenMipmap = (raster->format & (Raster::MIPMAP|Raster::AUTOMIPMAP)) == (Raster::MIPMAP|Raster::AUTOMIPMAP);
+ if(natras->autogenMipmap)
+ natras->numLevels = 1;
+
+ glGenTextures(1, &natras->texid);
+ uint32 prev = bindTexture(natras->texid);
+ glTexImage2D(GL_TEXTURE_2D, 0, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, nil);
+ // TODO: allocate other levels...probably
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, natras->numLevels-1);
+ natras->filterMode = 0;
+ natras->addressU = 0;
+ natras->addressV = 0;
+ natras->maxAnisotropy = 1;
+
+ bindTexture(prev);
+
+ raster->originalStride = raster->stride;
+
+ if(gl3Caps.gles && needToReadBackTextures){
+ // Uh oh, need to keep a copy in cpu memory
+ int32 i;
+ int32 size = 0;
+ for(i = 0; i < natras->numLevels; i++)
+ size += getLevelSize(raster, i);
+ uint8 *data = (uint8*)rwNew(sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(natras->numLevels-1)+size,
+ MEMDUR_EVENT | ID_DRIVER);
+ RasterLevels *levels = (RasterLevels*)data;
+ data += sizeof(RasterLevels)+sizeof(RasterLevels::Level)*(natras->numLevels-1);
+ levels->numlevels = natras->numLevels;
+ levels->format = 0; // not needed
+ for(i = 0; i < natras->numLevels; i++){
+ levels->levels[i].data = data;
+ levels->levels[i].size = getLevelSize(raster, i);
+ levels->levels[i].width = 0; // we don't need those here, maybe later...
+ levels->levels[i].height = 0;
+ data += levels->levels[i].size;
+ }
+ natras->backingStore = levels;
+ }
+
+ raster->flags &= ~Raster::DONTALLOCATE;
+#endif
+}
+
+/*
+{ 0, 0, 0 },
+{ 16, 4, GL_RGBA }, // 1555
+{ 16, 3, GL_RGB }, // 565
+{ 16, 4, GL_RGBA }, // 4444
+{ 0, 0, 0 }, // LUM8
+{ 32, 4, GL_RGBA }, // 8888
+{ 24, 3, GL_RGB }, // 888
+{ 16, 3, GL_RGB }, // D16
+{ 24, 3, GL_RGB }, // D24
+{ 32, 4, GL_RGBA }, // D32
+{ 16, 3, GL_RGB }, // 555
+
+0,
+GL_RGB5_A1,
+GL_RGB5,
+GL_RGBA4,
+0,
+GL_RGBA8,
+GL_RGB8,
+GL_RGB5,
+GL_RGB8,
+GL_RGBA8,
+GL_RGB5
+*/
+
+Raster*
+rasterCreate(Raster *raster)
+{
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ natras->isCompressed = 0;
+ natras->hasAlpha = 0;
+ natras->numLevels = 1;
+
+ Raster *ret = raster;
+
+ if(raster->width == 0 || raster->height == 0){
+ raster->flags |= Raster::DONTALLOCATE;
+ raster->stride = 0;
+ goto ret;
+ }
+ if(raster->flags & Raster::DONTALLOCATE)
+ goto ret;
+
+ switch(raster->type){
+#ifdef RW_OPENGL
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ ret = rasterCreateTexture(raster);
+ break;
+ case Raster::CAMERATEXTURE:
+ ret = rasterCreateCameraTexture(raster);
+ break;
+ case Raster::ZBUFFER:
+ ret = rasterCreateZbuffer(raster);
+ break;
+ case Raster::CAMERA:
+ ret = rasterCreateCamera(raster);
+ break;
+#endif
+
+ default:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ret:
+ raster->originalWidth = raster->width;
+ raster->originalHeight = raster->height;
+ raster->originalStride = raster->stride;
+ raster->originalPixels = raster->pixels;
+ return ret;
+}
+
+uint8*
+rasterLock(Raster *raster, int32 level, int32 lockMode)
+{
+#ifdef RW_OPENGL
+ Gl3Raster *natras GETGL3RASTEREXT(raster);
+ uint8 *px;
+ uint32 allocSz;
+ int i;
+
+ assert(raster->privateFlags == 0);
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ case Raster::CAMERATEXTURE:
+ for(i = 0; i < level; i++){
+ if(raster->width > 1){
+ raster->width /= 2;
+ raster->stride /= 2;
+ }
+ if(raster->height > 1)
+ raster->height /= 2;
+ }
+
+ allocSz = getLevelSize(raster, level);
+ px = (uint8*)rwMalloc(allocSz, MEMDUR_EVENT | ID_DRIVER);
+ assert(raster->pixels == nil);
+ raster->pixels = px;
+
+ if(lockMode & Raster::LOCKREAD || !(lockMode & Raster::LOCKNOFETCH)){
+ if(natras->isCompressed){
+ if(natras->backingStore){
+ assert(level < natras->backingStore->numlevels);
+ assert(allocSz >= natras->backingStore->levels[level].size);
+ memcpy(px, natras->backingStore->levels[level].data, allocSz);
+ }else{
+ // GLES is losing here
+ uint32 prev = bindTexture(natras->texid);
+ glGetCompressedTexImage(GL_TEXTURE_2D, level, px);
+ bindTexture(prev);
+ }
+ }else if(gl3Caps.gles){
+ GLuint fbo;
+ glGenFramebuffers(1, &fbo);
+ bindFramebuffer(fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, natras->texid, 0);
+ GLenum e = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+assert(natras->format == GL_RGBA);
+ glReadPixels(0, 0, raster->width, raster->height, natras->format, natras->type, px);
+//e = glGetError(); printf("GL err4 %x (%x)\n", e, natras->format);
+ bindFramebuffer(0);
+ glDeleteFramebuffers(1, &fbo);
+ }else{
+ uint32 prev = bindTexture(natras->texid);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ glGetTexImage(GL_TEXTURE_2D, level, natras->format, natras->type, px);
+ bindTexture(prev);
+ }
+ }
+
+ raster->privateFlags = lockMode;
+ break;
+
+ case Raster::CAMERA:
+ if(lockMode & Raster::PRIVATELOCK_WRITE)
+ assert(0 && "can't lock framebuffer for writing");
+ raster->width = glGlobals.presentWidth;
+ raster->height = glGlobals.presentHeight;
+ raster->stride = raster->width*natras->bpp;
+ assert(natras->bpp == 3);
+ allocSz = raster->height*raster->stride;
+ px = (uint8*)rwMalloc(allocSz, MEMDUR_EVENT | ID_DRIVER);
+ assert(raster->pixels == nil);
+ raster->pixels = px;
+ glReadBuffer(GL_BACK);
+ glReadPixels(0, 0, raster->width, raster->height, GL_RGB, GL_UNSIGNED_BYTE, px);
+
+ raster->privateFlags = lockMode;
+ break;
+
+ default:
+ assert(0 && "cannot lock this type of raster yet");
+ return nil;
+ }
+
+ return px;
+#else
+ return nil;
+#endif
+}
+
+void
+rasterUnlock(Raster *raster, int32 level)
+{
+#ifdef RW_OPENGL
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ assert(raster->pixels);
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ case Raster::CAMERATEXTURE:
+ if(raster->privateFlags & Raster::LOCKWRITE){
+ uint32 prev = bindTexture(natras->texid);
+ if(natras->isCompressed){
+ glCompressedTexImage2D(GL_TEXTURE_2D, level, natras->internalFormat,
+ raster->width, raster->height, 0,
+ getLevelSize(raster, level),
+ raster->pixels);
+ if(natras->backingStore){
+ assert(level < natras->backingStore->numlevels);
+ memcpy(natras->backingStore->levels[level].data, raster->pixels,
+ natras->backingStore->levels[level].size);
+ }
+ }else{
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D(GL_TEXTURE_2D, level, natras->internalFormat,
+ raster->width, raster->height,
+ 0, natras->format, natras->type, raster->pixels);
+ }
+ if(level == 0 && natras->autogenMipmap)
+ glGenerateMipmap(GL_TEXTURE_2D);
+ bindTexture(prev);
+ }
+ break;
+
+ case Raster::CAMERA:
+ // TODO: write?
+ break;
+ }
+
+ rwFree(raster->pixels);
+ raster->pixels = nil;
+#endif
+ raster->width = raster->originalWidth;
+ raster->height = raster->originalHeight;
+ raster->stride = raster->originalStride;
+ raster->pixels = raster->originalPixels;
+ raster->privateFlags = 0;
+}
+
+int32
+rasterNumLevels(Raster *raster)
+{
+ return GETGL3RASTEREXT(raster)->numLevels;
+}
+
+// Almost the same as d3d9 and ps2 function
+bool32
+imageFindRasterFormat(Image *img, int32 type,
+ int32 *pWidth, int32 *pHeight, int32 *pDepth, int32 *pFormat)
+{
+ int32 width, height, depth, format;
+
+ assert((type&0xF) == Raster::TEXTURE);
+
+// for(width = 1; width < img->width; width <<= 1);
+// for(height = 1; height < img->height; height <<= 1);
+ // Perhaps non-power-of-2 textures are acceptable?
+ width = img->width;
+ height = img->height;
+
+ depth = img->depth;
+
+ if(depth <= 8)
+ depth = 32;
+
+ switch(depth){
+ case 32:
+ if(img->hasAlpha())
+ format = Raster::C8888;
+ else{
+ format = Raster::C888;
+ depth = 24;
+ }
+ break;
+ case 24:
+ format = Raster::C888;
+ break;
+ case 16:
+ format = Raster::C1555;
+ break;
+
+ case 8:
+ case 4:
+ default:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ format |= type;
+
+ *pWidth = width;
+ *pHeight = height;
+ *pDepth = depth;
+ *pFormat = format;
+
+ return 1;
+}
+
+bool32
+rasterFromImage(Raster *raster, Image *image)
+{
+ if((raster->type&0xF) != Raster::TEXTURE)
+ return 0;
+
+ void (*conv)(uint8 *out, uint8 *in) = nil;
+
+ // Unpalettize image if necessary but don't change original
+ Image *truecolimg = nil;
+ if(image->depth <= 8){
+ truecolimg = Image::create(image->width, image->height, image->depth);
+ truecolimg->pixels = image->pixels;
+ truecolimg->stride = image->stride;
+ truecolimg->palette = image->palette;
+ truecolimg->unpalettize();
+ image = truecolimg;
+ }
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ int32 format = raster->format&0xF00;
+ assert(!natras->isCompressed);
+ switch(image->depth){
+ case 32:
+ if(gl3Caps.gles)
+ conv = conv_RGBA8888_from_RGBA8888;
+ else if(format == Raster::C8888)
+ conv = conv_RGBA8888_from_RGBA8888;
+ else if(format == Raster::C888)
+ conv = conv_RGB888_from_RGB888;
+ else
+ goto err;
+ break;
+ case 24:
+ if(gl3Caps.gles)
+ conv = conv_RGBA8888_from_RGB888;
+ else if(format == Raster::C8888)
+ conv = conv_RGBA8888_from_RGB888;
+ else if(format == Raster::C888)
+ conv = conv_RGB888_from_RGB888;
+ else
+ goto err;
+ break;
+ case 16:
+ if(gl3Caps.gles)
+ conv = conv_RGBA8888_from_ARGB1555;
+ else if(format == Raster::C1555)
+ conv = conv_RGBA5551_from_ARGB1555;
+ else
+ goto err;
+ break;
+
+ case 8:
+ case 4:
+ default:
+ err:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ natras->hasAlpha = image->hasAlpha();
+
+ bool unlock = false;
+ if(raster->pixels == nil){
+ raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ unlock = true;
+ }
+
+ uint8 *pixels = raster->pixels;
+ assert(pixels);
+ uint8 *imgpixels = image->pixels + (image->height-1)*image->stride;
+
+ int x, y;
+ assert(image->width == raster->width);
+ assert(image->height == raster->height);
+ for(y = 0; y < image->height; y++){
+ uint8 *imgrow = imgpixels;
+ uint8 *rasrow = pixels;
+ for(x = 0; x < image->width; x++){
+ conv(rasrow, imgrow);
+ imgrow += image->bpp;
+ rasrow += natras->bpp;
+ }
+ imgpixels -= image->stride;
+ pixels += raster->stride;
+ }
+ if(unlock)
+ raster->unlock(0);
+
+ if(truecolimg)
+ truecolimg->destroy();
+
+ return 1;
+}
+
+Image*
+rasterToImage(Raster *raster)
+{
+ int32 depth;
+ Image *image;
+
+ bool unlock = false;
+ if(raster->pixels == nil){
+ raster->lock(0, Raster::LOCKREAD);
+ unlock = true;
+ }
+
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+ if(natras->isCompressed){
+ // TODO
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ void (*conv)(uint8 *out, uint8 *in) = nil;
+ switch(raster->format & 0xF00){
+ case Raster::C1555:
+ depth = 16;
+ conv = conv_ARGB1555_from_RGBA5551;
+ break;
+ case Raster::C8888:
+ depth = 32;
+ conv = conv_RGBA8888_from_RGBA8888;
+ break;
+ case Raster::C888:
+ depth = 24;
+ conv = conv_RGB888_from_RGB888;
+ break;
+
+ default:
+ case Raster::C555:
+ case Raster::C565:
+ case Raster::C4444:
+ case Raster::LUM8:
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ if(raster->format & Raster::PAL4 ||
+ raster->format & Raster::PAL8){
+ RWERROR((ERR_INVRASTER));
+ return nil;
+ }
+
+ uint8 *in, *out;
+ image = Image::create(raster->width, raster->height, depth);
+ image->allocate();
+
+ uint8 *imgpixels = image->pixels + (image->height-1)*image->stride;
+ uint8 *pixels = raster->pixels;
+
+ int x, y;
+ assert(image->width == raster->width);
+ assert(image->height == raster->height);
+ for(y = 0; y < image->height; y++){
+ uint8 *imgrow = imgpixels;
+ uint8 *rasrow = pixels;
+ for(x = 0; x < image->width; x++){
+ conv(imgrow, rasrow);
+ imgrow += image->bpp;
+ rasrow += natras->bpp;
+ }
+ imgpixels -= image->stride;
+ pixels += raster->stride;
+ }
+
+ if(unlock)
+ raster->unlock(0);
+
+ return image;
+}
+
+static void*
+createNativeRaster(void *object, int32 offset, int32)
+{
+ Gl3Raster *ras = PLUGINOFFSET(Gl3Raster, object, offset);
+ ras->texid = 0;
+ ras->fbo = 0;
+ ras->fboMate = nil;
+ ras->backingStore = nil;
+ return object;
+}
+
+void evictRaster(Raster *raster);
+
+static void*
+destroyNativeRaster(void *object, int32 offset, int32)
+{
+ Raster *raster = (Raster*)object;
+ Gl3Raster *natras = PLUGINOFFSET(Gl3Raster, object, offset);
+#ifdef RW_OPENGL
+ evictRaster(raster);
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ glDeleteTextures(1, &natras->texid);
+ break;
+
+ case Raster::CAMERATEXTURE:
+ if(natras->fboMate){
+ // Break apart from currently associated zbuffer
+ Gl3Raster *zras = GETGL3RASTEREXT(natras->fboMate);
+ zras->fboMate = nil;
+ natras->fboMate = nil;
+ }
+ glDeleteFramebuffers(1, &natras->fbo);
+ glDeleteTextures(1, &natras->texid);
+ break;
+
+ case Raster::ZBUFFER:
+ if(natras->fboMate){
+ // Detatch from FBO we may be attached to
+ Gl3Raster *oldfb = GETGL3RASTEREXT(natras->fboMate);
+ if(oldfb->fbo){
+ bindFramebuffer(oldfb->fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
+ }
+ oldfb->fboMate = nil;
+ }
+ if(gl3Caps.gles)
+ glDeleteRenderbuffers(1, &natras->texid);
+ else
+ glDeleteTextures(1, &natras->texid);
+ break;
+
+ case Raster::CAMERA:
+ if(natras->fboMate){
+ // Break apart from currently associated zbuffer
+ Gl3Raster *zras = GETGL3RASTEREXT(natras->fboMate);
+ zras->fboMate = nil;
+ natras->fboMate = nil;
+ }
+ break;
+ }
+ natras->texid = 0;
+ natras->fbo = 0;
+
+ free(natras->backingStore);
+
+#endif
+ return object;
+}
+
+static void*
+copyNativeRaster(void *dst, void *, int32 offset, int32)
+{
+ Gl3Raster *d = PLUGINOFFSET(Gl3Raster, dst, offset);
+ d->texid = 0;
+ d->fbo = 0;
+ d->fboMate = nil;
+ d->backingStore = nil;
+ return dst;
+}
+
+Texture*
+readNativeTexture(Stream *stream)
+{
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_GL3){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ Texture *tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+
+ // Texture
+ tex->filterAddressing = stream->readU32();
+ stream->read8(tex->name, 32);
+ stream->read8(tex->mask, 32);
+
+ // Raster
+ uint32 format = stream->readU32();
+ int32 width = stream->readI32();
+ int32 height = stream->readI32();
+ int32 depth = stream->readI32();
+ int32 numLevels = stream->readI32();
+
+ // Native raster
+ int32 subplatform = stream->readI32();
+ int32 flags = stream->readI32();
+ int32 compression = stream->readI32();
+
+ if(subplatform != gl3Caps.gles){
+ tex->destroy();
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+
+ Raster *raster;
+ Gl3Raster *natras;
+ if(flags & 2){
+ if(!gl3Caps.dxtSupported){
+ tex->destroy();
+ RWERROR((ERR_FORMAT_UNSUPPORTED));
+ return nil;
+ }
+ raster = Raster::create(width, height, depth, format | Raster::TEXTURE | Raster::DONTALLOCATE, PLATFORM_GL3);
+ allocateDXT(raster, compression, numLevels, flags & 1);
+ }else{
+ raster = Raster::create(width, height, depth, format | Raster::TEXTURE, PLATFORM_GL3);
+ }
+ assert(raster);
+ natras = GETGL3RASTEREXT(raster);
+ tex->raster = raster;
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = stream->readU32();
+ data = raster->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ stream->read8(data, size);
+ raster->unlock(i);
+ }
+ return tex;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+ Raster *raster = tex->raster;
+ Gl3Raster *natras = GETGL3RASTEREXT(raster);
+
+ int32 chunksize = getSizeNativeTexture(tex);
+ writeChunkHeader(stream, ID_STRUCT, chunksize-12);
+ stream->writeU32(PLATFORM_GL3);
+
+ // Texture
+ stream->writeU32(tex->filterAddressing);
+ stream->write8(tex->name, 32);
+ stream->write8(tex->mask, 32);
+
+ // Raster
+ int32 numLevels = natras->numLevels;
+ stream->writeI32(raster->format);
+ stream->writeI32(raster->width);
+ stream->writeI32(raster->height);
+ stream->writeI32(raster->depth);
+ stream->writeI32(numLevels);
+
+ // Native raster
+ int32 flags = 0;
+ int32 compression = 0;
+ if(natras->hasAlpha)
+ flags |= 1;
+ if(natras->isCompressed){
+ flags |= 2;
+ switch(natras->internalFormat){
+#ifdef RW_OPENGL
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+ case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+ compression = 1;
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+ compression = 3;
+ break;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+ compression = 5;
+ break;
+#endif
+ default:
+ assert(0 && "unknown compression");
+ }
+ }
+ stream->writeI32(gl3Caps.gles);
+ stream->writeI32(flags);
+ stream->writeI32(compression);
+ // TODO: auto mipmaps?
+
+ uint32 size;
+ uint8 *data;
+ for(int32 i = 0; i < numLevels; i++){
+ size = getLevelSize(raster, i);
+ stream->writeU32(size);
+ data = raster->lock(i, Raster::LOCKREAD);
+ stream->write8(data, size);
+ raster->unlock(i);
+ }
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+ uint32 size = 12 + 72 + 32;
+ int32 levels = tex->raster->getNumLevels();
+ for(int32 i = 0; i < levels; i++)
+ size += 4 + getLevelSize(tex->raster, i);
+ return size;
+}
+
+
+
+void registerNativeRaster(void)
+{
+ nativeRasterOffset = Raster::registerPlugin(sizeof(Gl3Raster),
+ ID_RASTERGL3,
+ createNativeRaster,
+ destroyNativeRaster,
+ copyNativeRaster);
+}
+
+}
+}
diff --git a/src/gl/gl3render.cpp b/src/gl/gl3render.cpp
new file mode 100644
index 0000000..a02c5c0
--- /dev/null
+++ b/src/gl/gl3render.cpp
@@ -0,0 +1,193 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwrender.h"
+#include "../rwengine.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#ifdef RW_OPENGL
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+
+#include "rwgl3impl.h"
+
+namespace rw {
+namespace gl3 {
+
+#define MAX_LIGHTS
+
+void
+drawInst_simple(InstanceDataHeader *header, InstanceData *inst)
+{
+ flushCache();
+ glDrawElements(header->primType, inst->numIndex,
+ GL_UNSIGNED_SHORT, (void*)(uintptr)inst->offset);
+}
+
+// Emulate PS2 GS alpha test FB_ONLY case: failed alpha writes to frame- but not to depth buffer
+void
+drawInst_GSemu(InstanceDataHeader *header, InstanceData *inst)
+{
+ uint32 hasAlpha;
+ int alphafunc, alpharef, gsalpharef;
+ int zwrite;
+ hasAlpha = getAlphaBlend();
+ if(hasAlpha){
+ zwrite = rw::GetRenderState(rw::ZWRITEENABLE);
+ alphafunc = rw::GetRenderState(rw::ALPHATESTFUNC);
+ if(zwrite){
+ alpharef = rw::GetRenderState(rw::ALPHATESTREF);
+ gsalpharef = rw::GetRenderState(rw::GSALPHATESTREF);
+
+ SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL);
+ SetRenderState(rw::ALPHATESTREF, gsalpharef);
+ drawInst_simple(header, inst);
+ SetRenderState(rw::ALPHATESTFUNC, rw::ALPHALESS);
+ SetRenderState(rw::ZWRITEENABLE, 0);
+ drawInst_simple(header, inst);
+ SetRenderState(rw::ZWRITEENABLE, 1);
+ SetRenderState(rw::ALPHATESTFUNC, alphafunc);
+ SetRenderState(rw::ALPHATESTREF, alpharef);
+ }else{
+ SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAALWAYS);
+ drawInst_simple(header, inst);
+ SetRenderState(rw::ALPHATESTFUNC, alphafunc);
+ }
+ }else
+ drawInst_simple(header, inst);
+}
+
+void
+drawInst(InstanceDataHeader *header, InstanceData *inst)
+{
+ if(rw::GetRenderState(rw::GSALPHATEST))
+ drawInst_GSemu(header, inst);
+ else
+ drawInst_simple(header, inst);
+}
+
+
+void
+setAttribPointers(AttribDesc *attribDescs, int32 numAttribs)
+{
+ AttribDesc *a;
+ for(a = attribDescs; a != &attribDescs[numAttribs]; a++){
+ glEnableVertexAttribArray(a->index);
+ glVertexAttribPointer(a->index, a->size, a->type, a->normalized,
+ a->stride, (void*)(uint64)a->offset);
+ }
+}
+
+void
+disableAttribPointers(AttribDesc *attribDescs, int32 numAttribs)
+{
+ AttribDesc *a;
+ for(a = attribDescs; a != &attribDescs[numAttribs]; a++)
+ glDisableVertexAttribArray(a->index);
+}
+
+void
+setupVertexInput(InstanceDataHeader *header)
+{
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(header->vao);
+#else
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+ glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+ setAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+}
+
+void
+teardownVertexInput(InstanceDataHeader *header)
+{
+#ifndef RW_GL_USE_VAOS
+ disableAttribPointers(header->attribDesc, header->numAttribs);
+#endif
+}
+
+int32
+lightingCB(Atomic *atomic)
+{
+ WorldLights lightData;
+ Light *directionals[8];
+ Light *locals[8];
+ lightData.directionals = directionals;
+ lightData.numDirectionals = 8;
+ lightData.locals = locals;
+ lightData.numLocals = 8;
+
+ if(atomic->geometry->flags & rw::Geometry::LIGHT)
+ ((World*)engine->currentWorld)->enumerateLights(atomic, &lightData);
+ else
+ memset(&lightData, 0, sizeof(lightData));
+ return setLights(&lightData);
+}
+
+int32
+lightingCB(void)
+{
+ WorldLights lightData;
+ Light *directionals[8];
+ Light *locals[8];
+ lightData.directionals = directionals;
+ lightData.numDirectionals = 8;
+ lightData.locals = locals;
+ lightData.numLocals = 8;
+
+ ((World*)engine->currentWorld)->enumerateLights(&lightData);
+ return setLights(&lightData);
+}
+
+
+void
+defaultRenderCB(Atomic *atomic, InstanceDataHeader *header)
+{
+ Material *m;
+
+ uint32 flags = atomic->geometry->flags;
+ setWorldMatrix(atomic->getFrame()->getLTM());
+ int32 vsBits = lightingCB(atomic);
+
+ setupVertexInput(header);
+
+ InstanceData *inst = header->inst;
+ int32 n = header->numMeshes;
+
+ while(n--){
+ m = inst->material;
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+ if((vsBits & VSLIGHT_MASK) == 0){
+ if(getAlphaTest())
+ defaultShader->use();
+ else
+ defaultShader_noAT->use();
+ }else{
+ if(getAlphaTest())
+ defaultShader_fullLight->use();
+ else
+ defaultShader_fullLight_noAT->use();
+ }
+
+ drawInst(header, inst);
+ inst++;
+ }
+ teardownVertexInput(header);
+}
+
+
+}
+}
+
+#endif
+
diff --git a/src/gl/gl3shader.cpp b/src/gl/gl3shader.cpp
new file mode 100644
index 0000000..fc57ae0
--- /dev/null
+++ b/src/gl/gl3shader.cpp
@@ -0,0 +1,363 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#ifdef RW_OPENGL
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+
+namespace rw {
+namespace gl3 {
+
+#include "shaders/header_vs.inc"
+#include "shaders/header_fs.inc"
+
+UniformRegistry uniformRegistry;
+static char nameBuffer[(MAX_UNIFORMS + MAX_BLOCKS)*32]; // static because memory system isn't up yet when we register
+static uint32 nameBufPtr;
+static float uniformData[512*4]; // seems enough
+static uint32 dataPtr;
+
+static int uniformTypesize[] = {
+ 0, 4, 4, 16
+};
+
+static char*
+shader_strdup(const char *name)
+{
+ size_t len = strlen(name)+1;
+ char *s = &nameBuffer[nameBufPtr];
+ nameBufPtr += len;
+ assert(nameBufPtr <= nelem(nameBuffer));
+ memcpy(s, name, len);
+ return s;
+}
+
+int32
+registerUniform(const char *name, UniformType type, int32 num)
+{
+ int i;
+ i = findUniform(name);
+ if(type == UNIFORM_NA)
+ num = 0;
+ if(i >= 0){
+ Uniform *u = &uniformRegistry.uniforms[i];
+ assert(u->type == type);
+ assert(u->num == num);
+ return i;
+ }
+ // TODO: print error
+ if(uniformRegistry.numUniforms+1 >= MAX_UNIFORMS){
+ assert(0 && "no space for uniform");
+ return -1;
+ }
+ Uniform *u = &uniformRegistry.uniforms[uniformRegistry.numUniforms];
+ u->name = shader_strdup(name);
+ u->type = type;
+ u->serialNum = 0;
+ if(type == UNIFORM_NA){
+ u->num = 0;
+ u->data = nil;
+ }else{
+ u->num = num;
+ u->data = &uniformData[dataPtr];
+ dataPtr += uniformTypesize[type]*num;
+ assert(dataPtr <= nelem(uniformData));
+ }
+
+ return uniformRegistry.numUniforms++;
+}
+
+int32
+findUniform(const char *name)
+{
+ int i;
+ for(i = 0; i < uniformRegistry.numUniforms; i++)
+ if(strcmp(name, uniformRegistry.uniforms[i].name) == 0)
+ return i;
+ return -1;
+}
+
+int32
+registerBlock(const char *name)
+{
+ int i;
+ i = findBlock(name);
+ if(i >= 0) return i;
+ // TODO: print error
+ if(uniformRegistry.numBlocks+1 >= MAX_BLOCKS)
+ return -1;
+ uniformRegistry.blockNames[uniformRegistry.numBlocks] = shader_strdup(name);
+ return uniformRegistry.numBlocks++;
+}
+
+int32
+findBlock(const char *name)
+{
+ int i;
+ for(i = 0; i < uniformRegistry.numBlocks; i++)
+ if(strcmp(name, uniformRegistry.blockNames[i]) == 0)
+ return i;
+ return -1;
+}
+
+void
+setUniform(int32 id, void *data)
+{
+ Uniform *u = &uniformRegistry.uniforms[id];
+ assert(u->type != UNIFORM_NA);
+ if(memcmp(u->data, data, uniformTypesize[u->type]*u->num * sizeof(float)) != 0){
+ memcpy(u->data, data, uniformTypesize[u->type]*u->num * sizeof(float));
+ //u->dirty = true;
+ u->serialNum++;
+ }
+}
+
+void
+flushUniforms(void)
+{
+ for(int i = 0; i < uniformRegistry.numUniforms; i++){
+ // this is bad!
+ if(i >= currentShader->numUniforms){
+ printf("trying to set uniform %d %s that doesn't exist!\n", i, uniformRegistry.uniforms[i].name);
+ continue;
+ }
+
+ int32 loc = currentShader->uniformLocations[i];
+ if(loc == -1)
+ continue;
+
+ Uniform *u = &uniformRegistry.uniforms[i];
+ if(currentShader->serialNums[i] != u->serialNum)
+ switch(u->type){
+ case UNIFORM_NA:
+ break;
+ case UNIFORM_VEC4:
+ glUniform4fv(loc, u->num, (GLfloat*)u->data);
+ break;
+ case UNIFORM_IVEC4:
+ glUniform4iv(loc, u->num, (GLint*)u->data);
+ break;
+ case UNIFORM_MAT4:
+ glUniformMatrix4fv(loc, u->num, GL_FALSE, (GLfloat*)u->data);
+ break;
+ }
+ currentShader->serialNums[i] = u->serialNum;
+ }
+}
+
+Shader *currentShader;
+
+static void
+printShaderSource(const char **src)
+{
+ int f;
+ const char *file;
+ bool printline;
+ int line = 1;
+ for(f = 0; src[f]; f++){
+ int fileline = 1;
+ char c;
+ file = src[f];
+ printline = true;
+ while(c = *file++, c != '\0'){
+ if(printline)
+ printf("%.4d/%d:%.4d: ", line++, f, fileline++);
+ putchar(c);
+ printline = c == '\n';
+ }
+ putchar('\n');
+ }
+}
+
+static int
+compileshader(GLenum type, const char **src, GLuint *shader)
+{
+ GLint n;
+ GLint shdr, success;
+ GLint len;
+ char *log;
+
+ for(n = 0; src[n]; n++);
+
+ shdr = glCreateShader(type);
+ glShaderSource(shdr, n, src, nil);
+ glCompileShader(shdr);
+ glGetShaderiv(shdr, GL_COMPILE_STATUS, &success);
+ if(!success){
+ printShaderSource(src);
+ fprintf(stderr, "Error in %s shader\n",
+ type == GL_VERTEX_SHADER ? "vertex" : "fragment");
+ glGetShaderiv(shdr, GL_INFO_LOG_LENGTH, &len);
+ log = (char*)rwMalloc(len, MEMDUR_FUNCTION);
+ glGetShaderInfoLog(shdr, len, nil, log);
+ fprintf(stderr, "%s\n", log);
+ rwFree(log);
+ return 1;
+ }
+ *shader = shdr;
+ return 0;
+}
+
+static int
+linkprogram(GLint vs, GLint fs, GLuint *program)
+{
+ GLint prog, success;
+ GLint len;
+ char *log;
+
+ prog = glCreateProgram();
+
+ if(gl3Caps.glversion < 30){
+ // TODO: perhaps just do this always and get rid of the layout stuff?
+ glBindAttribLocation(prog, ATTRIB_POS, "in_pos");
+ glBindAttribLocation(prog, ATTRIB_NORMAL, "in_normal");
+ glBindAttribLocation(prog, ATTRIB_COLOR, "in_color");
+ glBindAttribLocation(prog, ATTRIB_WEIGHTS, "in_weights");
+ glBindAttribLocation(prog, ATTRIB_INDICES, "in_indices");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS0, "in_tex0");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS1, "in_tex1");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS2, "in_tex2");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS3, "in_tex3");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS4, "in_tex4");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS5, "in_tex5");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS6, "in_tex6");
+ glBindAttribLocation(prog, ATTRIB_TEXCOORDS7, "in_tex7");
+ }
+
+ glAttachShader(prog, vs);
+ glAttachShader(prog, fs);
+ glLinkProgram(prog);
+ glGetProgramiv(prog, GL_LINK_STATUS, &success);
+ if(!success){
+ fprintf(stderr, "Error in program\n");
+ glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
+ log = (char*)rwMalloc(len, MEMDUR_FUNCTION);
+ glGetProgramInfoLog(prog, len, nil, log);
+ fprintf(stderr, "%s\n", log);
+ rwFree(log);
+ return 1;
+ }
+ *program = prog;
+ return 0;
+}
+
+Shader*
+Shader::create(const char **vsrc, const char **fsrc)
+{
+ GLuint vs, fs, program;
+ int i;
+ int fail;
+
+ fail = compileshader(GL_VERTEX_SHADER, vsrc, &vs);
+ if(fail)
+ return nil;
+
+ fail = compileshader(GL_FRAGMENT_SHADER, fsrc, &fs);
+ if(fail){
+ glDeleteShader(vs);
+ return nil;
+ }
+
+ fail = linkprogram(vs, fs, &program);
+
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+ if(fail){
+ return nil;
+ }
+
+ Shader *sh = rwNewT(Shader, 1, MEMDUR_EVENT | ID_DRIVER); // or global?
+
+#ifdef xxxRW_GLES2
+ int numUniforms;
+ glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numUniforms);
+ for(i = 0; i < numUniforms; i++){
+ GLint size;
+ GLenum type;
+ char name[100];
+ glGetActiveUniform(program, i, 100, nil, &size, &type, name);
+ printf("%d %d %s\n", size, type, name);
+ }
+ printf("\n");
+#endif
+
+#ifdef xxxRW_GLES2
+ int numAttribs;
+ glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &numAttribs);
+ for(i = 0; i < numAttribs; i++){
+ GLint size;
+ GLenum type;
+ char name[100];
+ glGetActiveAttrib(program, i, 100, nil, &size, &type, name);
+ GLint bind = glGetAttribLocation(program, name);
+ printf("%d %d %s. %d\n", size, type, name, bind);
+ }
+ printf("\n");
+#endif
+
+ // set uniform block binding
+ for(i = 0; i < uniformRegistry.numBlocks; i++){
+ int idx = glGetUniformBlockIndex(program,
+ uniformRegistry.blockNames[i]);
+ if(idx >= 0)
+ glUniformBlockBinding(program, idx, i);
+ }
+
+ // query uniform locations
+ sh->program = program;
+ sh->numUniforms = uniformRegistry.numUniforms;
+ sh->uniformLocations = rwNewT(GLint, uniformRegistry.numUniforms, MEMDUR_EVENT | ID_DRIVER);
+ sh->serialNums = rwNewT(uint32, uniformRegistry.numUniforms, MEMDUR_EVENT | ID_DRIVER);
+ for(i = 0; i < uniformRegistry.numUniforms; i++){
+ sh->uniformLocations[i] = glGetUniformLocation(program,
+ uniformRegistry.uniforms[i].name);
+ sh->serialNums[i] = ~0; // let's hope this means dirty
+ }
+
+ // set samplers
+ glUseProgram(program);
+ char name[64];
+ GLint loc;
+ for(i = 0; i < 4; i++){
+ sprintf(name, "tex%d", i);
+ loc = glGetUniformLocation(program, name);
+ glUniform1i(loc, i);
+ }
+
+ // reset program
+ if(currentShader)
+ glUseProgram(currentShader->program);
+
+ return sh;
+}
+
+void
+Shader::use(void)
+{
+ if(currentShader != this){
+ glUseProgram(this->program);
+ currentShader = this;
+ }
+}
+
+void
+Shader::destroy(void)
+{
+ glDeleteProgram(this->program);
+ rwFree(this->uniformLocations);
+ rwFree(this->serialNums);
+ rwFree(this);
+}
+
+}
+}
+
+#endif
diff --git a/src/gl/gl3skin.cpp b/src/gl/gl3skin.cpp
new file mode 100644
index 0000000..6b69bbd
--- /dev/null
+++ b/src/gl/gl3skin.cpp
@@ -0,0 +1,366 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwrender.h"
+#include "../rwengine.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+
+#include "rwgl3.h"
+#include "rwgl3shader.h"
+#include "rwgl3plg.h"
+
+#include "rwgl3impl.h"
+
+namespace rw {
+namespace gl3 {
+
+#ifdef RW_OPENGL
+
+static Shader *skinShader, *skinShader_noAT;
+static Shader *skinShader_fullLight, *skinShader_fullLight_noAT;
+static int32 u_boneMatrices;
+
+void
+skinInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance)
+{
+ AttribDesc *attribs, *a;
+
+ bool isPrelit = !!(geo->flags & Geometry::PRELIT);
+ bool hasNormals = !!(geo->flags & Geometry::NORMALS);
+
+ if(!reinstance){
+ AttribDesc tmpAttribs[14];
+ uint32 stride;
+
+ //
+ // Create attribute descriptions
+ //
+ a = tmpAttribs;
+ stride = 0;
+
+ // Positions
+ a->index = ATTRIB_POS;
+ a->size = 3;
+ a->type = GL_FLOAT;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 12;
+ a++;
+
+ // Normals
+ // TODO: compress
+ if(hasNormals){
+ a->index = ATTRIB_NORMAL;
+ a->size = 3;
+ a->type = GL_FLOAT;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 12;
+ a++;
+ }
+
+ // Prelighting
+ if(isPrelit){
+ a->index = ATTRIB_COLOR;
+ a->size = 4;
+ a->type = GL_UNSIGNED_BYTE;
+ a->normalized = GL_TRUE;
+ a->offset = stride;
+ stride += 4;
+ a++;
+ }
+
+ // Texture coordinates
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ a->index = ATTRIB_TEXCOORDS0+n;
+ a->size = 2;
+ a->type = GL_FLOAT;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 8;
+ a++;
+ }
+
+ // Weights
+ a->index = ATTRIB_WEIGHTS;
+ a->size = 4;
+ a->type = GL_FLOAT;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 16;
+ a++;
+
+ // Indices
+ a->index = ATTRIB_INDICES;
+ a->size = 4;
+ a->type = GL_UNSIGNED_BYTE;
+ a->normalized = GL_FALSE;
+ a->offset = stride;
+ stride += 4;
+ a++;
+
+ header->numAttribs = a - tmpAttribs;
+ for(a = tmpAttribs; a != &tmpAttribs[header->numAttribs]; a++)
+ a->stride = stride;
+ header->attribDesc = rwNewT(AttribDesc, header->numAttribs, MEMDUR_EVENT | ID_GEOMETRY);
+ memcpy(header->attribDesc, tmpAttribs,
+ header->numAttribs*sizeof(AttribDesc));
+
+ //
+ // Allocate vertex buffer
+ //
+ header->vertexBuffer = rwNewT(uint8, header->totalNumVertex*stride, MEMDUR_EVENT | ID_GEOMETRY);
+ assert(header->vbo == 0);
+ glGenBuffers(1, &header->vbo);
+ }
+
+ Skin *skin = Skin::get(geo);
+ attribs = header->attribDesc;
+
+ //
+ // Fill vertex buffer
+ //
+
+ uint8 *verts = header->vertexBuffer;
+
+ // Positions
+ if(!reinstance || geo->lockedSinceInst&Geometry::LOCKVERTICES){
+ for(a = attribs; a->index != ATTRIB_POS; a++)
+ ;
+ instV3d(VERT_FLOAT3, verts + a->offset,
+ geo->morphTargets[0].vertices,
+ header->totalNumVertex, a->stride);
+ }
+
+ // Normals
+ if(hasNormals && (!reinstance || geo->lockedSinceInst&Geometry::LOCKNORMALS)){
+ for(a = attribs; a->index != ATTRIB_NORMAL; a++)
+ ;
+ instV3d(VERT_FLOAT3, verts + a->offset,
+ geo->morphTargets[0].normals,
+ header->totalNumVertex, a->stride);
+ }
+
+ // Prelighting
+ if(isPrelit && (!reinstance || geo->lockedSinceInst&Geometry::LOCKPRELIGHT)){
+ for(a = attribs; a->index != ATTRIB_COLOR; a++)
+ ;
+ instColor(VERT_RGBA, verts + a->offset,
+ geo->colors,
+ header->totalNumVertex, a->stride);
+ }
+
+ // Texture coordinates
+ for(int32 n = 0; n < geo->numTexCoordSets; n++){
+ if(!reinstance || geo->lockedSinceInst&(Geometry::LOCKTEXCOORDS<<n)){
+ for(a = attribs; a->index != ATTRIB_TEXCOORDS0+n; a++)
+ ;
+ instTexCoords(VERT_FLOAT2, verts + a->offset,
+ geo->texCoords[n],
+ header->totalNumVertex, a->stride);
+ }
+ }
+
+ // Weights
+ if(!reinstance){
+ for(a = attribs; a->index != ATTRIB_WEIGHTS; a++)
+ ;
+ float *w = skin->weights;
+ instV4d(VERT_FLOAT4, verts + a->offset,
+ (V4d*)w,
+ header->totalNumVertex, a->stride);
+ }
+
+ // Indices
+ if(!reinstance){
+ for(a = attribs; a->index != ATTRIB_INDICES; a++)
+ ;
+ // not really colors of course but what the heck
+ instColor(VERT_RGBA, verts + a->offset,
+ (RGBA*)skin->indices,
+ header->totalNumVertex, a->stride);
+ }
+
+#ifdef RW_GL_USE_VAOS
+ glBindVertexArray(header->vao);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, header->ibo);
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, header->vbo);
+ glBufferData(GL_ARRAY_BUFFER, header->totalNumVertex*attribs[0].stride,
+ header->vertexBuffer, GL_STATIC_DRAW);
+#ifdef RW_GL_USE_VAOS
+ setAttribPointers(header->attribDesc, header->numAttribs);
+ glBindVertexArray(0);
+#endif
+}
+
+void
+skinUninstanceCB(Geometry *geo, InstanceDataHeader *header)
+{
+ assert(0 && "can't uninstance");
+}
+
+static float skinMatrices[64*16];
+
+void
+uploadSkinMatrices(Atomic *a)
+{
+ int i;
+ Skin *skin = Skin::get(a->geometry);
+ Matrix *m = (Matrix*)skinMatrices;
+ HAnimHierarchy *hier = Skin::getHierarchy(a);
+
+ if(hier){
+ Matrix *invMats = (Matrix*)skin->inverseMatrices;
+ Matrix tmp;
+
+ assert(skin->numBones == hier->numNodes);
+ if(hier->flags & HAnimHierarchy::LOCALSPACEMATRICES){
+ for(i = 0; i < hier->numNodes; i++){
+ invMats[i].flags = 0;
+ Matrix::mult(m, &invMats[i], &hier->matrices[i]);
+ m++;
+ }
+ }else{
+ Matrix invAtmMat;
+ Matrix::invert(&invAtmMat, a->getFrame()->getLTM());
+ for(i = 0; i < hier->numNodes; i++){
+ invMats[i].flags = 0;
+ Matrix::mult(&tmp, &hier->matrices[i], &invAtmMat);
+ Matrix::mult(m, &invMats[i], &tmp);
+ m++;
+ }
+ }
+ }else{
+ for(i = 0; i < skin->numBones; i++){
+ m->setIdentity();
+ m++;
+ }
+ }
+ setUniform(u_boneMatrices, skinMatrices);
+}
+
+void
+skinRenderCB(Atomic *atomic, InstanceDataHeader *header)
+{
+ Material *m;
+
+ uint32 flags = atomic->geometry->flags;
+ setWorldMatrix(atomic->getFrame()->getLTM());
+ int32 vsBits = lightingCB(atomic);
+
+ setupVertexInput(header);
+
+ InstanceData *inst = header->inst;
+ int32 n = header->numMeshes;
+
+ uploadSkinMatrices(atomic);
+
+ while(n--){
+ m = inst->material;
+
+ setMaterial(flags, m->color, m->surfaceProps);
+
+ setTexture(0, m->texture);
+
+ rw::SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 0xFF);
+
+ if((vsBits & VSLIGHT_MASK) == 0){
+ if(getAlphaTest())
+ skinShader->use();
+ else
+ skinShader_noAT->use();
+ }else{
+ if(getAlphaTest())
+ skinShader_fullLight->use();
+ else
+ skinShader_fullLight_noAT->use();
+ }
+
+ drawInst(header, inst);
+ inst++;
+ }
+ teardownVertexInput(header);
+}
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+ skinGlobals.pipelines[PLATFORM_GL3] = makeSkinPipeline();
+
+#include "shaders/simple_fs_gl.inc"
+#include "shaders/skin_gl.inc"
+ const char *vs[] = { shaderDecl, header_vert_src, skin_vert_src, nil };
+ const char *vs_fullLight[] = { shaderDecl, "#define DIRECTIONALS\n#define POINTLIGHTS\n#define SPOTLIGHTS\n", header_vert_src, skin_vert_src, nil };
+ const char *fs[] = { shaderDecl, header_frag_src, simple_frag_src, nil };
+ const char *fs_noAT[] = { shaderDecl, "#define NO_ALPHATEST\n", header_frag_src, simple_frag_src, nil };
+
+ skinShader = Shader::create(vs, fs);
+ assert(skinShader);
+ skinShader_noAT = Shader::create(vs, fs_noAT);
+ assert(skinShader_noAT);
+
+ skinShader_fullLight = Shader::create(vs_fullLight, fs);
+ assert(skinShader_fullLight);
+ skinShader_fullLight_noAT = Shader::create(vs_fullLight, fs_noAT);
+ assert(skinShader_fullLight_noAT);
+
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_GL3])->destroy();
+ skinGlobals.pipelines[PLATFORM_GL3] = nil;
+
+ skinShader->destroy();
+ skinShader = nil;
+ skinShader_noAT->destroy();
+ skinShader_noAT = nil;
+ skinShader_fullLight->destroy();
+ skinShader_fullLight = nil;
+ skinShader_fullLight_noAT->destroy();
+ skinShader_fullLight_noAT = nil;
+
+ return o;
+}
+
+void
+initSkin(void)
+{
+ u_boneMatrices = registerUniform("u_boneMatrices", UNIFORM_MAT4, 64);
+
+ Driver::registerPlugin(PLATFORM_GL3, 0, ID_SKIN,
+ skinOpen, skinClose);
+}
+
+ObjPipeline*
+makeSkinPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = skinUninstanceCB;
+ pipe->renderCB = skinRenderCB;
+ pipe->pluginID = ID_SKIN;
+ pipe->pluginData = 1;
+ return pipe;
+}
+
+#else
+
+void initSkin(void) { }
+
+#endif
+
+}
+}
+
diff --git a/src/gl/glad/glad.c b/src/gl/glad/glad.c
new file mode 100644
index 0000000..05161d1
--- /dev/null
+++ b/src/gl/glad/glad.c
@@ -0,0 +1,1590 @@
+/*
+
+ OpenGL, OpenGL ES loader generated by glad 0.1.34 on Wed Feb 17 15:35:25 2021.
+
+ Language/Generator: C/C++
+ Specification: gl
+ APIs: gl=3.3, gles2=3.1
+ Profile: core
+ Extensions:
+ GL_EXT_framebuffer_object,
+ GL_EXT_texture_compression_s3tc,
+ GL_EXT_texture_filter_anisotropic,
+ GL_KHR_debug,
+ GL_KHR_texture_compression_astc_ldr
+ Loader: False
+ Local files: False
+ Omit khrplatform: False
+ Reproducible: False
+
+ Commandline:
+ --profile="core" --api="gl=3.3,gles2=3.1" --generator="c" --spec="gl" --no-loader --extensions="GL_EXT_framebuffer_object,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_KHR_debug,GL_KHR_texture_compression_astc_ldr"
+ Online:
+ https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D3.3&api=gles2%3D3.1&extensions=GL_EXT_framebuffer_object&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug&extensions=GL_KHR_texture_compression_astc_ldr
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "glad.h"
+
+struct gladGLversionStruct GLVersion = { 0, 0 };
+
+static int requested_major_version;
+static int requested_minor_version;
+
+#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
+#define _GLAD_IS_SOME_NEW_VERSION 1
+#endif
+
+static int max_loaded_major;
+static int max_loaded_minor;
+
+static const char *exts = NULL;
+static int num_exts_i = 0;
+static char **exts_i = NULL;
+
+static int get_exts(void) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ if(max_loaded_major < 3) {
+#endif
+ exts = (const char *)glGetString(GL_EXTENSIONS);
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ } else {
+ unsigned int index;
+
+ num_exts_i = 0;
+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i);
+ if (num_exts_i > 0) {
+ exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i));
+ }
+
+ if (exts_i == NULL) {
+ return 0;
+ }
+
+ for(index = 0; index < (unsigned)num_exts_i; index++) {
+ const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index);
+ size_t len = strlen(gl_str_tmp);
+
+ char *local_str = (char*)malloc((len+1) * sizeof(char));
+ if(local_str != NULL) {
+ memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char));
+ }
+ exts_i[index] = local_str;
+ }
+ }
+#endif
+ return 1;
+}
+
+static void free_exts(void) {
+ if (exts_i != NULL) {
+ int index;
+ for(index = 0; index < num_exts_i; index++) {
+ free((char *)exts_i[index]);
+ }
+ free((void *)exts_i);
+ exts_i = NULL;
+ }
+}
+
+static int has_ext(const char *ext) {
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ if(max_loaded_major < 3) {
+#endif
+ const char *extensions;
+ const char *loc;
+ const char *terminator;
+ extensions = exts;
+ if(extensions == NULL || ext == NULL) {
+ return 0;
+ }
+
+ while(1) {
+ loc = strstr(extensions, ext);
+ if(loc == NULL) {
+ return 0;
+ }
+
+ terminator = loc + strlen(ext);
+ if((loc == extensions || *(loc - 1) == ' ') &&
+ (*terminator == ' ' || *terminator == '\0')) {
+ return 1;
+ }
+ extensions = terminator;
+ }
+#ifdef _GLAD_IS_SOME_NEW_VERSION
+ } else {
+ int index;
+ if(exts_i == NULL) return 0;
+ for(index = 0; index < num_exts_i; index++) {
+ const char *e = exts_i[index];
+
+ if(exts_i[index] != NULL && strcmp(e, ext) == 0) {
+ return 1;
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+int GLAD_GL_VERSION_1_0 = 0;
+int GLAD_GL_VERSION_1_1 = 0;
+int GLAD_GL_VERSION_1_2 = 0;
+int GLAD_GL_VERSION_1_3 = 0;
+int GLAD_GL_VERSION_1_4 = 0;
+int GLAD_GL_VERSION_1_5 = 0;
+int GLAD_GL_VERSION_2_0 = 0;
+int GLAD_GL_VERSION_2_1 = 0;
+int GLAD_GL_VERSION_3_0 = 0;
+int GLAD_GL_VERSION_3_1 = 0;
+int GLAD_GL_VERSION_3_2 = 0;
+int GLAD_GL_VERSION_3_3 = 0;
+int GLAD_GL_ES_VERSION_2_0 = 0;
+int GLAD_GL_ES_VERSION_3_0 = 0;
+int GLAD_GL_ES_VERSION_3_1 = 0;
+PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram = NULL;
+PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL;
+PFNGLATTACHSHADERPROC glad_glAttachShader = NULL;
+PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL;
+PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL;
+PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL;
+PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL;
+PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL;
+PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL;
+PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL;
+PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL;
+PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL;
+PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL;
+PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture = NULL;
+PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline = NULL;
+PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;
+PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL;
+PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;
+PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL;
+PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;
+PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer = NULL;
+PFNGLBLENDCOLORPROC glad_glBlendColor = NULL;
+PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL;
+PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL;
+PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL;
+PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;
+PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;
+PFNGLBUFFERDATAPROC glad_glBufferData = NULL;
+PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL;
+PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL;
+PFNGLCLAMPCOLORPROC glad_glClampColor = NULL;
+PFNGLCLEARPROC glad_glClear = NULL;
+PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL;
+PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL;
+PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL;
+PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL;
+PFNGLCLEARCOLORPROC glad_glClearColor = NULL;
+PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL;
+PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL;
+PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;
+PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL;
+PFNGLCOLORMASKPROC glad_glColorMask = NULL;
+PFNGLCOLORMASKIPROC glad_glColorMaski = NULL;
+PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL;
+PFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL;
+PFNGLCOLORP4UIPROC glad_glColorP4ui = NULL;
+PFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL;
+PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL;
+PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL;
+PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL;
+PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL;
+PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL;
+PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL;
+PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL;
+PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL;
+PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL;
+PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL;
+PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL;
+PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL;
+PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL;
+PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL;
+PFNGLCREATESHADERPROC glad_glCreateShader = NULL;
+PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv = NULL;
+PFNGLCULLFACEPROC glad_glCullFace = NULL;
+PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL;
+PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL;
+PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL;
+PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines = NULL;
+PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL;
+PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL;
+PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL;
+PFNGLDELETESHADERPROC glad_glDeleteShader = NULL;
+PFNGLDELETESYNCPROC glad_glDeleteSync = NULL;
+PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL;
+PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks = NULL;
+PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL;
+PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL;
+PFNGLDEPTHMASKPROC glad_glDepthMask = NULL;
+PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL;
+PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL;
+PFNGLDETACHSHADERPROC glad_glDetachShader = NULL;
+PFNGLDISABLEPROC glad_glDisable = NULL;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL;
+PFNGLDISABLEIPROC glad_glDisablei = NULL;
+PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute = NULL;
+PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect = NULL;
+PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL;
+PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect = NULL;
+PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL;
+PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL;
+PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL;
+PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL;
+PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL;
+PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect = NULL;
+PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL;
+PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL;
+PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL;
+PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL;
+PFNGLENABLEPROC glad_glEnable = NULL;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL;
+PFNGLENABLEIPROC glad_glEnablei = NULL;
+PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL;
+PFNGLENDQUERYPROC glad_glEndQuery = NULL;
+PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL;
+PFNGLFENCESYNCPROC glad_glFenceSync = NULL;
+PFNGLFINISHPROC glad_glFinish = NULL;
+PFNGLFLUSHPROC glad_glFlush = NULL;
+PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL;
+PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri = NULL;
+PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL;
+PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL;
+PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL;
+PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL;
+PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL;
+PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL;
+PFNGLFRONTFACEPROC glad_glFrontFace = NULL;
+PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL;
+PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL;
+PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines = NULL;
+PFNGLGENQUERIESPROC glad_glGenQueries = NULL;
+PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL;
+PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL;
+PFNGLGENTEXTURESPROC glad_glGenTextures = NULL;
+PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL;
+PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;
+PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;
+PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL;
+PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL;
+PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL;
+PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL;
+PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL;
+PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL;
+PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL;
+PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL;
+PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL;
+PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL;
+PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL;
+PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL;
+PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL;
+PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL;
+PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;
+PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;
+PFNGLGETERRORPROC glad_glGetError = NULL;
+PFNGLGETFLOATVPROC glad_glGetFloatv = NULL;
+PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL;
+PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL;
+PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL;
+PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv = NULL;
+PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL;
+PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL;
+PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL;
+PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;
+PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL;
+PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL;
+PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary = NULL;
+PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL;
+PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv = NULL;
+PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog = NULL;
+PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv = NULL;
+PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex = NULL;
+PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation = NULL;
+PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName = NULL;
+PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv = NULL;
+PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
+PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;
+PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;
+PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL;
+PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL;
+PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL;
+PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL;
+PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL;
+PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL;
+PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL;
+PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL;
+PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL;
+PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL;
+PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL;
+PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL;
+PFNGLGETSTRINGPROC glad_glGetString = NULL;
+PFNGLGETSTRINGIPROC glad_glGetStringi = NULL;
+PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL;
+PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL;
+PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL;
+PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL;
+PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL;
+PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL;
+PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL;
+PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL;
+PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL;
+PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL;
+PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL;
+PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL;
+PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL;
+PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL;
+PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL;
+PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL;
+PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL;
+PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL;
+PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL;
+PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL;
+PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL;
+PFNGLHINTPROC glad_glHint = NULL;
+PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL;
+PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL;
+PFNGLISBUFFERPROC glad_glIsBuffer = NULL;
+PFNGLISENABLEDPROC glad_glIsEnabled = NULL;
+PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL;
+PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL;
+PFNGLISPROGRAMPROC glad_glIsProgram = NULL;
+PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline = NULL;
+PFNGLISQUERYPROC glad_glIsQuery = NULL;
+PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL;
+PFNGLISSAMPLERPROC glad_glIsSampler = NULL;
+PFNGLISSHADERPROC glad_glIsShader = NULL;
+PFNGLISSYNCPROC glad_glIsSync = NULL;
+PFNGLISTEXTUREPROC glad_glIsTexture = NULL;
+PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback = NULL;
+PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL;
+PFNGLLINEWIDTHPROC glad_glLineWidth = NULL;
+PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL;
+PFNGLLOGICOPPROC glad_glLogicOp = NULL;
+PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL;
+PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;
+PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier = NULL;
+PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion = NULL;
+PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;
+PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;
+PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL;
+PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL;
+PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL;
+PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL;
+PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL;
+PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL;
+PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;
+PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;
+PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;
+PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL;
+PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL;
+PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback = NULL;
+PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL;
+PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL;
+PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL;
+PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL;
+PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL;
+PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL;
+PFNGLPOINTSIZEPROC glad_glPointSize = NULL;
+PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;
+PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;
+PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;
+PFNGLPROGRAMBINARYPROC glad_glProgramBinary = NULL;
+PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri = NULL;
+PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f = NULL;
+PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv = NULL;
+PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i = NULL;
+PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv = NULL;
+PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui = NULL;
+PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv = NULL;
+PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f = NULL;
+PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv = NULL;
+PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i = NULL;
+PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv = NULL;
+PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui = NULL;
+PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv = NULL;
+PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f = NULL;
+PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv = NULL;
+PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i = NULL;
+PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv = NULL;
+PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui = NULL;
+PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv = NULL;
+PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f = NULL;
+PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv = NULL;
+PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i = NULL;
+PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv = NULL;
+PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui = NULL;
+PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv = NULL;
+PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv = NULL;
+PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL;
+PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL;
+PFNGLREADBUFFERPROC glad_glReadBuffer = NULL;
+PFNGLREADPIXELSPROC glad_glReadPixels = NULL;
+PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL;
+PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL;
+PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL;
+PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback = NULL;
+PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL;
+PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL;
+PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL;
+PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL;
+PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL;
+PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL;
+PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL;
+PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL;
+PFNGLSCISSORPROC glad_glScissor = NULL;
+PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL;
+PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL;
+PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL;
+PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;
+PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;
+PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;
+PFNGLSTENCILMASKPROC glad_glStencilMask = NULL;
+PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL;
+PFNGLSTENCILOPPROC glad_glStencilOp = NULL;
+PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL;
+PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL;
+PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL;
+PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL;
+PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL;
+PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL;
+PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL;
+PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL;
+PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL;
+PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL;
+PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL;
+PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL;
+PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL;
+PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL;
+PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL;
+PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL;
+PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL;
+PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL;
+PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL;
+PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL;
+PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL;
+PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D = NULL;
+PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample = NULL;
+PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D = NULL;
+PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL;
+PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL;
+PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL;
+PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL;
+PFNGLUNIFORM1FPROC glad_glUniform1f = NULL;
+PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL;
+PFNGLUNIFORM1IPROC glad_glUniform1i = NULL;
+PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL;
+PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL;
+PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL;
+PFNGLUNIFORM2FPROC glad_glUniform2f = NULL;
+PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL;
+PFNGLUNIFORM2IPROC glad_glUniform2i = NULL;
+PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL;
+PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL;
+PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL;
+PFNGLUNIFORM3FPROC glad_glUniform3f = NULL;
+PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL;
+PFNGLUNIFORM3IPROC glad_glUniform3i = NULL;
+PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL;
+PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL;
+PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL;
+PFNGLUNIFORM4FPROC glad_glUniform4f = NULL;
+PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL;
+PFNGLUNIFORM4IPROC glad_glUniform4i = NULL;
+PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL;
+PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL;
+PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL;
+PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL;
+PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL;
+PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL;
+PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL;
+PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL;
+PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL;
+PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL;
+PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL;
+PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL;
+PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL;
+PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL;
+PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL;
+PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages = NULL;
+PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL;
+PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline = NULL;
+PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL;
+PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL;
+PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL;
+PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL;
+PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL;
+PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL;
+PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL;
+PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL;
+PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL;
+PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL;
+PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL;
+PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL;
+PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL;
+PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL;
+PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL;
+PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL;
+PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL;
+PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL;
+PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL;
+PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL;
+PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL;
+PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL;
+PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL;
+PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL;
+PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL;
+PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL;
+PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL;
+PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL;
+PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL;
+PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL;
+PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL;
+PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL;
+PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL;
+PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL;
+PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL;
+PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL;
+PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding = NULL;
+PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL;
+PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat = NULL;
+PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL;
+PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL;
+PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL;
+PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL;
+PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL;
+PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL;
+PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL;
+PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL;
+PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL;
+PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL;
+PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL;
+PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL;
+PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL;
+PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL;
+PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL;
+PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL;
+PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL;
+PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL;
+PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL;
+PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL;
+PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat = NULL;
+PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL;
+PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL;
+PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL;
+PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL;
+PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL;
+PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL;
+PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL;
+PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL;
+PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL;
+PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL;
+PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor = NULL;
+PFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL;
+PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL;
+PFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL;
+PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL;
+PFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL;
+PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL;
+PFNGLVIEWPORTPROC glad_glViewport = NULL;
+PFNGLWAITSYNCPROC glad_glWaitSync = NULL;
+int GLAD_GL_EXT_framebuffer_object = 0;
+int GLAD_GL_EXT_texture_compression_s3tc = 0;
+int GLAD_GL_EXT_texture_filter_anisotropic = 0;
+int GLAD_GL_KHR_debug = 0;
+int GLAD_GL_KHR_texture_compression_astc_ldr = 0;
+PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL;
+PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL;
+PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL;
+PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL;
+PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL;
+PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL;
+PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL;
+PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL;
+PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL;
+PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL;
+PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL;
+PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR = NULL;
+PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR = NULL;
+PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR = NULL;
+PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR = NULL;
+PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR = NULL;
+PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR = NULL;
+PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR = NULL;
+PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR = NULL;
+PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR = NULL;
+PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR = NULL;
+PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR = NULL;
+static void load_GL_VERSION_1_0(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_0) return;
+ glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
+ glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace");
+ glad_glHint = (PFNGLHINTPROC)load("glHint");
+ glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth");
+ glad_glPointSize = (PFNGLPOINTSIZEPROC)load("glPointSize");
+ glad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load("glPolygonMode");
+ glad_glScissor = (PFNGLSCISSORPROC)load("glScissor");
+ glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf");
+ glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv");
+ glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri");
+ glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv");
+ glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load("glTexImage1D");
+ glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D");
+ glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load("glDrawBuffer");
+ glad_glClear = (PFNGLCLEARPROC)load("glClear");
+ glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor");
+ glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil");
+ glad_glClearDepth = (PFNGLCLEARDEPTHPROC)load("glClearDepth");
+ glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask");
+ glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask");
+ glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask");
+ glad_glDisable = (PFNGLDISABLEPROC)load("glDisable");
+ glad_glEnable = (PFNGLENABLEPROC)load("glEnable");
+ glad_glFinish = (PFNGLFINISHPROC)load("glFinish");
+ glad_glFlush = (PFNGLFLUSHPROC)load("glFlush");
+ glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc");
+ glad_glLogicOp = (PFNGLLOGICOPPROC)load("glLogicOp");
+ glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc");
+ glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp");
+ glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc");
+ glad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load("glPixelStoref");
+ glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei");
+ glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer");
+ glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels");
+ glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv");
+ glad_glGetDoublev = (PFNGLGETDOUBLEVPROC)load("glGetDoublev");
+ glad_glGetError = (PFNGLGETERRORPROC)load("glGetError");
+ glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv");
+ glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv");
+ glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC)load("glGetTexImage");
+ glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv");
+ glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv");
+ glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load("glGetTexLevelParameterfv");
+ glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load("glGetTexLevelParameteriv");
+ glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled");
+ glad_glDepthRange = (PFNGLDEPTHRANGEPROC)load("glDepthRange");
+ glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport");
+}
+static void load_GL_VERSION_1_1(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_1) return;
+ glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays");
+ glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements");
+ glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset");
+ glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)load("glCopyTexImage1D");
+ glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D");
+ glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)load("glCopyTexSubImage1D");
+ glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D");
+ glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)load("glTexSubImage1D");
+ glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D");
+ glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture");
+ glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures");
+ glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures");
+ glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture");
+}
+static void load_GL_VERSION_1_2(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_2) return;
+ glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements");
+ glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D");
+ glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D");
+ glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D");
+}
+static void load_GL_VERSION_1_3(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_3) return;
+ glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture");
+ glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage");
+ glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D");
+ glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D");
+ glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)load("glCompressedTexImage1D");
+ glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D");
+ glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D");
+ glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)load("glCompressedTexSubImage1D");
+ glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)load("glGetCompressedTexImage");
+}
+static void load_GL_VERSION_1_4(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_4) return;
+ glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate");
+ glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)load("glMultiDrawArrays");
+ glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)load("glMultiDrawElements");
+ glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC)load("glPointParameterf");
+ glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)load("glPointParameterfv");
+ glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC)load("glPointParameteri");
+ glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)load("glPointParameteriv");
+ glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor");
+ glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation");
+}
+static void load_GL_VERSION_1_5(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_1_5) return;
+ glad_glGenQueries = (PFNGLGENQUERIESPROC)load("glGenQueries");
+ glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load("glDeleteQueries");
+ glad_glIsQuery = (PFNGLISQUERYPROC)load("glIsQuery");
+ glad_glBeginQuery = (PFNGLBEGINQUERYPROC)load("glBeginQuery");
+ glad_glEndQuery = (PFNGLENDQUERYPROC)load("glEndQuery");
+ glad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load("glGetQueryiv");
+ glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)load("glGetQueryObjectiv");
+ glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv");
+ glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer");
+ glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers");
+ glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers");
+ glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer");
+ glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData");
+ glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData");
+ glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)load("glGetBufferSubData");
+ glad_glMapBuffer = (PFNGLMAPBUFFERPROC)load("glMapBuffer");
+ glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer");
+ glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv");
+ glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv");
+}
+static void load_GL_VERSION_2_0(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_2_0) return;
+ glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate");
+ glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers");
+ glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate");
+ glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate");
+ glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate");
+ glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader");
+ glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation");
+ glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader");
+ glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram");
+ glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader");
+ glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram");
+ glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader");
+ glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader");
+ glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray");
+ glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray");
+ glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib");
+ glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform");
+ glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders");
+ glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation");
+ glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv");
+ glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog");
+ glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv");
+ glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog");
+ glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource");
+ glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation");
+ glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv");
+ glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv");
+ glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)load("glGetVertexAttribdv");
+ glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv");
+ glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv");
+ glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv");
+ glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram");
+ glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader");
+ glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram");
+ glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource");
+ glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram");
+ glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f");
+ glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f");
+ glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f");
+ glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f");
+ glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i");
+ glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i");
+ glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i");
+ glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i");
+ glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv");
+ glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv");
+ glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv");
+ glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv");
+ glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv");
+ glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv");
+ glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv");
+ glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv");
+ glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv");
+ glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv");
+ glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv");
+ glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram");
+ glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)load("glVertexAttrib1d");
+ glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)load("glVertexAttrib1dv");
+ glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f");
+ glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv");
+ glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)load("glVertexAttrib1s");
+ glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)load("glVertexAttrib1sv");
+ glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)load("glVertexAttrib2d");
+ glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)load("glVertexAttrib2dv");
+ glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f");
+ glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv");
+ glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)load("glVertexAttrib2s");
+ glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)load("glVertexAttrib2sv");
+ glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)load("glVertexAttrib3d");
+ glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)load("glVertexAttrib3dv");
+ glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f");
+ glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv");
+ glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)load("glVertexAttrib3s");
+ glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)load("glVertexAttrib3sv");
+ glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)load("glVertexAttrib4Nbv");
+ glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)load("glVertexAttrib4Niv");
+ glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)load("glVertexAttrib4Nsv");
+ glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)load("glVertexAttrib4Nub");
+ glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)load("glVertexAttrib4Nubv");
+ glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)load("glVertexAttrib4Nuiv");
+ glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)load("glVertexAttrib4Nusv");
+ glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)load("glVertexAttrib4bv");
+ glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)load("glVertexAttrib4d");
+ glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)load("glVertexAttrib4dv");
+ glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f");
+ glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv");
+ glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)load("glVertexAttrib4iv");
+ glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)load("glVertexAttrib4s");
+ glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)load("glVertexAttrib4sv");
+ glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)load("glVertexAttrib4ubv");
+ glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)load("glVertexAttrib4uiv");
+ glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)load("glVertexAttrib4usv");
+ glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer");
+}
+static void load_GL_VERSION_2_1(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_2_1) return;
+ glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load("glUniformMatrix2x3fv");
+ glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load("glUniformMatrix3x2fv");
+ glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load("glUniformMatrix2x4fv");
+ glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load("glUniformMatrix4x2fv");
+ glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load("glUniformMatrix3x4fv");
+ glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load("glUniformMatrix4x3fv");
+}
+static void load_GL_VERSION_3_0(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_0) return;
+ glad_glColorMaski = (PFNGLCOLORMASKIPROC)load("glColorMaski");
+ glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load("glGetBooleani_v");
+ glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v");
+ glad_glEnablei = (PFNGLENABLEIPROC)load("glEnablei");
+ glad_glDisablei = (PFNGLDISABLEIPROC)load("glDisablei");
+ glad_glIsEnabledi = (PFNGLISENABLEDIPROC)load("glIsEnabledi");
+ glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load("glBeginTransformFeedback");
+ glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load("glEndTransformFeedback");
+ glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange");
+ glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase");
+ glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load("glTransformFeedbackVaryings");
+ glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load("glGetTransformFeedbackVarying");
+ glad_glClampColor = (PFNGLCLAMPCOLORPROC)load("glClampColor");
+ glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)load("glBeginConditionalRender");
+ glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)load("glEndConditionalRender");
+ glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer");
+ glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv");
+ glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv");
+ glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)load("glVertexAttribI1i");
+ glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)load("glVertexAttribI2i");
+ glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)load("glVertexAttribI3i");
+ glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i");
+ glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)load("glVertexAttribI1ui");
+ glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)load("glVertexAttribI2ui");
+ glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)load("glVertexAttribI3ui");
+ glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui");
+ glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)load("glVertexAttribI1iv");
+ glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)load("glVertexAttribI2iv");
+ glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)load("glVertexAttribI3iv");
+ glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv");
+ glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)load("glVertexAttribI1uiv");
+ glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)load("glVertexAttribI2uiv");
+ glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)load("glVertexAttribI3uiv");
+ glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv");
+ glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)load("glVertexAttribI4bv");
+ glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)load("glVertexAttribI4sv");
+ glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)load("glVertexAttribI4ubv");
+ glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)load("glVertexAttribI4usv");
+ glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv");
+ glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)load("glBindFragDataLocation");
+ glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load("glGetFragDataLocation");
+ glad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load("glUniform1ui");
+ glad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load("glUniform2ui");
+ glad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load("glUniform3ui");
+ glad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load("glUniform4ui");
+ glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load("glUniform1uiv");
+ glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load("glUniform2uiv");
+ glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load("glUniform3uiv");
+ glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load("glUniform4uiv");
+ glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)load("glTexParameterIiv");
+ glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)load("glTexParameterIuiv");
+ glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)load("glGetTexParameterIiv");
+ glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)load("glGetTexParameterIuiv");
+ glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load("glClearBufferiv");
+ glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load("glClearBufferuiv");
+ glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load("glClearBufferfv");
+ glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load("glClearBufferfi");
+ glad_glGetStringi = (PFNGLGETSTRINGIPROC)load("glGetStringi");
+ glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer");
+ glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer");
+ glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers");
+ glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers");
+ glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage");
+ glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv");
+ glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer");
+ glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer");
+ glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers");
+ glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers");
+ glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus");
+ glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load("glFramebufferTexture1D");
+ glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D");
+ glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load("glFramebufferTexture3D");
+ glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer");
+ glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv");
+ glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap");
+ glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load("glBlitFramebuffer");
+ glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample");
+ glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer");
+ glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load("glMapBufferRange");
+ glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load("glFlushMappedBufferRange");
+ glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load("glBindVertexArray");
+ glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load("glDeleteVertexArrays");
+ glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load("glGenVertexArrays");
+ glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load("glIsVertexArray");
+}
+static void load_GL_VERSION_3_1(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_1) return;
+ glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced");
+ glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced");
+ glad_glTexBuffer = (PFNGLTEXBUFFERPROC)load("glTexBuffer");
+ glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)load("glPrimitiveRestartIndex");
+ glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData");
+ glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices");
+ glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv");
+ glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load("glGetActiveUniformName");
+ glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex");
+ glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv");
+ glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName");
+ glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding");
+ glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange");
+ glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase");
+ glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v");
+}
+static void load_GL_VERSION_3_2(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_2) return;
+ glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load("glDrawElementsBaseVertex");
+ glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)load("glDrawRangeElementsBaseVertex");
+ glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)load("glDrawElementsInstancedBaseVertex");
+ glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)load("glMultiDrawElementsBaseVertex");
+ glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)load("glProvokingVertex");
+ glad_glFenceSync = (PFNGLFENCESYNCPROC)load("glFenceSync");
+ glad_glIsSync = (PFNGLISSYNCPROC)load("glIsSync");
+ glad_glDeleteSync = (PFNGLDELETESYNCPROC)load("glDeleteSync");
+ glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load("glClientWaitSync");
+ glad_glWaitSync = (PFNGLWAITSYNCPROC)load("glWaitSync");
+ glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load("glGetInteger64v");
+ glad_glGetSynciv = (PFNGLGETSYNCIVPROC)load("glGetSynciv");
+ glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load("glGetInteger64i_v");
+ glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load("glGetBufferParameteri64v");
+ glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)load("glFramebufferTexture");
+ glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)load("glTexImage2DMultisample");
+ glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)load("glTexImage3DMultisample");
+ glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load("glGetMultisamplefv");
+ glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load("glSampleMaski");
+}
+static void load_GL_VERSION_3_3(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_3) return;
+ glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)load("glBindFragDataLocationIndexed");
+ glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)load("glGetFragDataIndex");
+ glad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load("glGenSamplers");
+ glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load("glDeleteSamplers");
+ glad_glIsSampler = (PFNGLISSAMPLERPROC)load("glIsSampler");
+ glad_glBindSampler = (PFNGLBINDSAMPLERPROC)load("glBindSampler");
+ glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load("glSamplerParameteri");
+ glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load("glSamplerParameteriv");
+ glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load("glSamplerParameterf");
+ glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load("glSamplerParameterfv");
+ glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)load("glSamplerParameterIiv");
+ glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)load("glSamplerParameterIuiv");
+ glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load("glGetSamplerParameteriv");
+ glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)load("glGetSamplerParameterIiv");
+ glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load("glGetSamplerParameterfv");
+ glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)load("glGetSamplerParameterIuiv");
+ glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC)load("glQueryCounter");
+ glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)load("glGetQueryObjecti64v");
+ glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)load("glGetQueryObjectui64v");
+ glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load("glVertexAttribDivisor");
+ glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)load("glVertexAttribP1ui");
+ glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)load("glVertexAttribP1uiv");
+ glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)load("glVertexAttribP2ui");
+ glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)load("glVertexAttribP2uiv");
+ glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)load("glVertexAttribP3ui");
+ glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)load("glVertexAttribP3uiv");
+ glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)load("glVertexAttribP4ui");
+ glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)load("glVertexAttribP4uiv");
+ glad_glVertexP2ui = (PFNGLVERTEXP2UIPROC)load("glVertexP2ui");
+ glad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)load("glVertexP2uiv");
+ glad_glVertexP3ui = (PFNGLVERTEXP3UIPROC)load("glVertexP3ui");
+ glad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)load("glVertexP3uiv");
+ glad_glVertexP4ui = (PFNGLVERTEXP4UIPROC)load("glVertexP4ui");
+ glad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)load("glVertexP4uiv");
+ glad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)load("glTexCoordP1ui");
+ glad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)load("glTexCoordP1uiv");
+ glad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)load("glTexCoordP2ui");
+ glad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)load("glTexCoordP2uiv");
+ glad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)load("glTexCoordP3ui");
+ glad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)load("glTexCoordP3uiv");
+ glad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)load("glTexCoordP4ui");
+ glad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)load("glTexCoordP4uiv");
+ glad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)load("glMultiTexCoordP1ui");
+ glad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)load("glMultiTexCoordP1uiv");
+ glad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)load("glMultiTexCoordP2ui");
+ glad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)load("glMultiTexCoordP2uiv");
+ glad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)load("glMultiTexCoordP3ui");
+ glad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)load("glMultiTexCoordP3uiv");
+ glad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)load("glMultiTexCoordP4ui");
+ glad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)load("glMultiTexCoordP4uiv");
+ glad_glNormalP3ui = (PFNGLNORMALP3UIPROC)load("glNormalP3ui");
+ glad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC)load("glNormalP3uiv");
+ glad_glColorP3ui = (PFNGLCOLORP3UIPROC)load("glColorP3ui");
+ glad_glColorP3uiv = (PFNGLCOLORP3UIVPROC)load("glColorP3uiv");
+ glad_glColorP4ui = (PFNGLCOLORP4UIPROC)load("glColorP4ui");
+ glad_glColorP4uiv = (PFNGLCOLORP4UIVPROC)load("glColorP4uiv");
+ glad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)load("glSecondaryColorP3ui");
+ glad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)load("glSecondaryColorP3uiv");
+}
+static void load_GL_EXT_framebuffer_object(GLADloadproc load) {
+ if(!GLAD_GL_EXT_framebuffer_object || GLAD_GL_VERSION_3_0) return;
+ glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbufferEXT");
+ glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbufferEXT");
+ glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffersEXT");
+ glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffersEXT");
+ glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorageEXT");
+ glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameterivEXT");
+ glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebufferEXT");
+ glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebufferEXT");
+ glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffersEXT");
+ glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffersEXT");
+ glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatusEXT");
+ glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load("glFramebufferTexture1DEXT");
+ glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2DEXT");
+ glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load("glFramebufferTexture3DEXT");
+ glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbufferEXT");
+ glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameterivEXT");
+ glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmapEXT");
+}
+static void load_GL_KHR_debug(GLADloadproc load) {
+ if(!GLAD_GL_KHR_debug) return;
+ glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load("glDebugMessageControl");
+ glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load("glDebugMessageInsert");
+ glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load("glDebugMessageCallback");
+ glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load("glGetDebugMessageLog");
+ glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load("glPushDebugGroup");
+ glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load("glPopDebugGroup");
+ glad_glObjectLabel = (PFNGLOBJECTLABELPROC)load("glObjectLabel");
+ glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load("glGetObjectLabel");
+ glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load("glObjectPtrLabel");
+ glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel");
+ glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv");
+ glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)load("glDebugMessageControlKHR");
+ glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)load("glDebugMessageInsertKHR");
+ glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)load("glDebugMessageCallbackKHR");
+ glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)load("glGetDebugMessageLogKHR");
+ glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)load("glPushDebugGroupKHR");
+ glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)load("glPopDebugGroupKHR");
+ glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)load("glObjectLabelKHR");
+ glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)load("glGetObjectLabelKHR");
+ glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)load("glObjectPtrLabelKHR");
+ glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)load("glGetObjectPtrLabelKHR");
+ glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)load("glGetPointervKHR");
+}
+static int find_extensionsGL(void) {
+ if (!get_exts()) return 0;
+ GLAD_GL_EXT_framebuffer_object = has_ext("GL_EXT_framebuffer_object");
+ GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
+ GLAD_GL_EXT_texture_filter_anisotropic = has_ext("GL_EXT_texture_filter_anisotropic");
+ GLAD_GL_KHR_debug = has_ext("GL_KHR_debug");
+ GLAD_GL_KHR_texture_compression_astc_ldr = has_ext("GL_KHR_texture_compression_astc_ldr");
+ free_exts();
+ return 1;
+}
+
+static void find_coreGL(void) {
+
+ /* Thank you @elmindreda
+ * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176
+ * https://github.com/glfw/glfw/blob/master/src/context.c#L36
+ */
+ int i, major, minor;
+
+ const char* version;
+ const char* prefixes[] = {
+ "OpenGL ES-CM ",
+ "OpenGL ES-CL ",
+ "OpenGL ES ",
+ NULL
+ };
+
+ version = (const char*) glGetString(GL_VERSION);
+ if (!version) return;
+
+ for (i = 0; prefixes[i]; i++) {
+ const size_t length = strlen(prefixes[i]);
+ if (strncmp(version, prefixes[i], length) == 0) {
+ version += length;
+ break;
+ }
+ }
+
+/* PR #18 */
+#ifdef _MSC_VER
+ sscanf_s(version, "%d.%d", &major, &minor);
+#else
+ sscanf(version, "%d.%d", &major, &minor);
+#endif
+
+ if(major > requested_major_version) {
+ major = requested_major_version;
+ }
+
+ if(major == requested_major_version && minor > requested_minor_version) {
+ minor = requested_minor_version;
+ }
+
+ GLVersion.major = major; GLVersion.minor = minor;
+ max_loaded_major = major; max_loaded_minor = minor;
+ GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;
+ GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1;
+ GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1;
+ GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1;
+ GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1;
+ GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1;
+ GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;
+ GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2;
+ GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;
+ GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3;
+ GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3;
+ GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3;
+ if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 3)) {
+ max_loaded_major = 3;
+ max_loaded_minor = 3;
+ }
+}
+
+int gladLoadGLLoader(GLADloadproc load, int requestedVersion) {
+ requested_major_version = requestedVersion / 10;
+ requested_minor_version = requestedVersion % 10;
+ GLVersion.major = 0; GLVersion.minor = 0;
+ glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ if(glGetString == NULL) return 0;
+ if(glGetString(GL_VERSION) == NULL) return 0;
+ find_coreGL();
+ load_GL_VERSION_1_0(load);
+ load_GL_VERSION_1_1(load);
+ load_GL_VERSION_1_2(load);
+ load_GL_VERSION_1_3(load);
+ load_GL_VERSION_1_4(load);
+ load_GL_VERSION_1_5(load);
+ load_GL_VERSION_2_0(load);
+ load_GL_VERSION_2_1(load);
+ load_GL_VERSION_3_0(load);
+ load_GL_VERSION_3_1(load);
+ load_GL_VERSION_3_2(load);
+ load_GL_VERSION_3_3(load);
+
+ if (!find_extensionsGL()) return 0;
+ load_GL_EXT_framebuffer_object(load);
+ load_GL_KHR_debug(load);
+ return GLVersion.major != 0 || GLVersion.minor != 0;
+}
+
+static void load_GL_ES_VERSION_2_0(GLADloadproc load) {
+ if(!GLAD_GL_ES_VERSION_2_0) return;
+ glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture");
+ glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader");
+ glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation");
+ glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer");
+ glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer");
+ glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer");
+ glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture");
+ glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor");
+ glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation");
+ glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate");
+ glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc");
+ glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate");
+ glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData");
+ glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData");
+ glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus");
+ glad_glClear = (PFNGLCLEARPROC)load("glClear");
+ glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor");
+ glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC)load("glClearDepthf");
+ glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil");
+ glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask");
+ glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader");
+ glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D");
+ glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D");
+ glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D");
+ glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D");
+ glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram");
+ glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader");
+ glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace");
+ glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers");
+ glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers");
+ glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram");
+ glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers");
+ glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader");
+ glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures");
+ glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc");
+ glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask");
+ glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC)load("glDepthRangef");
+ glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader");
+ glad_glDisable = (PFNGLDISABLEPROC)load("glDisable");
+ glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray");
+ glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays");
+ glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements");
+ glad_glEnable = (PFNGLENABLEPROC)load("glEnable");
+ glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray");
+ glad_glFinish = (PFNGLFINISHPROC)load("glFinish");
+ glad_glFlush = (PFNGLFLUSHPROC)load("glFlush");
+ glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer");
+ glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D");
+ glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace");
+ glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers");
+ glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap");
+ glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers");
+ glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers");
+ glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures");
+ glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib");
+ glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform");
+ glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders");
+ glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation");
+ glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv");
+ glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv");
+ glad_glGetError = (PFNGLGETERRORPROC)load("glGetError");
+ glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv");
+ glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv");
+ glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv");
+ glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv");
+ glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog");
+ glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv");
+ glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv");
+ glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog");
+ glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)load("glGetShaderPrecisionFormat");
+ glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource");
+ glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv");
+ glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv");
+ glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv");
+ glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv");
+ glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation");
+ glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv");
+ glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv");
+ glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv");
+ glad_glHint = (PFNGLHINTPROC)load("glHint");
+ glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer");
+ glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled");
+ glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer");
+ glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram");
+ glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer");
+ glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader");
+ glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture");
+ glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth");
+ glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram");
+ glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei");
+ glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset");
+ glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels");
+ glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)load("glReleaseShaderCompiler");
+ glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage");
+ glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage");
+ glad_glScissor = (PFNGLSCISSORPROC)load("glScissor");
+ glad_glShaderBinary = (PFNGLSHADERBINARYPROC)load("glShaderBinary");
+ glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource");
+ glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc");
+ glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate");
+ glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask");
+ glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate");
+ glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp");
+ glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate");
+ glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D");
+ glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf");
+ glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv");
+ glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri");
+ glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv");
+ glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D");
+ glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f");
+ glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv");
+ glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i");
+ glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv");
+ glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f");
+ glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv");
+ glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i");
+ glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv");
+ glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f");
+ glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv");
+ glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i");
+ glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv");
+ glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f");
+ glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv");
+ glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i");
+ glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv");
+ glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv");
+ glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv");
+ glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv");
+ glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram");
+ glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram");
+ glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f");
+ glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv");
+ glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f");
+ glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv");
+ glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f");
+ glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv");
+ glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f");
+ glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv");
+ glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer");
+ glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport");
+}
+static void load_GL_ES_VERSION_3_0(GLADloadproc load) {
+ if(!GLAD_GL_ES_VERSION_3_0) return;
+ glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer");
+ glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements");
+ glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D");
+ glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D");
+ glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D");
+ glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D");
+ glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D");
+ glad_glGenQueries = (PFNGLGENQUERIESPROC)load("glGenQueries");
+ glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load("glDeleteQueries");
+ glad_glIsQuery = (PFNGLISQUERYPROC)load("glIsQuery");
+ glad_glBeginQuery = (PFNGLBEGINQUERYPROC)load("glBeginQuery");
+ glad_glEndQuery = (PFNGLENDQUERYPROC)load("glEndQuery");
+ glad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load("glGetQueryiv");
+ glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv");
+ glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer");
+ glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv");
+ glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers");
+ glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load("glUniformMatrix2x3fv");
+ glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load("glUniformMatrix3x2fv");
+ glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load("glUniformMatrix2x4fv");
+ glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load("glUniformMatrix4x2fv");
+ glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load("glUniformMatrix3x4fv");
+ glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load("glUniformMatrix4x3fv");
+ glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load("glBlitFramebuffer");
+ glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample");
+ glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer");
+ glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load("glMapBufferRange");
+ glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load("glFlushMappedBufferRange");
+ glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load("glBindVertexArray");
+ glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load("glDeleteVertexArrays");
+ glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load("glGenVertexArrays");
+ glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load("glIsVertexArray");
+ glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v");
+ glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load("glBeginTransformFeedback");
+ glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load("glEndTransformFeedback");
+ glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange");
+ glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase");
+ glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load("glTransformFeedbackVaryings");
+ glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load("glGetTransformFeedbackVarying");
+ glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer");
+ glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv");
+ glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv");
+ glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i");
+ glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui");
+ glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv");
+ glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv");
+ glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv");
+ glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load("glGetFragDataLocation");
+ glad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load("glUniform1ui");
+ glad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load("glUniform2ui");
+ glad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load("glUniform3ui");
+ glad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load("glUniform4ui");
+ glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load("glUniform1uiv");
+ glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load("glUniform2uiv");
+ glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load("glUniform3uiv");
+ glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load("glUniform4uiv");
+ glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load("glClearBufferiv");
+ glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load("glClearBufferuiv");
+ glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load("glClearBufferfv");
+ glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load("glClearBufferfi");
+ glad_glGetStringi = (PFNGLGETSTRINGIPROC)load("glGetStringi");
+ glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData");
+ glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices");
+ glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv");
+ glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex");
+ glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv");
+ glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName");
+ glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding");
+ glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced");
+ glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced");
+ glad_glFenceSync = (PFNGLFENCESYNCPROC)load("glFenceSync");
+ glad_glIsSync = (PFNGLISSYNCPROC)load("glIsSync");
+ glad_glDeleteSync = (PFNGLDELETESYNCPROC)load("glDeleteSync");
+ glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load("glClientWaitSync");
+ glad_glWaitSync = (PFNGLWAITSYNCPROC)load("glWaitSync");
+ glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load("glGetInteger64v");
+ glad_glGetSynciv = (PFNGLGETSYNCIVPROC)load("glGetSynciv");
+ glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load("glGetInteger64i_v");
+ glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load("glGetBufferParameteri64v");
+ glad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load("glGenSamplers");
+ glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load("glDeleteSamplers");
+ glad_glIsSampler = (PFNGLISSAMPLERPROC)load("glIsSampler");
+ glad_glBindSampler = (PFNGLBINDSAMPLERPROC)load("glBindSampler");
+ glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load("glSamplerParameteri");
+ glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load("glSamplerParameteriv");
+ glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load("glSamplerParameterf");
+ glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load("glSamplerParameterfv");
+ glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load("glGetSamplerParameteriv");
+ glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load("glGetSamplerParameterfv");
+ glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load("glVertexAttribDivisor");
+ glad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)load("glBindTransformFeedback");
+ glad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)load("glDeleteTransformFeedbacks");
+ glad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)load("glGenTransformFeedbacks");
+ glad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)load("glIsTransformFeedback");
+ glad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)load("glPauseTransformFeedback");
+ glad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)load("glResumeTransformFeedback");
+ glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)load("glGetProgramBinary");
+ glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC)load("glProgramBinary");
+ glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load("glProgramParameteri");
+ glad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)load("glInvalidateFramebuffer");
+ glad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)load("glInvalidateSubFramebuffer");
+ glad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)load("glTexStorage2D");
+ glad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)load("glTexStorage3D");
+ glad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)load("glGetInternalformativ");
+}
+static void load_GL_ES_VERSION_3_1(GLADloadproc load) {
+ if(!GLAD_GL_ES_VERSION_3_1) return;
+ glad_glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)load("glDispatchCompute");
+ glad_glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC)load("glDispatchComputeIndirect");
+ glad_glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)load("glDrawArraysIndirect");
+ glad_glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)load("glDrawElementsIndirect");
+ glad_glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)load("glFramebufferParameteri");
+ glad_glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC)load("glGetFramebufferParameteriv");
+ glad_glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)load("glGetProgramInterfaceiv");
+ glad_glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)load("glGetProgramResourceIndex");
+ glad_glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)load("glGetProgramResourceName");
+ glad_glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)load("glGetProgramResourceiv");
+ glad_glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC)load("glGetProgramResourceLocation");
+ glad_glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC)load("glUseProgramStages");
+ glad_glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC)load("glActiveShaderProgram");
+ glad_glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC)load("glCreateShaderProgramv");
+ glad_glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC)load("glBindProgramPipeline");
+ glad_glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC)load("glDeleteProgramPipelines");
+ glad_glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC)load("glGenProgramPipelines");
+ glad_glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC)load("glIsProgramPipeline");
+ glad_glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC)load("glGetProgramPipelineiv");
+ glad_glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)load("glProgramUniform1i");
+ glad_glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC)load("glProgramUniform2i");
+ glad_glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC)load("glProgramUniform3i");
+ glad_glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC)load("glProgramUniform4i");
+ glad_glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC)load("glProgramUniform1ui");
+ glad_glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC)load("glProgramUniform2ui");
+ glad_glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC)load("glProgramUniform3ui");
+ glad_glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC)load("glProgramUniform4ui");
+ glad_glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC)load("glProgramUniform1f");
+ glad_glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC)load("glProgramUniform2f");
+ glad_glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC)load("glProgramUniform3f");
+ glad_glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC)load("glProgramUniform4f");
+ glad_glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC)load("glProgramUniform1iv");
+ glad_glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC)load("glProgramUniform2iv");
+ glad_glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC)load("glProgramUniform3iv");
+ glad_glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC)load("glProgramUniform4iv");
+ glad_glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC)load("glProgramUniform1uiv");
+ glad_glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC)load("glProgramUniform2uiv");
+ glad_glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC)load("glProgramUniform3uiv");
+ glad_glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC)load("glProgramUniform4uiv");
+ glad_glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC)load("glProgramUniform1fv");
+ glad_glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC)load("glProgramUniform2fv");
+ glad_glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC)load("glProgramUniform3fv");
+ glad_glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC)load("glProgramUniform4fv");
+ glad_glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC)load("glProgramUniformMatrix2fv");
+ glad_glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC)load("glProgramUniformMatrix3fv");
+ glad_glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC)load("glProgramUniformMatrix4fv");
+ glad_glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)load("glProgramUniformMatrix2x3fv");
+ glad_glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)load("glProgramUniformMatrix3x2fv");
+ glad_glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)load("glProgramUniformMatrix2x4fv");
+ glad_glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)load("glProgramUniformMatrix4x2fv");
+ glad_glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)load("glProgramUniformMatrix3x4fv");
+ glad_glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)load("glProgramUniformMatrix4x3fv");
+ glad_glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC)load("glValidateProgramPipeline");
+ glad_glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC)load("glGetProgramPipelineInfoLog");
+ glad_glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)load("glBindImageTexture");
+ glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load("glGetBooleani_v");
+ glad_glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)load("glMemoryBarrier");
+ glad_glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC)load("glMemoryBarrierByRegion");
+ glad_glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)load("glTexStorage2DMultisample");
+ glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load("glGetMultisamplefv");
+ glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load("glSampleMaski");
+ glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load("glGetTexLevelParameteriv");
+ glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load("glGetTexLevelParameterfv");
+ glad_glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)load("glBindVertexBuffer");
+ glad_glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)load("glVertexAttribFormat");
+ glad_glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)load("glVertexAttribIFormat");
+ glad_glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)load("glVertexAttribBinding");
+ glad_glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)load("glVertexBindingDivisor");
+}
+static int find_extensionsGLES2(void) {
+ if (!get_exts()) return 0;
+ GLAD_GL_EXT_texture_compression_s3tc = has_ext("GL_EXT_texture_compression_s3tc");
+ GLAD_GL_EXT_texture_filter_anisotropic = has_ext("GL_EXT_texture_filter_anisotropic");
+ GLAD_GL_KHR_debug = has_ext("GL_KHR_debug");
+ GLAD_GL_KHR_texture_compression_astc_ldr = has_ext("GL_KHR_texture_compression_astc_ldr");
+ free_exts();
+ return 1;
+}
+
+static void find_coreGLES2(void) {
+
+ /* Thank you @elmindreda
+ * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176
+ * https://github.com/glfw/glfw/blob/master/src/context.c#L36
+ */
+ int i, major, minor;
+
+ const char* version;
+ const char* prefixes[] = {
+ "OpenGL ES-CM ",
+ "OpenGL ES-CL ",
+ "OpenGL ES ",
+ NULL
+ };
+
+ version = (const char*) glGetString(GL_VERSION);
+ if (!version) return;
+
+ for (i = 0; prefixes[i]; i++) {
+ const size_t length = strlen(prefixes[i]);
+ if (strncmp(version, prefixes[i], length) == 0) {
+ version += length;
+ break;
+ }
+ }
+
+/* PR #18 */
+#ifdef _MSC_VER
+ sscanf_s(version, "%d.%d", &major, &minor);
+#else
+ sscanf(version, "%d.%d", &major, &minor);
+#endif
+
+ if(major > requested_major_version) {
+ major = requested_major_version;
+ }
+
+ if(major == requested_major_version && minor > requested_minor_version) {
+ minor = requested_minor_version;
+ }
+
+ GLVersion.major = major; GLVersion.minor = minor;
+ max_loaded_major = major; max_loaded_minor = minor;
+ GLAD_GL_ES_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;
+ GLAD_GL_ES_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;
+ GLAD_GL_ES_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3;
+ if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 1)) {
+ max_loaded_major = 3;
+ max_loaded_minor = 1;
+ }
+}
+
+int gladLoadGLES2Loader(GLADloadproc load, int requestedVersion) {
+ requested_major_version = requestedVersion / 10;
+ requested_minor_version = requestedVersion % 10;
+ GLVersion.major = 0; GLVersion.minor = 0;
+ glGetString = (PFNGLGETSTRINGPROC)load("glGetString");
+ if(glGetString == NULL) return 0;
+ if(glGetString(GL_VERSION) == NULL) return 0;
+ find_coreGLES2();
+ load_GL_ES_VERSION_2_0(load);
+ load_GL_ES_VERSION_3_0(load);
+ load_GL_ES_VERSION_3_1(load);
+
+ if (!find_extensionsGLES2()) return 0;
+ load_GL_KHR_debug(load);
+ return GLVersion.major != 0 || GLVersion.minor != 0;
+}
diff --git a/src/gl/glad/glad.h b/src/gl/glad/glad.h
new file mode 100644
index 0000000..8f785d5
--- /dev/null
+++ b/src/gl/glad/glad.h
@@ -0,0 +1,2808 @@
+/*
+
+ OpenGL, OpenGL ES loader generated by glad 0.1.34 on Wed Feb 17 15:35:25 2021.
+
+ Language/Generator: C/C++
+ Specification: gl
+ APIs: gl=3.3, gles2=3.1
+ Profile: core
+ Extensions:
+ GL_EXT_framebuffer_object,
+ GL_EXT_texture_compression_s3tc,
+ GL_EXT_texture_filter_anisotropic,
+ GL_KHR_debug,
+ GL_KHR_texture_compression_astc_ldr
+ Loader: False
+ Local files: False
+ Omit khrplatform: False
+ Reproducible: False
+
+ Commandline:
+ --profile="core" --api="gl=3.3,gles2=3.1" --generator="c" --spec="gl" --no-loader --extensions="GL_EXT_framebuffer_object,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic,GL_KHR_debug,GL_KHR_texture_compression_astc_ldr"
+ Online:
+ https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D3.3&api=gles2%3D3.1&extensions=GL_EXT_framebuffer_object&extensions=GL_EXT_texture_compression_s3tc&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug&extensions=GL_KHR_texture_compression_astc_ldr
+*/
+
+
+#ifndef __glad_h_
+#define __glad_h_
+
+#ifdef __gl_h_
+#error OpenGL header already included, remove this include, glad already provides it
+#endif
+#define __gl_h_
+
+#ifdef __gl2_h_
+#error OpenGL ES 2 header already included, remove this include, glad already provides it
+#endif
+#define __gl2_h_
+
+#ifdef __gl3_h_
+#error OpenGL ES 3 header already included, remove this include, glad already provides it
+#endif
+#define __gl3_h_
+
+#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
+#define APIENTRY __stdcall
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+#ifndef APIENTRYP
+#define APIENTRYP APIENTRY *
+#endif
+
+#ifndef GLAPIENTRY
+#define GLAPIENTRY APIENTRY
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct gladGLversionStruct {
+ int major;
+ int minor;
+};
+
+typedef void* (* GLADloadproc)(const char *name);
+
+#ifndef GLAPI
+# if defined(GLAD_GLAPI_EXPORT)
+# if defined(_WIN32) || defined(__CYGWIN__)
+# if defined(GLAD_GLAPI_EXPORT_BUILD)
+# if defined(__GNUC__)
+# define GLAPI __attribute__ ((dllexport)) extern
+# else
+# define GLAPI __declspec(dllexport) extern
+# endif
+# else
+# if defined(__GNUC__)
+# define GLAPI __attribute__ ((dllimport)) extern
+# else
+# define GLAPI __declspec(dllimport) extern
+# endif
+# endif
+# elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)
+# define GLAPI __attribute__ ((visibility ("default"))) extern
+# else
+# define GLAPI extern
+# endif
+# else
+# define GLAPI extern
+# endif
+#endif
+
+GLAPI struct gladGLversionStruct GLVersion;
+GLAPI int gladLoadGLLoader(GLADloadproc, int requestedVersion);
+
+GLAPI int gladLoadGLES2Loader(GLADloadproc, int requestedVersion);
+
+#include "khrplatform.h"
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef void GLvoid;
+typedef khronos_int8_t GLbyte;
+typedef khronos_uint8_t GLubyte;
+typedef khronos_int16_t GLshort;
+typedef khronos_uint16_t GLushort;
+typedef int GLint;
+typedef unsigned int GLuint;
+typedef khronos_int32_t GLclampx;
+typedef int GLsizei;
+typedef khronos_float_t GLfloat;
+typedef khronos_float_t GLclampf;
+typedef double GLdouble;
+typedef double GLclampd;
+typedef void *GLeglClientBufferEXT;
+typedef void *GLeglImageOES;
+typedef char GLchar;
+typedef char GLcharARB;
+#ifdef __APPLE__
+typedef void *GLhandleARB;
+#else
+typedef unsigned int GLhandleARB;
+#endif
+typedef khronos_uint16_t GLhalf;
+typedef khronos_uint16_t GLhalfARB;
+typedef khronos_int32_t GLfixed;
+typedef khronos_intptr_t GLintptr;
+typedef khronos_intptr_t GLintptrARB;
+typedef khronos_ssize_t GLsizeiptr;
+typedef khronos_ssize_t GLsizeiptrARB;
+typedef khronos_int64_t GLint64;
+typedef khronos_int64_t GLint64EXT;
+typedef khronos_uint64_t GLuint64;
+typedef khronos_uint64_t GLuint64EXT;
+typedef struct __GLsync *GLsync;
+struct _cl_context;
+struct _cl_event;
+typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
+typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);
+typedef unsigned short GLhalfNV;
+typedef GLintptr GLvdpauSurfaceNV;
+typedef void (APIENTRY *GLVULKANPROCNV)(void);
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_COLOR_BUFFER_BIT 0x00004000
+#define GL_FALSE 0
+#define GL_TRUE 1
+#define GL_POINTS 0x0000
+#define GL_LINES 0x0001
+#define GL_LINE_LOOP 0x0002
+#define GL_LINE_STRIP 0x0003
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRIANGLE_FAN 0x0006
+#define GL_NEVER 0x0200
+#define GL_LESS 0x0201
+#define GL_EQUAL 0x0202
+#define GL_LEQUAL 0x0203
+#define GL_GREATER 0x0204
+#define GL_NOTEQUAL 0x0205
+#define GL_GEQUAL 0x0206
+#define GL_ALWAYS 0x0207
+#define GL_ZERO 0
+#define GL_ONE 1
+#define GL_SRC_COLOR 0x0300
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_ALPHA 0x0304
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+#define GL_DST_COLOR 0x0306
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_SRC_ALPHA_SATURATE 0x0308
+#define GL_NONE 0
+#define GL_FRONT_LEFT 0x0400
+#define GL_FRONT_RIGHT 0x0401
+#define GL_BACK_LEFT 0x0402
+#define GL_BACK_RIGHT 0x0403
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_LEFT 0x0406
+#define GL_RIGHT 0x0407
+#define GL_FRONT_AND_BACK 0x0408
+#define GL_NO_ERROR 0
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVALID_OPERATION 0x0502
+#define GL_OUT_OF_MEMORY 0x0505
+#define GL_CW 0x0900
+#define GL_CCW 0x0901
+#define GL_POINT_SIZE 0x0B11
+#define GL_POINT_SIZE_RANGE 0x0B12
+#define GL_POINT_SIZE_GRANULARITY 0x0B13
+#define GL_LINE_SMOOTH 0x0B20
+#define GL_LINE_WIDTH 0x0B21
+#define GL_LINE_WIDTH_RANGE 0x0B22
+#define GL_LINE_WIDTH_GRANULARITY 0x0B23
+#define GL_POLYGON_MODE 0x0B40
+#define GL_POLYGON_SMOOTH 0x0B41
+#define GL_CULL_FACE 0x0B44
+#define GL_CULL_FACE_MODE 0x0B45
+#define GL_FRONT_FACE 0x0B46
+#define GL_DEPTH_RANGE 0x0B70
+#define GL_DEPTH_TEST 0x0B71
+#define GL_DEPTH_WRITEMASK 0x0B72
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
+#define GL_DEPTH_FUNC 0x0B74
+#define GL_STENCIL_TEST 0x0B90
+#define GL_STENCIL_CLEAR_VALUE 0x0B91
+#define GL_STENCIL_FUNC 0x0B92
+#define GL_STENCIL_VALUE_MASK 0x0B93
+#define GL_STENCIL_FAIL 0x0B94
+#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define GL_STENCIL_REF 0x0B97
+#define GL_STENCIL_WRITEMASK 0x0B98
+#define GL_VIEWPORT 0x0BA2
+#define GL_DITHER 0x0BD0
+#define GL_BLEND_DST 0x0BE0
+#define GL_BLEND_SRC 0x0BE1
+#define GL_BLEND 0x0BE2
+#define GL_LOGIC_OP_MODE 0x0BF0
+#define GL_DRAW_BUFFER 0x0C01
+#define GL_READ_BUFFER 0x0C02
+#define GL_SCISSOR_BOX 0x0C10
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_COLOR_WRITEMASK 0x0C23
+#define GL_DOUBLEBUFFER 0x0C32
+#define GL_STEREO 0x0C33
+#define GL_LINE_SMOOTH_HINT 0x0C52
+#define GL_POLYGON_SMOOTH_HINT 0x0C53
+#define GL_UNPACK_SWAP_BYTES 0x0CF0
+#define GL_UNPACK_LSB_FIRST 0x0CF1
+#define GL_UNPACK_ROW_LENGTH 0x0CF2
+#define GL_UNPACK_SKIP_ROWS 0x0CF3
+#define GL_UNPACK_SKIP_PIXELS 0x0CF4
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_PACK_SWAP_BYTES 0x0D00
+#define GL_PACK_LSB_FIRST 0x0D01
+#define GL_PACK_ROW_LENGTH 0x0D02
+#define GL_PACK_SKIP_ROWS 0x0D03
+#define GL_PACK_SKIP_PIXELS 0x0D04
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_MAX_VIEWPORT_DIMS 0x0D3A
+#define GL_SUBPIXEL_BITS 0x0D50
+#define GL_TEXTURE_1D 0x0DE0
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_TEXTURE_WIDTH 0x1000
+#define GL_TEXTURE_HEIGHT 0x1001
+#define GL_TEXTURE_BORDER_COLOR 0x1004
+#define GL_DONT_CARE 0x1100
+#define GL_FASTEST 0x1101
+#define GL_NICEST 0x1102
+#define GL_BYTE 0x1400
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_SHORT 0x1402
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_INT 0x1404
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_CLEAR 0x1500
+#define GL_AND 0x1501
+#define GL_AND_REVERSE 0x1502
+#define GL_COPY 0x1503
+#define GL_AND_INVERTED 0x1504
+#define GL_NOOP 0x1505
+#define GL_XOR 0x1506
+#define GL_OR 0x1507
+#define GL_NOR 0x1508
+#define GL_EQUIV 0x1509
+#define GL_INVERT 0x150A
+#define GL_OR_REVERSE 0x150B
+#define GL_COPY_INVERTED 0x150C
+#define GL_OR_INVERTED 0x150D
+#define GL_NAND 0x150E
+#define GL_SET 0x150F
+#define GL_TEXTURE 0x1702
+#define GL_COLOR 0x1800
+#define GL_DEPTH 0x1801
+#define GL_STENCIL 0x1802
+#define GL_STENCIL_INDEX 0x1901
+#define GL_DEPTH_COMPONENT 0x1902
+#define GL_RED 0x1903
+#define GL_GREEN 0x1904
+#define GL_BLUE 0x1905
+#define GL_ALPHA 0x1906
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_POINT 0x1B00
+#define GL_LINE 0x1B01
+#define GL_FILL 0x1B02
+#define GL_KEEP 0x1E00
+#define GL_REPLACE 0x1E01
+#define GL_INCR 0x1E02
+#define GL_DECR 0x1E03
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+#define GL_NEAREST 0x2600
+#define GL_LINEAR 0x2601
+#define GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_REPEAT 0x2901
+#define GL_COLOR_LOGIC_OP 0x0BF2
+#define GL_POLYGON_OFFSET_UNITS 0x2A00
+#define GL_POLYGON_OFFSET_POINT 0x2A01
+#define GL_POLYGON_OFFSET_LINE 0x2A02
+#define GL_POLYGON_OFFSET_FILL 0x8037
+#define GL_POLYGON_OFFSET_FACTOR 0x8038
+#define GL_TEXTURE_BINDING_1D 0x8068
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_TEXTURE_INTERNAL_FORMAT 0x1003
+#define GL_TEXTURE_RED_SIZE 0x805C
+#define GL_TEXTURE_GREEN_SIZE 0x805D
+#define GL_TEXTURE_BLUE_SIZE 0x805E
+#define GL_TEXTURE_ALPHA_SIZE 0x805F
+#define GL_DOUBLE 0x140A
+#define GL_PROXY_TEXTURE_1D 0x8063
+#define GL_PROXY_TEXTURE_2D 0x8064
+#define GL_R3_G3_B2 0x2A10
+#define GL_RGB4 0x804F
+#define GL_RGB5 0x8050
+#define GL_RGB8 0x8051
+#define GL_RGB10 0x8052
+#define GL_RGB12 0x8053
+#define GL_RGB16 0x8054
+#define GL_RGBA2 0x8055
+#define GL_RGBA4 0x8056
+#define GL_RGB5_A1 0x8057
+#define GL_RGBA8 0x8058
+#define GL_RGB10_A2 0x8059
+#define GL_RGBA12 0x805A
+#define GL_RGBA16 0x805B
+#define GL_UNSIGNED_BYTE_3_3_2 0x8032
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_INT_8_8_8_8 0x8035
+#define GL_UNSIGNED_INT_10_10_10_2 0x8036
+#define GL_TEXTURE_BINDING_3D 0x806A
+#define GL_PACK_SKIP_IMAGES 0x806B
+#define GL_PACK_IMAGE_HEIGHT 0x806C
+#define GL_UNPACK_SKIP_IMAGES 0x806D
+#define GL_UNPACK_IMAGE_HEIGHT 0x806E
+#define GL_TEXTURE_3D 0x806F
+#define GL_PROXY_TEXTURE_3D 0x8070
+#define GL_TEXTURE_DEPTH 0x8071
+#define GL_TEXTURE_WRAP_R 0x8072
+#define GL_MAX_3D_TEXTURE_SIZE 0x8073
+#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368
+#define GL_BGR 0x80E0
+#define GL_BGRA 0x80E1
+#define GL_MAX_ELEMENTS_VERTICES 0x80E8
+#define GL_MAX_ELEMENTS_INDICES 0x80E9
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_TEXTURE_MIN_LOD 0x813A
+#define GL_TEXTURE_MAX_LOD 0x813B
+#define GL_TEXTURE_BASE_LEVEL 0x813C
+#define GL_TEXTURE_MAX_LEVEL 0x813D
+#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12
+#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13
+#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22
+#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23
+#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+#define GL_TEXTURE2 0x84C2
+#define GL_TEXTURE3 0x84C3
+#define GL_TEXTURE4 0x84C4
+#define GL_TEXTURE5 0x84C5
+#define GL_TEXTURE6 0x84C6
+#define GL_TEXTURE7 0x84C7
+#define GL_TEXTURE8 0x84C8
+#define GL_TEXTURE9 0x84C9
+#define GL_TEXTURE10 0x84CA
+#define GL_TEXTURE11 0x84CB
+#define GL_TEXTURE12 0x84CC
+#define GL_TEXTURE13 0x84CD
+#define GL_TEXTURE14 0x84CE
+#define GL_TEXTURE15 0x84CF
+#define GL_TEXTURE16 0x84D0
+#define GL_TEXTURE17 0x84D1
+#define GL_TEXTURE18 0x84D2
+#define GL_TEXTURE19 0x84D3
+#define GL_TEXTURE20 0x84D4
+#define GL_TEXTURE21 0x84D5
+#define GL_TEXTURE22 0x84D6
+#define GL_TEXTURE23 0x84D7
+#define GL_TEXTURE24 0x84D8
+#define GL_TEXTURE25 0x84D9
+#define GL_TEXTURE26 0x84DA
+#define GL_TEXTURE27 0x84DB
+#define GL_TEXTURE28 0x84DC
+#define GL_TEXTURE29 0x84DD
+#define GL_TEXTURE30 0x84DE
+#define GL_TEXTURE31 0x84DF
+#define GL_ACTIVE_TEXTURE 0x84E0
+#define GL_MULTISAMPLE 0x809D
+#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
+#define GL_SAMPLE_ALPHA_TO_ONE 0x809F
+#define GL_SAMPLE_COVERAGE 0x80A0
+#define GL_SAMPLE_BUFFERS 0x80A8
+#define GL_SAMPLES 0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE 0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT 0x80AB
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+#define GL_COMPRESSED_RGB 0x84ED
+#define GL_COMPRESSED_RGBA 0x84EE
+#define GL_TEXTURE_COMPRESSION_HINT 0x84EF
+#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0
+#define GL_TEXTURE_COMPRESSED 0x86A1
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
+#define GL_CLAMP_TO_BORDER 0x812D
+#define GL_BLEND_DST_RGB 0x80C8
+#define GL_BLEND_SRC_RGB 0x80C9
+#define GL_BLEND_DST_ALPHA 0x80CA
+#define GL_BLEND_SRC_ALPHA 0x80CB
+#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_DEPTH_COMPONENT24 0x81A6
+#define GL_DEPTH_COMPONENT32 0x81A7
+#define GL_MIRRORED_REPEAT 0x8370
+#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD
+#define GL_TEXTURE_LOD_BIAS 0x8501
+#define GL_INCR_WRAP 0x8507
+#define GL_DECR_WRAP 0x8508
+#define GL_TEXTURE_DEPTH_SIZE 0x884A
+#define GL_TEXTURE_COMPARE_MODE 0x884C
+#define GL_TEXTURE_COMPARE_FUNC 0x884D
+#define GL_BLEND_COLOR 0x8005
+#define GL_BLEND_EQUATION 0x8009
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_FUNC_ADD 0x8006
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_MIN 0x8007
+#define GL_MAX 0x8008
+#define GL_BUFFER_SIZE 0x8764
+#define GL_BUFFER_USAGE 0x8765
+#define GL_QUERY_COUNTER_BITS 0x8864
+#define GL_CURRENT_QUERY 0x8865
+#define GL_QUERY_RESULT 0x8866
+#define GL_QUERY_RESULT_AVAILABLE 0x8867
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define GL_READ_ONLY 0x88B8
+#define GL_WRITE_ONLY 0x88B9
+#define GL_READ_WRITE 0x88BA
+#define GL_BUFFER_ACCESS 0x88BB
+#define GL_BUFFER_MAPPED 0x88BC
+#define GL_BUFFER_MAP_POINTER 0x88BD
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STREAM_READ 0x88E1
+#define GL_STREAM_COPY 0x88E2
+#define GL_STATIC_DRAW 0x88E4
+#define GL_STATIC_READ 0x88E5
+#define GL_STATIC_COPY 0x88E6
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_DYNAMIC_READ 0x88E9
+#define GL_DYNAMIC_COPY 0x88EA
+#define GL_SAMPLES_PASSED 0x8914
+#define GL_SRC1_ALPHA 0x8589
+#define GL_BLEND_EQUATION_RGB 0x8009
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define GL_STENCIL_BACK_FUNC 0x8800
+#define GL_STENCIL_BACK_FAIL 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_MAX_DRAW_BUFFERS 0x8824
+#define GL_DRAW_BUFFER0 0x8825
+#define GL_DRAW_BUFFER1 0x8826
+#define GL_DRAW_BUFFER2 0x8827
+#define GL_DRAW_BUFFER3 0x8828
+#define GL_DRAW_BUFFER4 0x8829
+#define GL_DRAW_BUFFER5 0x882A
+#define GL_DRAW_BUFFER6 0x882B
+#define GL_DRAW_BUFFER7 0x882C
+#define GL_DRAW_BUFFER8 0x882D
+#define GL_DRAW_BUFFER9 0x882E
+#define GL_DRAW_BUFFER10 0x882F
+#define GL_DRAW_BUFFER11 0x8830
+#define GL_DRAW_BUFFER12 0x8831
+#define GL_DRAW_BUFFER13 0x8832
+#define GL_DRAW_BUFFER14 0x8833
+#define GL_DRAW_BUFFER15 0x8834
+#define GL_BLEND_EQUATION_ALPHA 0x883D
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
+#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
+#define GL_MAX_VARYING_FLOATS 0x8B4B
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_SHADER_TYPE 0x8B4F
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_1D 0x8B5D
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_3D 0x8B5F
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_SAMPLER_1D_SHADOW 0x8B61
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#define GL_DELETE_STATUS 0x8B80
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+#define GL_VALIDATE_STATUS 0x8B83
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_ATTACHED_SHADERS 0x8B85
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define GL_CURRENT_PROGRAM 0x8B8D
+#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0
+#define GL_LOWER_LEFT 0x8CA1
+#define GL_UPPER_LEFT 0x8CA2
+#define GL_STENCIL_BACK_REF 0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#define GL_PIXEL_PACK_BUFFER 0x88EB
+#define GL_PIXEL_UNPACK_BUFFER 0x88EC
+#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED
+#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
+#define GL_FLOAT_MAT2x3 0x8B65
+#define GL_FLOAT_MAT2x4 0x8B66
+#define GL_FLOAT_MAT3x2 0x8B67
+#define GL_FLOAT_MAT3x4 0x8B68
+#define GL_FLOAT_MAT4x2 0x8B69
+#define GL_FLOAT_MAT4x3 0x8B6A
+#define GL_SRGB 0x8C40
+#define GL_SRGB8 0x8C41
+#define GL_SRGB_ALPHA 0x8C42
+#define GL_SRGB8_ALPHA8 0x8C43
+#define GL_COMPRESSED_SRGB 0x8C48
+#define GL_COMPRESSED_SRGB_ALPHA 0x8C49
+#define GL_COMPARE_REF_TO_TEXTURE 0x884E
+#define GL_CLIP_DISTANCE0 0x3000
+#define GL_CLIP_DISTANCE1 0x3001
+#define GL_CLIP_DISTANCE2 0x3002
+#define GL_CLIP_DISTANCE3 0x3003
+#define GL_CLIP_DISTANCE4 0x3004
+#define GL_CLIP_DISTANCE5 0x3005
+#define GL_CLIP_DISTANCE6 0x3006
+#define GL_CLIP_DISTANCE7 0x3007
+#define GL_MAX_CLIP_DISTANCES 0x0D32
+#define GL_MAJOR_VERSION 0x821B
+#define GL_MINOR_VERSION 0x821C
+#define GL_NUM_EXTENSIONS 0x821D
+#define GL_CONTEXT_FLAGS 0x821E
+#define GL_COMPRESSED_RED 0x8225
+#define GL_COMPRESSED_RG 0x8226
+#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001
+#define GL_RGBA32F 0x8814
+#define GL_RGB32F 0x8815
+#define GL_RGBA16F 0x881A
+#define GL_RGB16F 0x881B
+#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD
+#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
+#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904
+#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905
+#define GL_CLAMP_READ_COLOR 0x891C
+#define GL_FIXED_ONLY 0x891D
+#define GL_MAX_VARYING_COMPONENTS 0x8B4B
+#define GL_TEXTURE_1D_ARRAY 0x8C18
+#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19
+#define GL_TEXTURE_2D_ARRAY 0x8C1A
+#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B
+#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C
+#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D
+#define GL_R11F_G11F_B10F 0x8C3A
+#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B
+#define GL_RGB9_E5 0x8C3D
+#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E
+#define GL_TEXTURE_SHARED_SIZE 0x8C3F
+#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76
+#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F
+#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80
+#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83
+#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84
+#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85
+#define GL_PRIMITIVES_GENERATED 0x8C87
+#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88
+#define GL_RASTERIZER_DISCARD 0x8C89
+#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A
+#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B
+#define GL_INTERLEAVED_ATTRIBS 0x8C8C
+#define GL_SEPARATE_ATTRIBS 0x8C8D
+#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E
+#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F
+#define GL_RGBA32UI 0x8D70
+#define GL_RGB32UI 0x8D71
+#define GL_RGBA16UI 0x8D76
+#define GL_RGB16UI 0x8D77
+#define GL_RGBA8UI 0x8D7C
+#define GL_RGB8UI 0x8D7D
+#define GL_RGBA32I 0x8D82
+#define GL_RGB32I 0x8D83
+#define GL_RGBA16I 0x8D88
+#define GL_RGB16I 0x8D89
+#define GL_RGBA8I 0x8D8E
+#define GL_RGB8I 0x8D8F
+#define GL_RED_INTEGER 0x8D94
+#define GL_GREEN_INTEGER 0x8D95
+#define GL_BLUE_INTEGER 0x8D96
+#define GL_RGB_INTEGER 0x8D98
+#define GL_RGBA_INTEGER 0x8D99
+#define GL_BGR_INTEGER 0x8D9A
+#define GL_BGRA_INTEGER 0x8D9B
+#define GL_SAMPLER_1D_ARRAY 0x8DC0
+#define GL_SAMPLER_2D_ARRAY 0x8DC1
+#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3
+#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
+#define GL_SAMPLER_CUBE_SHADOW 0x8DC5
+#define GL_UNSIGNED_INT_VEC2 0x8DC6
+#define GL_UNSIGNED_INT_VEC3 0x8DC7
+#define GL_UNSIGNED_INT_VEC4 0x8DC8
+#define GL_INT_SAMPLER_1D 0x8DC9
+#define GL_INT_SAMPLER_2D 0x8DCA
+#define GL_INT_SAMPLER_3D 0x8DCB
+#define GL_INT_SAMPLER_CUBE 0x8DCC
+#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE
+#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF
+#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1
+#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2
+#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3
+#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4
+#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6
+#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7
+#define GL_QUERY_WAIT 0x8E13
+#define GL_QUERY_NO_WAIT 0x8E14
+#define GL_QUERY_BY_REGION_WAIT 0x8E15
+#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16
+#define GL_BUFFER_ACCESS_FLAGS 0x911F
+#define GL_BUFFER_MAP_LENGTH 0x9120
+#define GL_BUFFER_MAP_OFFSET 0x9121
+#define GL_DEPTH_COMPONENT32F 0x8CAC
+#define GL_DEPTH32F_STENCIL8 0x8CAD
+#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
+#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
+#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210
+#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211
+#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212
+#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213
+#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214
+#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215
+#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216
+#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217
+#define GL_FRAMEBUFFER_DEFAULT 0x8218
+#define GL_FRAMEBUFFER_UNDEFINED 0x8219
+#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
+#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
+#define GL_DEPTH_STENCIL 0x84F9
+#define GL_UNSIGNED_INT_24_8 0x84FA
+#define GL_DEPTH24_STENCIL8 0x88F0
+#define GL_TEXTURE_STENCIL_SIZE 0x88F1
+#define GL_TEXTURE_RED_TYPE 0x8C10
+#define GL_TEXTURE_GREEN_TYPE 0x8C11
+#define GL_TEXTURE_BLUE_TYPE 0x8C12
+#define GL_TEXTURE_ALPHA_TYPE 0x8C13
+#define GL_TEXTURE_DEPTH_TYPE 0x8C16
+#define GL_UNSIGNED_NORMALIZED 0x8C17
+#define GL_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_RENDERBUFFER_BINDING 0x8CA7
+#define GL_READ_FRAMEBUFFER 0x8CA8
+#define GL_DRAW_FRAMEBUFFER 0x8CA9
+#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA
+#define GL_RENDERBUFFER_SAMPLES 0x8CAB
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
+#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_COLOR_ATTACHMENT1 0x8CE1
+#define GL_COLOR_ATTACHMENT2 0x8CE2
+#define GL_COLOR_ATTACHMENT3 0x8CE3
+#define GL_COLOR_ATTACHMENT4 0x8CE4
+#define GL_COLOR_ATTACHMENT5 0x8CE5
+#define GL_COLOR_ATTACHMENT6 0x8CE6
+#define GL_COLOR_ATTACHMENT7 0x8CE7
+#define GL_COLOR_ATTACHMENT8 0x8CE8
+#define GL_COLOR_ATTACHMENT9 0x8CE9
+#define GL_COLOR_ATTACHMENT10 0x8CEA
+#define GL_COLOR_ATTACHMENT11 0x8CEB
+#define GL_COLOR_ATTACHMENT12 0x8CEC
+#define GL_COLOR_ATTACHMENT13 0x8CED
+#define GL_COLOR_ATTACHMENT14 0x8CEE
+#define GL_COLOR_ATTACHMENT15 0x8CEF
+#define GL_COLOR_ATTACHMENT16 0x8CF0
+#define GL_COLOR_ATTACHMENT17 0x8CF1
+#define GL_COLOR_ATTACHMENT18 0x8CF2
+#define GL_COLOR_ATTACHMENT19 0x8CF3
+#define GL_COLOR_ATTACHMENT20 0x8CF4
+#define GL_COLOR_ATTACHMENT21 0x8CF5
+#define GL_COLOR_ATTACHMENT22 0x8CF6
+#define GL_COLOR_ATTACHMENT23 0x8CF7
+#define GL_COLOR_ATTACHMENT24 0x8CF8
+#define GL_COLOR_ATTACHMENT25 0x8CF9
+#define GL_COLOR_ATTACHMENT26 0x8CFA
+#define GL_COLOR_ATTACHMENT27 0x8CFB
+#define GL_COLOR_ATTACHMENT28 0x8CFC
+#define GL_COLOR_ATTACHMENT29 0x8CFD
+#define GL_COLOR_ATTACHMENT30 0x8CFE
+#define GL_COLOR_ATTACHMENT31 0x8CFF
+#define GL_DEPTH_ATTACHMENT 0x8D00
+#define GL_STENCIL_ATTACHMENT 0x8D20
+#define GL_FRAMEBUFFER 0x8D40
+#define GL_RENDERBUFFER 0x8D41
+#define GL_RENDERBUFFER_WIDTH 0x8D42
+#define GL_RENDERBUFFER_HEIGHT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
+#define GL_STENCIL_INDEX1 0x8D46
+#define GL_STENCIL_INDEX4 0x8D47
+#define GL_STENCIL_INDEX8 0x8D48
+#define GL_STENCIL_INDEX16 0x8D49
+#define GL_RENDERBUFFER_RED_SIZE 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
+#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
+#define GL_MAX_SAMPLES 0x8D57
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#define GL_HALF_FLOAT 0x140B
+#define GL_MAP_READ_BIT 0x0001
+#define GL_MAP_WRITE_BIT 0x0002
+#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
+#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
+#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
+#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020
+#define GL_COMPRESSED_RED_RGTC1 0x8DBB
+#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
+#define GL_COMPRESSED_RG_RGTC2 0x8DBD
+#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
+#define GL_RG 0x8227
+#define GL_RG_INTEGER 0x8228
+#define GL_R8 0x8229
+#define GL_R16 0x822A
+#define GL_RG8 0x822B
+#define GL_RG16 0x822C
+#define GL_R16F 0x822D
+#define GL_R32F 0x822E
+#define GL_RG16F 0x822F
+#define GL_RG32F 0x8230
+#define GL_R8I 0x8231
+#define GL_R8UI 0x8232
+#define GL_R16I 0x8233
+#define GL_R16UI 0x8234
+#define GL_R32I 0x8235
+#define GL_R32UI 0x8236
+#define GL_RG8I 0x8237
+#define GL_RG8UI 0x8238
+#define GL_RG16I 0x8239
+#define GL_RG16UI 0x823A
+#define GL_RG32I 0x823B
+#define GL_RG32UI 0x823C
+#define GL_VERTEX_ARRAY_BINDING 0x85B5
+#define GL_SAMPLER_2D_RECT 0x8B63
+#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64
+#define GL_SAMPLER_BUFFER 0x8DC2
+#define GL_INT_SAMPLER_2D_RECT 0x8DCD
+#define GL_INT_SAMPLER_BUFFER 0x8DD0
+#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5
+#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8
+#define GL_TEXTURE_BUFFER 0x8C2A
+#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B
+#define GL_TEXTURE_BINDING_BUFFER 0x8C2C
+#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D
+#define GL_TEXTURE_RECTANGLE 0x84F5
+#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6
+#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8
+#define GL_R8_SNORM 0x8F94
+#define GL_RG8_SNORM 0x8F95
+#define GL_RGB8_SNORM 0x8F96
+#define GL_RGBA8_SNORM 0x8F97
+#define GL_R16_SNORM 0x8F98
+#define GL_RG16_SNORM 0x8F99
+#define GL_RGB16_SNORM 0x8F9A
+#define GL_RGBA16_SNORM 0x8F9B
+#define GL_SIGNED_NORMALIZED 0x8F9C
+#define GL_PRIMITIVE_RESTART 0x8F9D
+#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E
+#define GL_COPY_READ_BUFFER 0x8F36
+#define GL_COPY_WRITE_BUFFER 0x8F37
+#define GL_UNIFORM_BUFFER 0x8A11
+#define GL_UNIFORM_BUFFER_BINDING 0x8A28
+#define GL_UNIFORM_BUFFER_START 0x8A29
+#define GL_UNIFORM_BUFFER_SIZE 0x8A2A
+#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B
+#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C
+#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D
+#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E
+#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F
+#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30
+#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31
+#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32
+#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33
+#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34
+#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35
+#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36
+#define GL_UNIFORM_TYPE 0x8A37
+#define GL_UNIFORM_SIZE 0x8A38
+#define GL_UNIFORM_NAME_LENGTH 0x8A39
+#define GL_UNIFORM_BLOCK_INDEX 0x8A3A
+#define GL_UNIFORM_OFFSET 0x8A3B
+#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C
+#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D
+#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E
+#define GL_UNIFORM_BLOCK_BINDING 0x8A3F
+#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40
+#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41
+#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42
+#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45
+#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46
+#define GL_INVALID_INDEX 0xFFFFFFFF
+#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
+#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
+#define GL_LINES_ADJACENCY 0x000A
+#define GL_LINE_STRIP_ADJACENCY 0x000B
+#define GL_TRIANGLES_ADJACENCY 0x000C
+#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D
+#define GL_PROGRAM_POINT_SIZE 0x8642
+#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29
+#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7
+#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8
+#define GL_GEOMETRY_SHADER 0x8DD9
+#define GL_GEOMETRY_VERTICES_OUT 0x8916
+#define GL_GEOMETRY_INPUT_TYPE 0x8917
+#define GL_GEOMETRY_OUTPUT_TYPE 0x8918
+#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF
+#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0
+#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1
+#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
+#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123
+#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124
+#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125
+#define GL_CONTEXT_PROFILE_MASK 0x9126
+#define GL_DEPTH_CLAMP 0x864F
+#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C
+#define GL_FIRST_VERTEX_CONVENTION 0x8E4D
+#define GL_LAST_VERTEX_CONVENTION 0x8E4E
+#define GL_PROVOKING_VERTEX 0x8E4F
+#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
+#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111
+#define GL_OBJECT_TYPE 0x9112
+#define GL_SYNC_CONDITION 0x9113
+#define GL_SYNC_STATUS 0x9114
+#define GL_SYNC_FLAGS 0x9115
+#define GL_SYNC_FENCE 0x9116
+#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
+#define GL_UNSIGNALED 0x9118
+#define GL_SIGNALED 0x9119
+#define GL_ALREADY_SIGNALED 0x911A
+#define GL_TIMEOUT_EXPIRED 0x911B
+#define GL_CONDITION_SATISFIED 0x911C
+#define GL_WAIT_FAILED 0x911D
+#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF
+#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
+#define GL_SAMPLE_POSITION 0x8E50
+#define GL_SAMPLE_MASK 0x8E51
+#define GL_SAMPLE_MASK_VALUE 0x8E52
+#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59
+#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
+#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101
+#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102
+#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103
+#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104
+#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105
+#define GL_TEXTURE_SAMPLES 0x9106
+#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107
+#define GL_SAMPLER_2D_MULTISAMPLE 0x9108
+#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109
+#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A
+#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B
+#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C
+#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D
+#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E
+#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F
+#define GL_MAX_INTEGER_SAMPLES 0x9110
+#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE
+#define GL_SRC1_COLOR 0x88F9
+#define GL_ONE_MINUS_SRC1_COLOR 0x88FA
+#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
+#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC
+#define GL_ANY_SAMPLES_PASSED 0x8C2F
+#define GL_SAMPLER_BINDING 0x8919
+#define GL_RGB10_A2UI 0x906F
+#define GL_TEXTURE_SWIZZLE_R 0x8E42
+#define GL_TEXTURE_SWIZZLE_G 0x8E43
+#define GL_TEXTURE_SWIZZLE_B 0x8E44
+#define GL_TEXTURE_SWIZZLE_A 0x8E45
+#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#define GL_TIME_ELAPSED 0x88BF
+#define GL_TIMESTAMP 0x8E28
+#define GL_INT_2_10_10_10_REV 0x8D9F
+#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
+#define GL_RED_BITS 0x0D52
+#define GL_GREEN_BITS 0x0D53
+#define GL_BLUE_BITS 0x0D54
+#define GL_ALPHA_BITS 0x0D55
+#define GL_DEPTH_BITS 0x0D56
+#define GL_STENCIL_BITS 0x0D57
+#define GL_GENERATE_MIPMAP_HINT 0x8192
+#define GL_FIXED 0x140C
+#define GL_LUMINANCE 0x1909
+#define GL_LUMINANCE_ALPHA 0x190A
+#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
+#define GL_MAX_VARYING_VECTORS 0x8DFC
+#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
+#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
+#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+#define GL_SHADER_COMPILER 0x8DFA
+#define GL_SHADER_BINARY_FORMATS 0x8DF8
+#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
+#define GL_LOW_FLOAT 0x8DF0
+#define GL_MEDIUM_FLOAT 0x8DF1
+#define GL_HIGH_FLOAT 0x8DF2
+#define GL_LOW_INT 0x8DF3
+#define GL_MEDIUM_INT 0x8DF4
+#define GL_HIGH_INT 0x8DF5
+#define GL_RGB565 0x8D62
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69
+#define GL_COPY_READ_BUFFER_BINDING 0x8F36
+#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37
+#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A
+#define GL_TRANSFORM_FEEDBACK 0x8E22
+#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23
+#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24
+#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25
+#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257
+#define GL_PROGRAM_BINARY_LENGTH 0x8741
+#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE
+#define GL_PROGRAM_BINARY_FORMATS 0x87FF
+#define GL_COMPRESSED_R11_EAC 0x9270
+#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271
+#define GL_COMPRESSED_RG11_EAC 0x9272
+#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273
+#define GL_COMPRESSED_RGB8_ETC2 0x9274
+#define GL_COMPRESSED_SRGB8_ETC2 0x9275
+#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276
+#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277
+#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278
+#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
+#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F
+#define GL_MAX_ELEMENT_INDEX 0x8D6B
+#define GL_NUM_SAMPLE_COUNTS 0x9380
+#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF
+#define GL_COMPUTE_SHADER 0x91B9
+#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB
+#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC
+#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD
+#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262
+#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263
+#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264
+#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265
+#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266
+#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB
+#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE
+#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF
+#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267
+#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE
+#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF
+#define GL_COMPUTE_SHADER_BIT 0x00000020
+#define GL_DRAW_INDIRECT_BUFFER 0x8F3F
+#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43
+#define GL_MAX_UNIFORM_LOCATIONS 0x826E
+#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310
+#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311
+#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313
+#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314
+#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315
+#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316
+#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318
+#define GL_UNIFORM 0x92E1
+#define GL_UNIFORM_BLOCK 0x92E2
+#define GL_PROGRAM_INPUT 0x92E3
+#define GL_PROGRAM_OUTPUT 0x92E4
+#define GL_BUFFER_VARIABLE 0x92E5
+#define GL_SHADER_STORAGE_BLOCK 0x92E6
+#define GL_ATOMIC_COUNTER_BUFFER 0x92C0
+#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4
+#define GL_ACTIVE_RESOURCES 0x92F5
+#define GL_MAX_NAME_LENGTH 0x92F6
+#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7
+#define GL_NAME_LENGTH 0x92F9
+#define GL_TYPE 0x92FA
+#define GL_ARRAY_SIZE 0x92FB
+#define GL_OFFSET 0x92FC
+#define GL_BLOCK_INDEX 0x92FD
+#define GL_ARRAY_STRIDE 0x92FE
+#define GL_MATRIX_STRIDE 0x92FF
+#define GL_IS_ROW_MAJOR 0x9300
+#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301
+#define GL_BUFFER_BINDING 0x9302
+#define GL_BUFFER_DATA_SIZE 0x9303
+#define GL_NUM_ACTIVE_VARIABLES 0x9304
+#define GL_ACTIVE_VARIABLES 0x9305
+#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306
+#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A
+#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B
+#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C
+#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D
+#define GL_LOCATION 0x930E
+#define GL_VERTEX_SHADER_BIT 0x00000001
+#define GL_FRAGMENT_SHADER_BIT 0x00000002
+#define GL_ALL_SHADER_BITS 0xFFFFFFFF
+#define GL_PROGRAM_SEPARABLE 0x8258
+#define GL_ACTIVE_PROGRAM 0x8259
+#define GL_PROGRAM_PIPELINE_BINDING 0x825A
+#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1
+#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2
+#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3
+#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC
+#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0
+#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1
+#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2
+#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6
+#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7
+#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8
+#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC
+#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9
+#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB
+#define GL_MAX_IMAGE_UNITS 0x8F38
+#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA
+#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE
+#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF
+#define GL_IMAGE_BINDING_NAME 0x8F3A
+#define GL_IMAGE_BINDING_LEVEL 0x8F3B
+#define GL_IMAGE_BINDING_LAYERED 0x8F3C
+#define GL_IMAGE_BINDING_LAYER 0x8F3D
+#define GL_IMAGE_BINDING_ACCESS 0x8F3E
+#define GL_IMAGE_BINDING_FORMAT 0x906E
+#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001
+#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002
+#define GL_UNIFORM_BARRIER_BIT 0x00000004
+#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008
+#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
+#define GL_COMMAND_BARRIER_BIT 0x00000040
+#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080
+#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100
+#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200
+#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400
+#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800
+#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000
+#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
+#define GL_IMAGE_2D 0x904D
+#define GL_IMAGE_3D 0x904E
+#define GL_IMAGE_CUBE 0x9050
+#define GL_IMAGE_2D_ARRAY 0x9053
+#define GL_INT_IMAGE_2D 0x9058
+#define GL_INT_IMAGE_3D 0x9059
+#define GL_INT_IMAGE_CUBE 0x905B
+#define GL_INT_IMAGE_2D_ARRAY 0x905E
+#define GL_UNSIGNED_INT_IMAGE_2D 0x9063
+#define GL_UNSIGNED_INT_IMAGE_3D 0x9064
+#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066
+#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069
+#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7
+#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8
+#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9
+#define GL_SHADER_STORAGE_BUFFER 0x90D2
+#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3
+#define GL_SHADER_STORAGE_BUFFER_START 0x90D4
+#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5
+#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6
+#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA
+#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB
+#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC
+#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD
+#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE
+#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF
+#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
+#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39
+#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA
+#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E
+#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F
+#define GL_VERTEX_ATTRIB_BINDING 0x82D4
+#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5
+#define GL_VERTEX_BINDING_DIVISOR 0x82D6
+#define GL_VERTEX_BINDING_OFFSET 0x82D7
+#define GL_VERTEX_BINDING_STRIDE 0x82D8
+#define GL_VERTEX_BINDING_BUFFER 0x8F4F
+#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9
+#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA
+#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5
+#ifndef GL_VERSION_1_0
+#define GL_VERSION_1_0 1
+GLAPI int GLAD_GL_VERSION_1_0;
+typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode);
+GLAPI PFNGLCULLFACEPROC glad_glCullFace;
+#define glCullFace glad_glCullFace
+typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode);
+GLAPI PFNGLFRONTFACEPROC glad_glFrontFace;
+#define glFrontFace glad_glFrontFace
+typedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode);
+GLAPI PFNGLHINTPROC glad_glHint;
+#define glHint glad_glHint
+typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width);
+GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth;
+#define glLineWidth glad_glLineWidth
+typedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat size);
+GLAPI PFNGLPOINTSIZEPROC glad_glPointSize;
+#define glPointSize glad_glPointSize
+typedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode);
+GLAPI PFNGLPOLYGONMODEPROC glad_glPolygonMode;
+#define glPolygonMode glad_glPolygonMode
+typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLSCISSORPROC glad_glScissor;
+#define glScissor glad_glScissor
+typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param);
+GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf;
+#define glTexParameterf glad_glTexParameterf
+typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params);
+GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;
+#define glTexParameterfv glad_glTexParameterfv
+typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);
+GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri;
+#define glTexParameteri glad_glTexParameteri
+typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params);
+GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;
+#define glTexParameteriv glad_glTexParameteriv
+typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D;
+#define glTexImage1D glad_glTexImage1D
+typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D;
+#define glTexImage2D glad_glTexImage2D
+typedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum buf);
+GLAPI PFNGLDRAWBUFFERPROC glad_glDrawBuffer;
+#define glDrawBuffer glad_glDrawBuffer
+typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask);
+GLAPI PFNGLCLEARPROC glad_glClear;
+#define glClear glad_glClear
+typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+GLAPI PFNGLCLEARCOLORPROC glad_glClearColor;
+#define glClearColor glad_glClearColor
+typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s);
+GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil;
+#define glClearStencil glad_glClearStencil
+typedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble depth);
+GLAPI PFNGLCLEARDEPTHPROC glad_glClearDepth;
+#define glClearDepth glad_glClearDepth
+typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask);
+GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask;
+#define glStencilMask glad_glStencilMask
+typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+GLAPI PFNGLCOLORMASKPROC glad_glColorMask;
+#define glColorMask glad_glColorMask
+typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag);
+GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask;
+#define glDepthMask glad_glDepthMask
+typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap);
+GLAPI PFNGLDISABLEPROC glad_glDisable;
+#define glDisable glad_glDisable
+typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap);
+GLAPI PFNGLENABLEPROC glad_glEnable;
+#define glEnable glad_glEnable
+typedef void (APIENTRYP PFNGLFINISHPROC)(void);
+GLAPI PFNGLFINISHPROC glad_glFinish;
+#define glFinish glad_glFinish
+typedef void (APIENTRYP PFNGLFLUSHPROC)(void);
+GLAPI PFNGLFLUSHPROC glad_glFlush;
+#define glFlush glad_glFlush
+typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor);
+GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc;
+#define glBlendFunc glad_glBlendFunc
+typedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum opcode);
+GLAPI PFNGLLOGICOPPROC glad_glLogicOp;
+#define glLogicOp glad_glLogicOp
+typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask);
+GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc;
+#define glStencilFunc glad_glStencilFunc
+typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass);
+GLAPI PFNGLSTENCILOPPROC glad_glStencilOp;
+#define glStencilOp glad_glStencilOp
+typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func);
+GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc;
+#define glDepthFunc glad_glDepthFunc
+typedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param);
+GLAPI PFNGLPIXELSTOREFPROC glad_glPixelStoref;
+#define glPixelStoref glad_glPixelStoref
+typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param);
+GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei;
+#define glPixelStorei glad_glPixelStorei
+typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src);
+GLAPI PFNGLREADBUFFERPROC glad_glReadBuffer;
+#define glReadBuffer glad_glReadBuffer
+typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
+GLAPI PFNGLREADPIXELSPROC glad_glReadPixels;
+#define glReadPixels glad_glReadPixels
+typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data);
+GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv;
+#define glGetBooleanv glad_glGetBooleanv
+typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data);
+GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev;
+#define glGetDoublev glad_glGetDoublev
+typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void);
+GLAPI PFNGLGETERRORPROC glad_glGetError;
+#define glGetError glad_glGetError
+typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data);
+GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv;
+#define glGetFloatv glad_glGetFloatv
+typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data);
+GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv;
+#define glGetIntegerv glad_glGetIntegerv
+typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name);
+GLAPI PFNGLGETSTRINGPROC glad_glGetString;
+#define glGetString glad_glGetString
+typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels);
+GLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage;
+#define glGetTexImage glad_glGetTexImage
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params);
+GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;
+#define glGetTexParameterfv glad_glGetTexParameterfv
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;
+#define glGetTexParameteriv glad_glGetTexParameteriv
+typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params);
+GLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv;
+#define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv
+typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params);
+GLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;
+#define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv
+typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap);
+GLAPI PFNGLISENABLEDPROC glad_glIsEnabled;
+#define glIsEnabled glad_glIsEnabled
+typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f);
+GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange;
+#define glDepthRange glad_glDepthRange
+typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLVIEWPORTPROC glad_glViewport;
+#define glViewport glad_glViewport
+#endif
+#ifndef GL_VERSION_1_1
+#define GL_VERSION_1_1 1
+GLAPI int GLAD_GL_VERSION_1_1;
+typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count);
+GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays;
+#define glDrawArrays glad_glDrawArrays
+typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices);
+GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements;
+#define glDrawElements glad_glDrawElements
+typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units);
+GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;
+#define glPolygonOffset glad_glPolygonOffset
+typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);
+GLAPI PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D;
+#define glCopyTexImage1D glad_glCopyTexImage1D
+typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+GLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;
+#define glCopyTexImage2D glad_glCopyTexImage2D
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
+GLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D;
+#define glCopyTexSubImage1D glad_glCopyTexSubImage1D
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;
+#define glCopyTexSubImage2D glad_glCopyTexSubImage2D
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D;
+#define glTexSubImage1D glad_glTexSubImage1D
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;
+#define glTexSubImage2D glad_glTexSubImage2D
+typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture);
+GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture;
+#define glBindTexture glad_glBindTexture
+typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures);
+GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures;
+#define glDeleteTextures glad_glDeleteTextures
+typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures);
+GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures;
+#define glGenTextures glad_glGenTextures
+typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture);
+GLAPI PFNGLISTEXTUREPROC glad_glIsTexture;
+#define glIsTexture glad_glIsTexture
+#endif
+#ifndef GL_VERSION_1_2
+#define GL_VERSION_1_2 1
+GLAPI int GLAD_GL_VERSION_1_2;
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);
+GLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements;
+#define glDrawRangeElements glad_glDrawRangeElements
+typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D;
+#define glTexImage3D glad_glTexImage3D
+typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);
+GLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D;
+#define glTexSubImage3D glad_glTexSubImage3D
+typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D;
+#define glCopyTexSubImage3D glad_glCopyTexSubImage3D
+#endif
+#ifndef GL_VERSION_1_3
+#define GL_VERSION_1_3 1
+GLAPI int GLAD_GL_VERSION_1_3;
+typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture);
+GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture;
+#define glActiveTexture glad_glActiveTexture
+typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert);
+GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;
+#define glSampleCoverage glad_glSampleCoverage
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D;
+#define glCompressedTexImage3D glad_glCompressedTexImage3D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;
+#define glCompressedTexImage2D glad_glCompressedTexImage2D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D;
+#define glCompressedTexImage1D glad_glCompressedTexImage1D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D;
+#define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;
+#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);
+GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D;
+#define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D
+typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img);
+GLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage;
+#define glGetCompressedTexImage glad_glGetCompressedTexImage
+#endif
+#ifndef GL_VERSION_1_4
+#define GL_VERSION_1_4 1
+GLAPI int GLAD_GL_VERSION_1_4;
+typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
+GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;
+#define glBlendFuncSeparate glad_glBlendFuncSeparate
+typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount);
+GLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays;
+#define glMultiDrawArrays glad_glMultiDrawArrays
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount);
+GLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements;
+#define glMultiDrawElements glad_glMultiDrawElements
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param);
+GLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf;
+#define glPointParameterf glad_glPointParameterf
+typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params);
+GLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv;
+#define glPointParameterfv glad_glPointParameterfv
+typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param);
+GLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri;
+#define glPointParameteri glad_glPointParameteri
+typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params);
+GLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv;
+#define glPointParameteriv glad_glPointParameteriv
+typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+GLAPI PFNGLBLENDCOLORPROC glad_glBlendColor;
+#define glBlendColor glad_glBlendColor
+typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode);
+GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation;
+#define glBlendEquation glad_glBlendEquation
+#endif
+#ifndef GL_VERSION_1_5
+#define GL_VERSION_1_5 1
+GLAPI int GLAD_GL_VERSION_1_5;
+typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids);
+GLAPI PFNGLGENQUERIESPROC glad_glGenQueries;
+#define glGenQueries glad_glGenQueries
+typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids);
+GLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries;
+#define glDeleteQueries glad_glDeleteQueries
+typedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint id);
+GLAPI PFNGLISQUERYPROC glad_glIsQuery;
+#define glIsQuery glad_glIsQuery
+typedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum target, GLuint id);
+GLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery;
+#define glBeginQuery glad_glBeginQuery
+typedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum target);
+GLAPI PFNGLENDQUERYPROC glad_glEndQuery;
+#define glEndQuery glad_glEndQuery
+typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv;
+#define glGetQueryiv glad_glGetQueryiv
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params);
+GLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv;
+#define glGetQueryObjectiv glad_glGetQueryObjectiv
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params);
+GLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv;
+#define glGetQueryObjectuiv glad_glGetQueryObjectuiv
+typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
+GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer;
+#define glBindBuffer glad_glBindBuffer
+typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);
+GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;
+#define glDeleteBuffers glad_glDeleteBuffers
+typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers);
+GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers;
+#define glGenBuffers glad_glGenBuffers
+typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer);
+GLAPI PFNGLISBUFFERPROC glad_glIsBuffer;
+#define glIsBuffer glad_glIsBuffer
+typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
+GLAPI PFNGLBUFFERDATAPROC glad_glBufferData;
+#define glBufferData glad_glBufferData
+typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
+GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;
+#define glBufferSubData glad_glBufferSubData
+typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data);
+GLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData;
+#define glGetBufferSubData glad_glGetBufferSubData
+typedef void * (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access);
+GLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer;
+#define glMapBuffer glad_glMapBuffer
+typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target);
+GLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer;
+#define glUnmapBuffer glad_glUnmapBuffer
+typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;
+#define glGetBufferParameteriv glad_glGetBufferParameteriv
+typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params);
+GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;
+#define glGetBufferPointerv glad_glGetBufferPointerv
+#endif
+#ifndef GL_VERSION_2_0
+#define GL_VERSION_2_0 1
+GLAPI int GLAD_GL_VERSION_2_0;
+typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha);
+GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;
+#define glBlendEquationSeparate glad_glBlendEquationSeparate
+typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs);
+GLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers;
+#define glDrawBuffers glad_glDrawBuffers
+typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
+GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;
+#define glStencilOpSeparate glad_glStencilOpSeparate
+typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask);
+GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;
+#define glStencilFuncSeparate glad_glStencilFuncSeparate
+typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask);
+GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;
+#define glStencilMaskSeparate glad_glStencilMaskSeparate
+typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader);
+GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader;
+#define glAttachShader glad_glAttachShader
+typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name);
+GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;
+#define glBindAttribLocation glad_glBindAttribLocation
+typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader);
+GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;
+#define glCompileShader glad_glCompileShader
+typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void);
+GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;
+#define glCreateProgram glad_glCreateProgram
+typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type);
+GLAPI PFNGLCREATESHADERPROC glad_glCreateShader;
+#define glCreateShader glad_glCreateShader
+typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program);
+GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;
+#define glDeleteProgram glad_glDeleteProgram
+typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader);
+GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader;
+#define glDeleteShader glad_glDeleteShader
+typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader);
+GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader;
+#define glDetachShader glad_glDetachShader
+typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index);
+GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;
+#define glDisableVertexAttribArray glad_glDisableVertexAttribArray
+typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index);
+GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;
+#define glEnableVertexAttribArray glad_glEnableVertexAttribArray
+typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
+GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;
+#define glGetActiveAttrib glad_glGetActiveAttrib
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name);
+GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;
+#define glGetActiveUniform glad_glGetActiveUniform
+typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders);
+GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;
+#define glGetAttachedShaders glad_glGetAttachedShaders
+typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name);
+GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;
+#define glGetAttribLocation glad_glGetAttribLocation
+typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params);
+GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;
+#define glGetProgramiv glad_glGetProgramiv
+typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;
+#define glGetProgramInfoLog glad_glGetProgramInfoLog
+typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params);
+GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv;
+#define glGetShaderiv glad_glGetShaderiv
+typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;
+#define glGetShaderInfoLog glad_glGetShaderInfoLog
+typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source);
+GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;
+#define glGetShaderSource glad_glGetShaderSource
+typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name);
+GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;
+#define glGetUniformLocation glad_glGetUniformLocation
+typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params);
+GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;
+#define glGetUniformfv glad_glGetUniformfv
+typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params);
+GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;
+#define glGetUniformiv glad_glGetUniformiv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params);
+GLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv;
+#define glGetVertexAttribdv glad_glGetVertexAttribdv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params);
+GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;
+#define glGetVertexAttribfv glad_glGetVertexAttribfv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params);
+GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;
+#define glGetVertexAttribiv glad_glGetVertexAttribiv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer);
+GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;
+#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv
+typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program);
+GLAPI PFNGLISPROGRAMPROC glad_glIsProgram;
+#define glIsProgram glad_glIsProgram
+typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader);
+GLAPI PFNGLISSHADERPROC glad_glIsShader;
+#define glIsShader glad_glIsShader
+typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program);
+GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram;
+#define glLinkProgram glad_glLinkProgram
+typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
+GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource;
+#define glShaderSource glad_glShaderSource
+typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program);
+GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram;
+#define glUseProgram glad_glUseProgram
+typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0);
+GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f;
+#define glUniform1f glad_glUniform1f
+typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1);
+GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f;
+#define glUniform2f glad_glUniform2f
+typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f;
+#define glUniform3f glad_glUniform3f
+typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+GLAPI PFNGLUNIFORM4FPROC glad_glUniform4f;
+#define glUniform4f glad_glUniform4f
+typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0);
+GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i;
+#define glUniform1i glad_glUniform1i
+typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1);
+GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i;
+#define glUniform2i glad_glUniform2i
+typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2);
+GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i;
+#define glUniform3i glad_glUniform3i
+typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
+GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i;
+#define glUniform4i glad_glUniform4i
+typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv;
+#define glUniform1fv glad_glUniform1fv
+typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv;
+#define glUniform2fv glad_glUniform2fv
+typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv;
+#define glUniform3fv glad_glUniform3fv
+typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv;
+#define glUniform4fv glad_glUniform4fv
+typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv;
+#define glUniform1iv glad_glUniform1iv
+typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv;
+#define glUniform2iv glad_glUniform2iv
+typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv;
+#define glUniform3iv glad_glUniform3iv
+typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv;
+#define glUniform4iv glad_glUniform4iv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;
+#define glUniformMatrix2fv glad_glUniformMatrix2fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;
+#define glUniformMatrix3fv glad_glUniformMatrix3fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;
+#define glUniformMatrix4fv glad_glUniformMatrix4fv
+typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program);
+GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;
+#define glValidateProgram glad_glValidateProgram
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x);
+GLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d;
+#define glVertexAttrib1d glad_glVertexAttrib1d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v);
+GLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv;
+#define glVertexAttrib1dv glad_glVertexAttrib1dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x);
+GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;
+#define glVertexAttrib1f glad_glVertexAttrib1f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;
+#define glVertexAttrib1fv glad_glVertexAttrib1fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x);
+GLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s;
+#define glVertexAttrib1s glad_glVertexAttrib1s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v);
+GLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv;
+#define glVertexAttrib1sv glad_glVertexAttrib1sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y);
+GLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d;
+#define glVertexAttrib2d glad_glVertexAttrib2d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v);
+GLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv;
+#define glVertexAttrib2dv glad_glVertexAttrib2dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y);
+GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;
+#define glVertexAttrib2f glad_glVertexAttrib2f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;
+#define glVertexAttrib2fv glad_glVertexAttrib2fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y);
+GLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s;
+#define glVertexAttrib2s glad_glVertexAttrib2s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v);
+GLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv;
+#define glVertexAttrib2sv glad_glVertexAttrib2sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z);
+GLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d;
+#define glVertexAttrib3d glad_glVertexAttrib3d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v);
+GLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv;
+#define glVertexAttrib3dv glad_glVertexAttrib3dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z);
+GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;
+#define glVertexAttrib3f glad_glVertexAttrib3f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;
+#define glVertexAttrib3fv glad_glVertexAttrib3fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z);
+GLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s;
+#define glVertexAttrib3s glad_glVertexAttrib3s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v);
+GLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv;
+#define glVertexAttrib3sv glad_glVertexAttrib3sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v);
+GLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv;
+#define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v);
+GLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv;
+#define glVertexAttrib4Niv glad_glVertexAttrib4Niv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v);
+GLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv;
+#define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w);
+GLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub;
+#define glVertexAttrib4Nub glad_glVertexAttrib4Nub
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v);
+GLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv;
+#define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v);
+GLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv;
+#define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v);
+GLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv;
+#define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v);
+GLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv;
+#define glVertexAttrib4bv glad_glVertexAttrib4bv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w);
+GLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d;
+#define glVertexAttrib4d glad_glVertexAttrib4d
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v);
+GLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv;
+#define glVertexAttrib4dv glad_glVertexAttrib4dv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;
+#define glVertexAttrib4f glad_glVertexAttrib4f
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v);
+GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;
+#define glVertexAttrib4fv glad_glVertexAttrib4fv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v);
+GLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv;
+#define glVertexAttrib4iv glad_glVertexAttrib4iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w);
+GLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s;
+#define glVertexAttrib4s glad_glVertexAttrib4s
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v);
+GLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv;
+#define glVertexAttrib4sv glad_glVertexAttrib4sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v);
+GLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv;
+#define glVertexAttrib4ubv glad_glVertexAttrib4ubv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v);
+GLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv;
+#define glVertexAttrib4uiv glad_glVertexAttrib4uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v);
+GLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv;
+#define glVertexAttrib4usv glad_glVertexAttrib4usv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
+GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;
+#define glVertexAttribPointer glad_glVertexAttribPointer
+#endif
+#ifndef GL_VERSION_2_1
+#define GL_VERSION_2_1 1
+GLAPI int GLAD_GL_VERSION_2_1;
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv;
+#define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv;
+#define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv;
+#define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv;
+#define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv;
+#define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv
+typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv;
+#define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv
+#endif
+#ifndef GL_VERSION_3_0
+#define GL_VERSION_3_0 1
+GLAPI int GLAD_GL_VERSION_3_0;
+typedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a);
+GLAPI PFNGLCOLORMASKIPROC glad_glColorMaski;
+#define glColorMaski glad_glColorMaski
+typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data);
+GLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v;
+#define glGetBooleani_v glad_glGetBooleani_v
+typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data);
+GLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v;
+#define glGetIntegeri_v glad_glGetIntegeri_v
+typedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum target, GLuint index);
+GLAPI PFNGLENABLEIPROC glad_glEnablei;
+#define glEnablei glad_glEnablei
+typedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum target, GLuint index);
+GLAPI PFNGLDISABLEIPROC glad_glDisablei;
+#define glDisablei glad_glDisablei
+typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum target, GLuint index);
+GLAPI PFNGLISENABLEDIPROC glad_glIsEnabledi;
+#define glIsEnabledi glad_glIsEnabledi
+typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode);
+GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;
+#define glBeginTransformFeedback glad_glBeginTransformFeedback
+typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void);
+GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;
+#define glEndTransformFeedback glad_glEndTransformFeedback
+typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
+GLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange;
+#define glBindBufferRange glad_glBindBufferRange
+typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer);
+GLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase;
+#define glBindBufferBase glad_glBindBufferBase
+typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode);
+GLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings;
+#define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings
+typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name);
+GLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying;
+#define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying
+typedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp);
+GLAPI PFNGLCLAMPCOLORPROC glad_glClampColor;
+#define glClampColor glad_glClampColor
+typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode);
+GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;
+#define glBeginConditionalRender glad_glBeginConditionalRender
+typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(void);
+GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;
+#define glEndConditionalRender glad_glEndConditionalRender
+typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);
+GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer;
+#define glVertexAttribIPointer glad_glVertexAttribIPointer
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params);
+GLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv;
+#define glGetVertexAttribIiv glad_glGetVertexAttribIiv
+typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params);
+GLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv;
+#define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x);
+GLAPI PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i;
+#define glVertexAttribI1i glad_glVertexAttribI1i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y);
+GLAPI PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i;
+#define glVertexAttribI2i glad_glVertexAttribI2i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z);
+GLAPI PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i;
+#define glVertexAttribI3i glad_glVertexAttribI3i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w);
+GLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i;
+#define glVertexAttribI4i glad_glVertexAttribI4i
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x);
+GLAPI PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui;
+#define glVertexAttribI1ui glad_glVertexAttribI1ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y);
+GLAPI PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui;
+#define glVertexAttribI2ui glad_glVertexAttribI2ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z);
+GLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui;
+#define glVertexAttribI3ui glad_glVertexAttribI3ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
+GLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui;
+#define glVertexAttribI4ui glad_glVertexAttribI4ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v);
+GLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv;
+#define glVertexAttribI1iv glad_glVertexAttribI1iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v);
+GLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv;
+#define glVertexAttribI2iv glad_glVertexAttribI2iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v);
+GLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv;
+#define glVertexAttribI3iv glad_glVertexAttribI3iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v);
+GLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv;
+#define glVertexAttribI4iv glad_glVertexAttribI4iv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v);
+GLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv;
+#define glVertexAttribI1uiv glad_glVertexAttribI1uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v);
+GLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv;
+#define glVertexAttribI2uiv glad_glVertexAttribI2uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v);
+GLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv;
+#define glVertexAttribI3uiv glad_glVertexAttribI3uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v);
+GLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv;
+#define glVertexAttribI4uiv glad_glVertexAttribI4uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v);
+GLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv;
+#define glVertexAttribI4bv glad_glVertexAttribI4bv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v);
+GLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv;
+#define glVertexAttribI4sv glad_glVertexAttribI4sv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v);
+GLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv;
+#define glVertexAttribI4ubv glad_glVertexAttribI4ubv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v);
+GLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv;
+#define glVertexAttribI4usv glad_glVertexAttribI4usv
+typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params);
+GLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv;
+#define glGetUniformuiv glad_glGetUniformuiv
+typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name);
+GLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation;
+#define glBindFragDataLocation glad_glBindFragDataLocation
+typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name);
+GLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation;
+#define glGetFragDataLocation glad_glGetFragDataLocation
+typedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0);
+GLAPI PFNGLUNIFORM1UIPROC glad_glUniform1ui;
+#define glUniform1ui glad_glUniform1ui
+typedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1);
+GLAPI PFNGLUNIFORM2UIPROC glad_glUniform2ui;
+#define glUniform2ui glad_glUniform2ui
+typedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2);
+GLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui;
+#define glUniform3ui glad_glUniform3ui
+typedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
+GLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui;
+#define glUniform4ui glad_glUniform4ui
+typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv;
+#define glUniform1uiv glad_glUniform1uiv
+typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv;
+#define glUniform2uiv glad_glUniform2uiv
+typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv;
+#define glUniform3uiv glad_glUniform3uiv
+typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv;
+#define glUniform4uiv glad_glUniform4uiv
+typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params);
+GLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv;
+#define glTexParameterIiv glad_glTexParameterIiv
+typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params);
+GLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv;
+#define glTexParameterIuiv glad_glTexParameterIuiv
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv;
+#define glGetTexParameterIiv glad_glGetTexParameterIiv
+typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params);
+GLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv;
+#define glGetTexParameterIuiv glad_glGetTexParameterIuiv
+typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value);
+GLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv;
+#define glClearBufferiv glad_glClearBufferiv
+typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value);
+GLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv;
+#define glClearBufferuiv glad_glClearBufferuiv
+typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value);
+GLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv;
+#define glClearBufferfv glad_glClearBufferfv
+typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
+GLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi;
+#define glClearBufferfi glad_glClearBufferfi
+typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index);
+GLAPI PFNGLGETSTRINGIPROC glad_glGetStringi;
+#define glGetStringi glad_glGetStringi
+typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer);
+GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;
+#define glIsRenderbuffer glad_glIsRenderbuffer
+typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer);
+GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;
+#define glBindRenderbuffer glad_glBindRenderbuffer
+typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers);
+GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;
+#define glDeleteRenderbuffers glad_glDeleteRenderbuffers
+typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers);
+GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;
+#define glGenRenderbuffers glad_glGenRenderbuffers
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;
+#define glRenderbufferStorage glad_glRenderbufferStorage
+typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;
+#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv
+typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer);
+GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;
+#define glIsFramebuffer glad_glIsFramebuffer
+typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);
+GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;
+#define glBindFramebuffer glad_glBindFramebuffer
+typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers);
+GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;
+#define glDeleteFramebuffers glad_glDeleteFramebuffers
+typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers);
+GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;
+#define glGenFramebuffers glad_glGenFramebuffers
+typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target);
+GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;
+#define glCheckFramebufferStatus glad_glCheckFramebufferStatus
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+GLAPI PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D;
+#define glFramebufferTexture1D glad_glFramebufferTexture1D
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+GLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;
+#define glFramebufferTexture2D glad_glFramebufferTexture2D
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
+GLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D;
+#define glFramebufferTexture3D glad_glFramebufferTexture3D
+typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;
+#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer
+typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params);
+GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;
+#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv
+typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target);
+GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;
+#define glGenerateMipmap glad_glGenerateMipmap
+typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
+GLAPI PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;
+#define glBlitFramebuffer glad_glBlitFramebuffer
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+GLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;
+#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
+GLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer;
+#define glFramebufferTextureLayer glad_glFramebufferTextureLayer
+typedef void * (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
+GLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange;
+#define glMapBufferRange glad_glMapBufferRange
+typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length);
+GLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange;
+#define glFlushMappedBufferRange glad_glFlushMappedBufferRange
+typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array);
+GLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray;
+#define glBindVertexArray glad_glBindVertexArray
+typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays);
+GLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays;
+#define glDeleteVertexArrays glad_glDeleteVertexArrays
+typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays);
+GLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays;
+#define glGenVertexArrays glad_glGenVertexArrays
+typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint array);
+GLAPI PFNGLISVERTEXARRAYPROC glad_glIsVertexArray;
+#define glIsVertexArray glad_glIsVertexArray
+#endif
+#ifndef GL_VERSION_3_1
+#define GL_VERSION_3_1 1
+GLAPI int GLAD_GL_VERSION_3_1;
+typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
+GLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced;
+#define glDrawArraysInstanced glad_glDrawArraysInstanced
+typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);
+GLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced;
+#define glDrawElementsInstanced glad_glDrawElementsInstanced
+typedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer);
+GLAPI PFNGLTEXBUFFERPROC glad_glTexBuffer;
+#define glTexBuffer glad_glTexBuffer
+typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index);
+GLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;
+#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex
+typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
+GLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData;
+#define glCopyBufferSubData glad_glCopyBufferSubData
+typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices);
+GLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices;
+#define glGetUniformIndices glad_glGetUniformIndices
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params);
+GLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv;
+#define glGetActiveUniformsiv glad_glGetActiveUniformsiv
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName);
+GLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName;
+#define glGetActiveUniformName glad_glGetActiveUniformName
+typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName);
+GLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex;
+#define glGetUniformBlockIndex glad_glGetUniformBlockIndex
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params);
+GLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv;
+#define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv
+typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName);
+GLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName;
+#define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName
+typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
+GLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding;
+#define glUniformBlockBinding glad_glUniformBlockBinding
+#endif
+#ifndef GL_VERSION_3_2
+#define GL_VERSION_3_2 1
+GLAPI int GLAD_GL_VERSION_3_2;
+typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
+GLAPI PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex;
+#define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex
+typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex);
+GLAPI PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex;
+#define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex
+typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex);
+GLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex;
+#define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex
+typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex);
+GLAPI PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex;
+#define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex
+typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum mode);
+GLAPI PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex;
+#define glProvokingVertex glad_glProvokingVertex
+typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags);
+GLAPI PFNGLFENCESYNCPROC glad_glFenceSync;
+#define glFenceSync glad_glFenceSync
+typedef GLboolean (APIENTRYP PFNGLISSYNCPROC)(GLsync sync);
+GLAPI PFNGLISSYNCPROC glad_glIsSync;
+#define glIsSync glad_glIsSync
+typedef void (APIENTRYP PFNGLDELETESYNCPROC)(GLsync sync);
+GLAPI PFNGLDELETESYNCPROC glad_glDeleteSync;
+#define glDeleteSync glad_glDeleteSync
+typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout);
+GLAPI PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync;
+#define glClientWaitSync glad_glClientWaitSync
+typedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout);
+GLAPI PFNGLWAITSYNCPROC glad_glWaitSync;
+#define glWaitSync glad_glWaitSync
+typedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 *data);
+GLAPI PFNGLGETINTEGER64VPROC glad_glGetInteger64v;
+#define glGetInteger64v glad_glGetInteger64v
+typedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values);
+GLAPI PFNGLGETSYNCIVPROC glad_glGetSynciv;
+#define glGetSynciv glad_glGetSynciv
+typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 *data);
+GLAPI PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v;
+#define glGetInteger64i_v glad_glGetInteger64i_v
+typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 *params);
+GLAPI PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v;
+#define glGetBufferParameteri64v glad_glGetBufferParameteri64v
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level);
+GLAPI PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture;
+#define glFramebufferTexture glad_glFramebufferTexture
+typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);
+GLAPI PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample;
+#define glTexImage2DMultisample glad_glTexImage2DMultisample
+typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations);
+GLAPI PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample;
+#define glTexImage3DMultisample glad_glTexImage3DMultisample
+typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat *val);
+GLAPI PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv;
+#define glGetMultisamplefv glad_glGetMultisamplefv
+typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask);
+GLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski;
+#define glSampleMaski glad_glSampleMaski
+#endif
+#ifndef GL_VERSION_3_3
+#define GL_VERSION_3_3 1
+GLAPI int GLAD_GL_VERSION_3_3;
+typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name);
+GLAPI PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed;
+#define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed
+typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar *name);
+GLAPI PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex;
+#define glGetFragDataIndex glad_glGetFragDataIndex
+typedef void (APIENTRYP PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint *samplers);
+GLAPI PFNGLGENSAMPLERSPROC glad_glGenSamplers;
+#define glGenSamplers glad_glGenSamplers
+typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint *samplers);
+GLAPI PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers;
+#define glDeleteSamplers glad_glDeleteSamplers
+typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint sampler);
+GLAPI PFNGLISSAMPLERPROC glad_glIsSampler;
+#define glIsSampler glad_glIsSampler
+typedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler);
+GLAPI PFNGLBINDSAMPLERPROC glad_glBindSampler;
+#define glBindSampler glad_glBindSampler
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param);
+GLAPI PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri;
+#define glSamplerParameteri glad_glSamplerParameteri
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint *param);
+GLAPI PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv;
+#define glSamplerParameteriv glad_glSamplerParameteriv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param);
+GLAPI PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf;
+#define glSamplerParameterf glad_glSamplerParameterf
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat *param);
+GLAPI PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv;
+#define glSamplerParameterfv glad_glSamplerParameterfv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint *param);
+GLAPI PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv;
+#define glSamplerParameterIiv glad_glSamplerParameterIiv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint *param);
+GLAPI PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv;
+#define glSamplerParameterIuiv glad_glSamplerParameterIuiv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint *params);
+GLAPI PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv;
+#define glGetSamplerParameteriv glad_glGetSamplerParameteriv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint *params);
+GLAPI PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv;
+#define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat *params);
+GLAPI PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv;
+#define glGetSamplerParameterfv glad_glGetSamplerParameterfv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint *params);
+GLAPI PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv;
+#define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv
+typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target);
+GLAPI PFNGLQUERYCOUNTERPROC glad_glQueryCounter;
+#define glQueryCounter glad_glQueryCounter
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 *params);
+GLAPI PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v;
+#define glGetQueryObjecti64v glad_glGetQueryObjecti64v
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 *params);
+GLAPI PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v;
+#define glGetQueryObjectui64v glad_glGetQueryObjectui64v
+typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor);
+GLAPI PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor;
+#define glVertexAttribDivisor glad_glVertexAttribDivisor
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui;
+#define glVertexAttribP1ui glad_glVertexAttribP1ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv;
+#define glVertexAttribP1uiv glad_glVertexAttribP1uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui;
+#define glVertexAttribP2ui glad_glVertexAttribP2ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv;
+#define glVertexAttribP2uiv glad_glVertexAttribP2uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui;
+#define glVertexAttribP3ui glad_glVertexAttribP3ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv;
+#define glVertexAttribP3uiv glad_glVertexAttribP3uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui;
+#define glVertexAttribP4ui glad_glVertexAttribP4ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv;
+#define glVertexAttribP4uiv glad_glVertexAttribP4uiv
+typedef void (APIENTRYP PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value);
+GLAPI PFNGLVERTEXP2UIPROC glad_glVertexP2ui;
+#define glVertexP2ui glad_glVertexP2ui
+typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint *value);
+GLAPI PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv;
+#define glVertexP2uiv glad_glVertexP2uiv
+typedef void (APIENTRYP PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value);
+GLAPI PFNGLVERTEXP3UIPROC glad_glVertexP3ui;
+#define glVertexP3ui glad_glVertexP3ui
+typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint *value);
+GLAPI PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv;
+#define glVertexP3uiv glad_glVertexP3uiv
+typedef void (APIENTRYP PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value);
+GLAPI PFNGLVERTEXP4UIPROC glad_glVertexP4ui;
+#define glVertexP4ui glad_glVertexP4ui
+typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint *value);
+GLAPI PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv;
+#define glVertexP4uiv glad_glVertexP4uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui;
+#define glTexCoordP1ui glad_glTexCoordP1ui
+typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv;
+#define glTexCoordP1uiv glad_glTexCoordP1uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui;
+#define glTexCoordP2ui glad_glTexCoordP2ui
+typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv;
+#define glTexCoordP2uiv glad_glTexCoordP2uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui;
+#define glTexCoordP3ui glad_glTexCoordP3ui
+typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv;
+#define glTexCoordP3uiv glad_glTexCoordP3uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui;
+#define glTexCoordP4ui glad_glTexCoordP4ui
+typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv;
+#define glTexCoordP4uiv glad_glTexCoordP4uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui;
+#define glMultiTexCoordP1ui glad_glMultiTexCoordP1ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv;
+#define glMultiTexCoordP1uiv glad_glMultiTexCoordP1uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui;
+#define glMultiTexCoordP2ui glad_glMultiTexCoordP2ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv;
+#define glMultiTexCoordP2uiv glad_glMultiTexCoordP2uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui;
+#define glMultiTexCoordP3ui glad_glMultiTexCoordP3ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv;
+#define glMultiTexCoordP3uiv glad_glMultiTexCoordP3uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;
+#define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;
+#define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv
+typedef void (APIENTRYP PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLNORMALP3UIPROC glad_glNormalP3ui;
+#define glNormalP3ui glad_glNormalP3ui
+typedef void (APIENTRYP PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLNORMALP3UIVPROC glad_glNormalP3uiv;
+#define glNormalP3uiv glad_glNormalP3uiv
+typedef void (APIENTRYP PFNGLCOLORP3UIPROC)(GLenum type, GLuint color);
+GLAPI PFNGLCOLORP3UIPROC glad_glColorP3ui;
+#define glColorP3ui glad_glColorP3ui
+typedef void (APIENTRYP PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint *color);
+GLAPI PFNGLCOLORP3UIVPROC glad_glColorP3uiv;
+#define glColorP3uiv glad_glColorP3uiv
+typedef void (APIENTRYP PFNGLCOLORP4UIPROC)(GLenum type, GLuint color);
+GLAPI PFNGLCOLORP4UIPROC glad_glColorP4ui;
+#define glColorP4ui glad_glColorP4ui
+typedef void (APIENTRYP PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint *color);
+GLAPI PFNGLCOLORP4UIVPROC glad_glColorP4uiv;
+#define glColorP4uiv glad_glColorP4uiv
+typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color);
+GLAPI PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui;
+#define glSecondaryColorP3ui glad_glSecondaryColorP3ui
+typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint *color);
+GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;
+#define glSecondaryColorP3uiv glad_glSecondaryColorP3uiv
+#endif
+#ifndef GL_ES_VERSION_2_0
+#define GL_ES_VERSION_2_0 1
+GLAPI int GLAD_GL_ES_VERSION_2_0;
+typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat d);
+GLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf;
+#define glClearDepthf glad_glClearDepthf
+typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f);
+GLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef;
+#define glDepthRangef glad_glDepthRangef
+typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision);
+GLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;
+#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat
+typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)(void);
+GLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;
+#define glReleaseShaderCompiler glad_glReleaseShaderCompiler
+typedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length);
+GLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary;
+#define glShaderBinary glad_glShaderBinary
+#endif
+#ifndef GL_ES_VERSION_3_0
+#define GL_ES_VERSION_3_0 1
+GLAPI int GLAD_GL_ES_VERSION_3_0;
+typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum target, GLuint id);
+GLAPI PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback;
+#define glBindTransformFeedback glad_glBindTransformFeedback
+typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei n, const GLuint *ids);
+GLAPI PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks;
+#define glDeleteTransformFeedbacks glad_glDeleteTransformFeedbacks
+typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids);
+GLAPI PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks;
+#define glGenTransformFeedbacks glad_glGenTransformFeedbacks
+typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC)(GLuint id);
+GLAPI PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback;
+#define glIsTransformFeedback glad_glIsTransformFeedback
+typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC)(void);
+GLAPI PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback;
+#define glPauseTransformFeedback glad_glPauseTransformFeedback
+typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC)(void);
+GLAPI PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback;
+#define glResumeTransformFeedback glad_glResumeTransformFeedback
+typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);
+GLAPI PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary;
+#define glGetProgramBinary glad_glGetProgramBinary
+typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length);
+GLAPI PFNGLPROGRAMBINARYPROC glad_glProgramBinary;
+#define glProgramBinary glad_glProgramBinary
+typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value);
+GLAPI PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri;
+#define glProgramParameteri glad_glProgramParameteri
+typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments);
+GLAPI PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer;
+#define glInvalidateFramebuffer glad_glInvalidateFramebuffer
+typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height);
+GLAPI PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer;
+#define glInvalidateSubFramebuffer glad_glInvalidateSubFramebuffer
+typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
+GLAPI PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D;
+#define glTexStorage2D glad_glTexStorage2D
+typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
+GLAPI PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D;
+#define glTexStorage3D glad_glTexStorage3D
+typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params);
+GLAPI PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ;
+#define glGetInternalformativ glad_glGetInternalformativ
+#endif
+#ifndef GL_ES_VERSION_3_1
+#define GL_ES_VERSION_3_1 1
+GLAPI int GLAD_GL_ES_VERSION_3_1;
+typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);
+GLAPI PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute;
+#define glDispatchCompute glad_glDispatchCompute
+typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC)(GLintptr indirect);
+GLAPI PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect;
+#define glDispatchComputeIndirect glad_glDispatchComputeIndirect
+typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void *indirect);
+GLAPI PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect;
+#define glDrawArraysIndirect glad_glDrawArraysIndirect
+typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void *indirect);
+GLAPI PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect;
+#define glDrawElementsIndirect glad_glDrawElementsIndirect
+typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC)(GLenum target, GLenum pname, GLint param);
+GLAPI PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri;
+#define glFramebufferParameteri glad_glFramebufferParameteri
+typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params);
+GLAPI PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv;
+#define glGetFramebufferParameteriv glad_glGetFramebufferParameteriv
+typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC)(GLuint program, GLenum programInterface, GLenum pname, GLint *params);
+GLAPI PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv;
+#define glGetProgramInterfaceiv glad_glGetProgramInterfaceiv
+typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC)(GLuint program, GLenum programInterface, const GLchar *name);
+GLAPI PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex;
+#define glGetProgramResourceIndex glad_glGetProgramResourceIndex
+typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name);
+GLAPI PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName;
+#define glGetProgramResourceName glad_glGetProgramResourceName
+typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params);
+GLAPI PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv;
+#define glGetProgramResourceiv glad_glGetProgramResourceiv
+typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC)(GLuint program, GLenum programInterface, const GLchar *name);
+GLAPI PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation;
+#define glGetProgramResourceLocation glad_glGetProgramResourceLocation
+typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC)(GLuint pipeline, GLbitfield stages, GLuint program);
+GLAPI PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages;
+#define glUseProgramStages glad_glUseProgramStages
+typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC)(GLuint pipeline, GLuint program);
+GLAPI PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram;
+#define glActiveShaderProgram glad_glActiveShaderProgram
+typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC)(GLenum type, GLsizei count, const GLchar *const*strings);
+GLAPI PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv;
+#define glCreateShaderProgramv glad_glCreateShaderProgramv
+typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC)(GLuint pipeline);
+GLAPI PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline;
+#define glBindProgramPipeline glad_glBindProgramPipeline
+typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC)(GLsizei n, const GLuint *pipelines);
+GLAPI PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines;
+#define glDeleteProgramPipelines glad_glDeleteProgramPipelines
+typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC)(GLsizei n, GLuint *pipelines);
+GLAPI PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines;
+#define glGenProgramPipelines glad_glGenProgramPipelines
+typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC)(GLuint pipeline);
+GLAPI PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline;
+#define glIsProgramPipeline glad_glIsProgramPipeline
+typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC)(GLuint pipeline, GLenum pname, GLint *params);
+GLAPI PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv;
+#define glGetProgramPipelineiv glad_glGetProgramPipelineiv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC)(GLuint program, GLint location, GLint v0);
+GLAPI PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i;
+#define glProgramUniform1i glad_glProgramUniform1i
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC)(GLuint program, GLint location, GLint v0, GLint v1);
+GLAPI PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i;
+#define glProgramUniform2i glad_glProgramUniform2i
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2);
+GLAPI PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i;
+#define glProgramUniform3i glad_glProgramUniform3i
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
+GLAPI PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i;
+#define glProgramUniform4i glad_glProgramUniform4i
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC)(GLuint program, GLint location, GLuint v0);
+GLAPI PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui;
+#define glProgramUniform1ui glad_glProgramUniform1ui
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1);
+GLAPI PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui;
+#define glProgramUniform2ui glad_glProgramUniform2ui
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2);
+GLAPI PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui;
+#define glProgramUniform3ui glad_glProgramUniform3ui
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
+GLAPI PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui;
+#define glProgramUniform4ui glad_glProgramUniform4ui
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC)(GLuint program, GLint location, GLfloat v0);
+GLAPI PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f;
+#define glProgramUniform1f glad_glProgramUniform1f
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1);
+GLAPI PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f;
+#define glProgramUniform2f glad_glProgramUniform2f
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
+GLAPI PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f;
+#define glProgramUniform3f glad_glProgramUniform3f
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
+GLAPI PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f;
+#define glProgramUniform4f glad_glProgramUniform4f
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv;
+#define glProgramUniform1iv glad_glProgramUniform1iv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv;
+#define glProgramUniform2iv glad_glProgramUniform2iv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv;
+#define glProgramUniform3iv glad_glProgramUniform3iv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC)(GLuint program, GLint location, GLsizei count, const GLint *value);
+GLAPI PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv;
+#define glProgramUniform4iv glad_glProgramUniform4iv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv;
+#define glProgramUniform1uiv glad_glProgramUniform1uiv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv;
+#define glProgramUniform2uiv glad_glProgramUniform2uiv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv;
+#define glProgramUniform3uiv glad_glProgramUniform3uiv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint *value);
+GLAPI PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv;
+#define glProgramUniform4uiv glad_glProgramUniform4uiv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv;
+#define glProgramUniform1fv glad_glProgramUniform1fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv;
+#define glProgramUniform2fv glad_glProgramUniform2fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv;
+#define glProgramUniform3fv glad_glProgramUniform3fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv;
+#define glProgramUniform4fv glad_glProgramUniform4fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv;
+#define glProgramUniformMatrix2fv glad_glProgramUniformMatrix2fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv;
+#define glProgramUniformMatrix3fv glad_glProgramUniformMatrix3fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv;
+#define glProgramUniformMatrix4fv glad_glProgramUniformMatrix4fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv;
+#define glProgramUniformMatrix2x3fv glad_glProgramUniformMatrix2x3fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv;
+#define glProgramUniformMatrix3x2fv glad_glProgramUniformMatrix3x2fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv;
+#define glProgramUniformMatrix2x4fv glad_glProgramUniformMatrix2x4fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv;
+#define glProgramUniformMatrix4x2fv glad_glProgramUniformMatrix4x2fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv;
+#define glProgramUniformMatrix3x4fv glad_glProgramUniformMatrix3x4fv
+typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+GLAPI PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv;
+#define glProgramUniformMatrix4x3fv glad_glProgramUniformMatrix4x3fv
+typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC)(GLuint pipeline);
+GLAPI PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline;
+#define glValidateProgramPipeline glad_glValidateProgramPipeline
+typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC)(GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+GLAPI PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog;
+#define glGetProgramPipelineInfoLog glad_glGetProgramPipelineInfoLog
+typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format);
+GLAPI PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture;
+#define glBindImageTexture glad_glBindImageTexture
+typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC)(GLbitfield barriers);
+GLAPI PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier;
+#define glMemoryBarrier glad_glMemoryBarrier
+typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers);
+GLAPI PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion;
+#define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion
+typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations);
+GLAPI PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample;
+#define glTexStorage2DMultisample glad_glTexStorage2DMultisample
+typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride);
+GLAPI PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer;
+#define glBindVertexBuffer glad_glBindVertexBuffer
+typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);
+GLAPI PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat;
+#define glVertexAttribFormat glad_glVertexAttribFormat
+typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
+GLAPI PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat;
+#define glVertexAttribIFormat glad_glVertexAttribIFormat
+typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC)(GLuint attribindex, GLuint bindingindex);
+GLAPI PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding;
+#define glVertexAttribBinding glad_glVertexAttribBinding
+typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC)(GLuint bindingindex, GLuint divisor);
+GLAPI PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor;
+#define glVertexBindingDivisor glad_glVertexBindingDivisor
+#endif
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
+#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
+#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243
+#define GL_DEBUG_CALLBACK_FUNCTION 0x8244
+#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245
+#define GL_DEBUG_SOURCE_API 0x8246
+#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247
+#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248
+#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249
+#define GL_DEBUG_SOURCE_APPLICATION 0x824A
+#define GL_DEBUG_SOURCE_OTHER 0x824B
+#define GL_DEBUG_TYPE_ERROR 0x824C
+#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
+#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
+#define GL_DEBUG_TYPE_PORTABILITY 0x824F
+#define GL_DEBUG_TYPE_PERFORMANCE 0x8250
+#define GL_DEBUG_TYPE_OTHER 0x8251
+#define GL_DEBUG_TYPE_MARKER 0x8268
+#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269
+#define GL_DEBUG_TYPE_POP_GROUP 0x826A
+#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
+#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C
+#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D
+#define GL_BUFFER 0x82E0
+#define GL_SHADER 0x82E1
+#define GL_PROGRAM 0x82E2
+#define GL_VERTEX_ARRAY 0x8074
+#define GL_QUERY 0x82E3
+#define GL_PROGRAM_PIPELINE 0x82E4
+#define GL_SAMPLER 0x82E6
+#define GL_MAX_LABEL_LENGTH 0x82E8
+#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143
+#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144
+#define GL_DEBUG_LOGGED_MESSAGES 0x9145
+#define GL_DEBUG_SEVERITY_HIGH 0x9146
+#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
+#define GL_DEBUG_SEVERITY_LOW 0x9148
+#define GL_DEBUG_OUTPUT 0x92E0
+#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
+#define GL_STACK_OVERFLOW 0x0503
+#define GL_STACK_UNDERFLOW 0x0504
+#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242
+#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243
+#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244
+#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245
+#define GL_DEBUG_SOURCE_API_KHR 0x8246
+#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247
+#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248
+#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249
+#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A
+#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B
+#define GL_DEBUG_TYPE_ERROR_KHR 0x824C
+#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D
+#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E
+#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F
+#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250
+#define GL_DEBUG_TYPE_OTHER_KHR 0x8251
+#define GL_DEBUG_TYPE_MARKER_KHR 0x8268
+#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269
+#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A
+#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B
+#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C
+#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D
+#define GL_BUFFER_KHR 0x82E0
+#define GL_SHADER_KHR 0x82E1
+#define GL_PROGRAM_KHR 0x82E2
+#define GL_VERTEX_ARRAY_KHR 0x8074
+#define GL_QUERY_KHR 0x82E3
+#define GL_PROGRAM_PIPELINE_KHR 0x82E4
+#define GL_SAMPLER_KHR 0x82E6
+#define GL_MAX_LABEL_LENGTH_KHR 0x82E8
+#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143
+#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144
+#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145
+#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146
+#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147
+#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148
+#define GL_DEBUG_OUTPUT_KHR 0x92E0
+#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002
+#define GL_STACK_OVERFLOW_KHR 0x0503
+#define GL_STACK_UNDERFLOW_KHR 0x0504
+#define GL_DISPLAY_LIST 0x82E7
+#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0
+#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1
+#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2
+#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3
+#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4
+#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5
+#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6
+#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7
+#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8
+#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9
+#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA
+#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB
+#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC
+#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD
+#ifndef GL_EXT_framebuffer_object
+#define GL_EXT_framebuffer_object 1
+GLAPI int GLAD_GL_EXT_framebuffer_object;
+#endif
+#ifndef GL_EXT_texture_compression_s3tc
+#define GL_EXT_texture_compression_s3tc 1
+GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
+#endif
+#ifndef GL_EXT_texture_filter_anisotropic
+#define GL_EXT_texture_filter_anisotropic 1
+GLAPI int GLAD_GL_EXT_texture_filter_anisotropic;
+#endif
+#ifndef GL_KHR_debug
+#define GL_KHR_debug 1
+GLAPI int GLAD_GL_KHR_debug;
+typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
+GLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl;
+#define glDebugMessageControl glad_glDebugMessageControl
+typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);
+GLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert;
+#define glDebugMessageInsert glad_glDebugMessageInsert
+typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam);
+GLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback;
+#define glDebugMessageCallback glad_glDebugMessageCallback
+typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);
+GLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog;
+#define glGetDebugMessageLog glad_glGetDebugMessageLog
+typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message);
+GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup;
+#define glPushDebugGroup glad_glPushDebugGroup
+typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void);
+GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;
+#define glPopDebugGroup glad_glPopDebugGroup
+typedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label);
+GLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel;
+#define glObjectLabel glad_glObjectLabel
+typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label);
+GLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel;
+#define glGetObjectLabel glad_glGetObjectLabel
+typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label);
+GLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel;
+#define glObjectPtrLabel glad_glObjectPtrLabel
+typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label);
+GLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel;
+#define glGetObjectPtrLabel glad_glGetObjectPtrLabel
+typedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void **params);
+GLAPI PFNGLGETPOINTERVPROC glad_glGetPointerv;
+#define glGetPointerv glad_glGetPointerv
+typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
+GLAPI PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR;
+#define glDebugMessageControlKHR glad_glDebugMessageControlKHR
+typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);
+GLAPI PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR;
+#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR
+typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void *userParam);
+GLAPI PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR;
+#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR
+typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);
+GLAPI PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR;
+#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR
+typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message);
+GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR;
+#define glPushDebugGroupKHR glad_glPushDebugGroupKHR
+typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void);
+GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR;
+#define glPopDebugGroupKHR glad_glPopDebugGroupKHR
+typedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label);
+GLAPI PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR;
+#define glObjectLabelKHR glad_glObjectLabelKHR
+typedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label);
+GLAPI PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR;
+#define glGetObjectLabelKHR glad_glGetObjectLabelKHR
+typedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei length, const GLchar *label);
+GLAPI PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR;
+#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR
+typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label);
+GLAPI PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR;
+#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR
+typedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum pname, void **params);
+GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR;
+#define glGetPointervKHR glad_glGetPointervKHR
+#endif
+#ifndef GL_KHR_texture_compression_astc_ldr
+#define GL_KHR_texture_compression_astc_ldr 1
+GLAPI int GLAD_GL_KHR_texture_compression_astc_ldr;
+#endif
+#ifndef GL_EXT_texture_compression_s3tc
+#define GL_EXT_texture_compression_s3tc 1
+GLAPI int GLAD_GL_EXT_texture_compression_s3tc;
+#endif
+#ifndef GL_EXT_texture_filter_anisotropic
+#define GL_EXT_texture_filter_anisotropic 1
+GLAPI int GLAD_GL_EXT_texture_filter_anisotropic;
+#endif
+#ifndef GL_KHR_debug
+#define GL_KHR_debug 1
+GLAPI int GLAD_GL_KHR_debug;
+#endif
+#ifndef GL_KHR_texture_compression_astc_ldr
+#define GL_KHR_texture_compression_astc_ldr 1
+GLAPI int GLAD_GL_KHR_texture_compression_astc_ldr;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/gl/glad/khrplatform.h b/src/gl/glad/khrplatform.h
new file mode 100644
index 0000000..dd22d92
--- /dev/null
+++ b/src/gl/glad/khrplatform.h
@@ -0,0 +1,290 @@
+#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
+
+#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 _WIN64
+typedef signed long long int khronos_intptr_t;
+typedef unsigned long long int khronos_uintptr_t;
+typedef signed long long int khronos_ssize_t;
+typedef unsigned long long int khronos_usize_t;
+#else
+typedef signed long int khronos_intptr_t;
+typedef unsigned long int khronos_uintptr_t;
+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_ */
diff --git a/src/gl/rwgl3.h b/src/gl/rwgl3.h
new file mode 100644
index 0000000..f7d963d
--- /dev/null
+++ b/src/gl/rwgl3.h
@@ -0,0 +1,307 @@
+#ifdef RW_GL3
+#include "glad/glad.h"
+#ifdef LIBRW_SDL2
+#include <SDL.h>
+#elif defined(LIBRW_SDL3)
+#include <SDL3/SDL.h>
+#elif defined(LIBRW_GLFW)
+#include <GLFW/glfw3.h>
+#else
+not implemented
+#endif
+#endif
+
+namespace rw {
+
+#ifdef RW_GL3
+struct EngineOpenParams
+{
+#ifdef LIBRW_SDL2
+ SDL_Window **window;
+ bool32 fullscreen;
+#elif defined(LIBRW_SDL3)
+ SDL_Window **window;
+ bool32 fullscreen;
+#elif defined(LIBRW_GLFW)
+ GLFWwindow **window;
+#else
+ not implemented
+#endif
+ int width, height;
+ const char *windowtitle;
+};
+#endif
+
+namespace gl3 {
+
+void registerPlatformPlugins(void);
+
+extern Device renderdevice;
+
+// arguments to glVertexAttribPointer basically
+struct AttribDesc
+{
+ uint32 index;
+ int32 type;
+ bool32 normalized;
+ int32 size;
+ uint32 stride;
+ uint32 offset;
+};
+
+enum AttribIndices
+{
+ ATTRIB_POS = 0,
+ ATTRIB_NORMAL,
+ ATTRIB_COLOR,
+ ATTRIB_WEIGHTS,
+ ATTRIB_INDICES,
+ ATTRIB_TEXCOORDS0,
+ ATTRIB_TEXCOORDS1,
+ ATTRIB_TEXCOORDS2,
+ ATTRIB_TEXCOORDS3,
+ ATTRIB_TEXCOORDS4,
+ ATTRIB_TEXCOORDS5,
+ ATTRIB_TEXCOORDS6,
+ ATTRIB_TEXCOORDS7,
+};
+
+// default uniform indices
+extern int32 u_matColor;
+extern int32 u_surfProps;
+
+struct InstanceData
+{
+ uint32 numIndex;
+ uint32 minVert; // not used for rendering
+ int32 numVertices; //
+ Material *material;
+ bool32 vertexAlpha;
+ uint32 program;
+ uint32 offset;
+};
+
+struct InstanceDataHeader : rw::InstanceDataHeader
+{
+ uint32 serialNumber;
+ uint32 numMeshes;
+ uint16 *indexBuffer;
+ uint32 primType;
+ uint8 *vertexBuffer;
+ int32 numAttribs;
+ AttribDesc *attribDesc;
+ uint32 totalNumIndex;
+ uint32 totalNumVertex;
+
+ uint32 ibo;
+ uint32 vbo; // or 2?
+#ifdef RW_GL_USE_VAOS
+ uint32 vao;
+#endif
+
+ InstanceData *inst;
+};
+
+#ifdef RW_GL3
+
+struct Shader;
+
+extern Shader *defaultShader, *defaultShader_noAT;
+extern Shader *defaultShader_fullLight, *defaultShader_fullLight_noAT;
+
+struct Im3DVertex
+{
+ V3d position;
+ V3d normal; // librw extension
+ uint8 r, g, b, a;
+ float32 u, v;
+
+ void setX(float32 x) { this->position.x = x; }
+ void setY(float32 y) { this->position.y = y; }
+ void setZ(float32 z) { this->position.z = z; }
+ void setNormalX(float32 x) { this->normal.x = x; }
+ void setNormalY(float32 y) { this->normal.y = y; }
+ void setNormalZ(float32 z) { this->normal.z = z; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) {
+ this->r = r; this->g = g; this->b = b; this->a = a; }
+ void setU(float32 u) { this->u = u; }
+ void setV(float32 v) { this->v = v; }
+
+ float getX(void) { return this->position.x; }
+ float getY(void) { return this->position.y; }
+ float getZ(void) { return this->position.z; }
+ float getNormalX(void) { return this->normal.x; }
+ float getNormalY(void) { return this->normal.y; }
+ float getNormalZ(void) { return this->normal.z; }
+ RGBA getColor(void) { return makeRGBA(this->r, this->g, this->b, this->a); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+extern RGBA im3dMaterialColor;
+extern SurfaceProperties im3dSurfaceProps;
+
+struct Im2DVertex
+{
+ float32 x, y, z, w;
+ uint8 r, g, b, a;
+ float32 u, v;
+
+ void setScreenX(float32 x) { this->x = x; }
+ void setScreenY(float32 y) { this->y = y; }
+ void setScreenZ(float32 z) { this->z = z; }
+ // This is a bit unefficient but we have to counteract GL's divide, so multiply
+ void setCameraZ(float32 z) { this->w = z; }
+ void setRecipCameraZ(float32 recipz) { this->w = 1.0f/recipz; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) {
+ this->r = r; this->g = g; this->b = b; this->a = a; }
+ void setU(float32 u, float recipz) { this->u = u; }
+ void setV(float32 v, float recipz) { this->v = v; }
+
+ float getScreenX(void) { return this->x; }
+ float getScreenY(void) { return this->y; }
+ float getScreenZ(void) { return this->z; }
+ float getCameraZ(void) { return this->w; }
+ float getRecipCameraZ(void) { return 1.0f/this->w; }
+ RGBA getColor(void) { return makeRGBA(this->r, this->g, this->b, this->a); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+
+void setAttribPointers(AttribDesc *attribDescs, int32 numAttribs);
+void disableAttribPointers(AttribDesc *attribDescs, int32 numAttribs);
+void setupVertexInput(InstanceDataHeader *header);
+void teardownVertexInput(InstanceDataHeader *header);
+
+// Render state
+
+// Vertex shader bits
+enum
+{
+ // These should be low so they could be used as indices
+ VSLIGHT_DIRECT = 1,
+ VSLIGHT_POINT = 2,
+ VSLIGHT_SPOT = 4,
+ VSLIGHT_MASK = 7, // all the above
+ // less critical
+ VSLIGHT_AMBIENT = 8,
+};
+
+extern const char *shaderDecl; // #version stuff
+extern const char *header_vert_src;
+extern const char *header_frag_src;
+
+extern Shader *im2dOverrideShader;
+
+// per Scene
+void setProjectionMatrix(float32*);
+void setViewMatrix(float32*);
+
+// per Object
+void setWorldMatrix(Matrix*);
+int32 setLights(WorldLights *lightData);
+
+// per Mesh
+void setTexture(int32 n, Texture *tex);
+void setMaterial(const RGBA &color, const SurfaceProperties &surfaceprops, float extraSurfProp = 0.0f);
+inline void setMaterial(uint32 flags, const RGBA &color, const SurfaceProperties &surfaceprops, float extraSurfProp = 0.0f)
+{
+ static RGBA white = { 255, 255, 255, 255 };
+ if(flags & Geometry::MODULATE)
+ setMaterial(color, surfaceprops, extraSurfProp);
+ else
+ setMaterial(white, surfaceprops, extraSurfProp);
+}
+
+void setAlphaBlend(bool32 enable);
+bool32 getAlphaBlend(void);
+
+bool32 getAlphaTest(void);
+
+void bindFramebuffer(uint32 fbo);
+uint32 bindTexture(uint32 texid);
+
+void flushCache(void);
+
+#endif
+
+class ObjPipeline : public rw::ObjPipeline
+{
+public:
+ void init(void);
+ static ObjPipeline *create(void);
+
+ void (*instanceCB)(Geometry *geo, InstanceDataHeader *header, bool32 reinstance);
+ void (*uninstanceCB)(Geometry *geo, InstanceDataHeader *header);
+ void (*renderCB)(Atomic *atomic, InstanceDataHeader *header);
+};
+
+void defaultInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance);
+void defaultUninstanceCB(Geometry *geo, InstanceDataHeader *header);
+void defaultRenderCB(Atomic *atomic, InstanceDataHeader *header);
+int32 lightingCB(Atomic *atomic);
+int32 lightingCB(void);
+
+void drawInst_simple(InstanceDataHeader *header, InstanceData *inst);
+// Emulate PS2 GS alpha test FB_ONLY case: failed alpha writes to frame- but not to depth buffer
+void drawInst_GSemu(InstanceDataHeader *header, InstanceData *inst);
+// This one switches between the above two depending on render state;
+void drawInst(InstanceDataHeader *header, InstanceData *inst);
+
+
+void *destroyNativeData(void *object, int32, int32);
+
+ObjPipeline *makeDefaultPipeline(void);
+
+// Native Texture and Raster
+
+struct Gl3Raster
+{
+ // arguments to glTexImage2D
+ int32 internalFormat;
+ int32 type;
+ int32 format;
+ int32 bpp; // bytes per pixel
+ // texture object
+ uint32 texid;
+
+ bool isCompressed;
+ bool hasAlpha;
+ bool autogenMipmap;
+ int8 numLevels;
+ // cached filtermode and addressing
+ uint8 filterMode;
+ uint8 addressU;
+ uint8 addressV;
+ int32 maxAnisotropy;
+
+ uint32 fbo; // used for camera texture only!
+ Raster *fboMate; // color or zbuffer raster mate of this one
+ RasterLevels *backingStore; // if we can't read back GPU memory but have to
+};
+
+struct Gl3Caps
+{
+ int gles;
+ int glversion;
+ bool dxtSupported;
+ bool astcSupported; // not used yet
+ float maxAnisotropy;
+};
+extern Gl3Caps gl3Caps;
+// GLES can't read back textures very nicely.
+// In most cases that's not an issue, but when it is,
+// this has to be set before the texture is filled:
+extern bool32 needToReadBackTextures;
+
+void allocateDXT(Raster *raster, int32 dxt, int32 numLevels, bool32 hasAlpha);
+
+Texture *readNativeTexture(Stream *stream);
+void writeNativeTexture(Texture *tex, Stream *stream);
+uint32 getSizeNativeTexture(Texture *tex);
+
+extern int32 nativeRasterOffset;
+void registerNativeRaster(void);
+#define GETGL3RASTEREXT(raster) PLUGINOFFSET(Gl3Raster, raster, rw::gl3::nativeRasterOffset)
+
+}
+}
diff --git a/src/gl/rwgl3impl.h b/src/gl/rwgl3impl.h
new file mode 100644
index 0000000..9d3be44
--- /dev/null
+++ b/src/gl/rwgl3impl.h
@@ -0,0 +1,92 @@
+namespace rw {
+namespace gl3 {
+
+#ifdef RW_OPENGL
+
+extern uint32 im2DVbo, im2DIbo;
+void openIm2D(void);
+void closeIm2D(void);
+void im2DRenderLine(void *vertices, int32 numVertices,
+ int32 vert1, int32 vert2);
+void im2DRenderTriangle(void *vertices, int32 numVertices,
+ int32 vert1, int32 vert2, int32 vert3);
+void im2DRenderPrimitive(PrimitiveType primType,
+ void *vertices, int32 numVertices);
+void im2DRenderIndexedPrimitive(PrimitiveType primType,
+ void *vertices, int32 numVertices, void *indices, int32 numIndices);
+
+void openIm3D(void);
+void closeIm3D(void);
+void im3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags);
+void im3DRenderPrimitive(PrimitiveType primType);
+void im3DRenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices);
+void im3DEnd(void);
+
+struct DisplayMode
+{
+#ifdef LIBRW_SDL2
+ SDL_DisplayMode mode;
+#elif defined(LIBRW_SDL3)
+ SDL_DisplayMode mode;
+#elif defined(LIBRW_GLFW)
+ GLFWvidmode mode;
+#else
+ not implemented
+#endif
+ int32 depth;
+ uint32 flags;
+};
+
+struct GlGlobals
+{
+#ifdef LIBRW_SDL2
+ SDL_Window **pWindow;
+ SDL_Window *window;
+ SDL_GLContext glcontext;
+
+ int numDisplays;
+ int currentDisplay;
+#elif defined(LIBRW_SDL3)
+ SDL_Window **pWindow;
+ SDL_Window *window;
+ SDL_GLContext glcontext;
+
+ int numDisplays;
+ int currentDisplay;
+#elif defined(LIBRW_GLFW)
+ GLFWwindow **pWindow;
+ GLFWwindow *window;
+
+ GLFWmonitor *monitor;
+ int numMonitors;
+ int currentMonitor;
+#else
+ not implemented
+#endif
+
+ DisplayMode *modes;
+ int numModes;
+ int currentMode;
+ int presentWidth, presentHeight;
+ int presentOffX, presentOffY;
+
+ // for opening the window
+ int winWidth, winHeight;
+ const char *winTitle;
+ uint32 numSamples;
+};
+
+extern GlGlobals glGlobals;
+#endif
+
+Raster *rasterCreate(Raster *raster);
+uint8 *rasterLock(Raster*, int32 level, int32 lockMode);
+void rasterUnlock(Raster*, int32);
+int32 rasterNumLevels(Raster*);
+bool32 imageFindRasterFormat(Image *img, int32 type,
+ int32 *width, int32 *height, int32 *depth, int32 *format);
+bool32 rasterFromImage(Raster *raster, Image *image);
+Image *rasterToImage(Raster *raster);
+
+}
+}
diff --git a/src/gl/rwgl3plg.h b/src/gl/rwgl3plg.h
new file mode 100644
index 0000000..5374b3e
--- /dev/null
+++ b/src/gl/rwgl3plg.h
@@ -0,0 +1,16 @@
+namespace rw {
+namespace gl3 {
+
+void initMatFX(void);
+ObjPipeline *makeMatFXPipeline(void);
+void matfxRenderCB(Atomic *atomic, InstanceDataHeader *header);
+
+void initSkin(void);
+ObjPipeline *makeSkinPipeline(void);
+void skinInstanceCB(Geometry *geo, InstanceDataHeader *header, bool32 reinstance);
+void skinRenderCB(Atomic *atomic, InstanceDataHeader *header);
+void uploadSkinMatrices(Atomic *atomic);
+
+
+}
+}
diff --git a/src/gl/rwgl3shader.h b/src/gl/rwgl3shader.h
new file mode 100644
index 0000000..890f12b
--- /dev/null
+++ b/src/gl/rwgl3shader.h
@@ -0,0 +1,69 @@
+#ifdef RW_OPENGL
+
+namespace rw {
+namespace gl3 {
+
+// TODO: make this dynamic
+enum {
+ MAX_UNIFORMS = 40,
+ MAX_BLOCKS = 20
+};
+
+enum UniformType
+{
+ UNIFORM_NA, // managed by the user
+ UNIFORM_VEC4,
+ UNIFORM_IVEC4,
+ UNIFORM_MAT4
+};
+
+struct Uniform
+{
+ char *name;
+ UniformType type;
+ //bool dirty;
+ uint32 serialNum;
+ int32 num;
+ void *data;
+};
+
+struct UniformRegistry
+{
+ int32 numUniforms;
+ Uniform uniforms[MAX_UNIFORMS];
+
+ int32 numBlocks;
+ char *blockNames[MAX_BLOCKS];
+};
+
+int32 registerUniform(const char *name, UniformType type = UNIFORM_NA, int32 num = 1);
+int32 findUniform(const char *name);
+int32 registerBlock(const char *name);
+int32 findBlock(const char *name);
+
+void setUniform(int32 id, void *data);
+void flushUniforms(void);
+
+extern UniformRegistry uniformRegistry;
+
+struct Shader
+{
+ GLuint program;
+ // same number of elements as UniformRegistry::numUniforms
+ GLint *uniformLocations;
+ uint32 *serialNums;
+ int32 numUniforms; // just to be sure!
+
+ static Shader *create(const char **vsrc, const char **fsrc);
+// static Shader *fromFiles(const char *vs, const char *fs);
+// static Shader *fromStrings(const char *vsrc, const char *fsrc);
+ void use(void);
+ void destroy(void);
+};
+
+extern Shader *currentShader;
+
+}
+}
+
+#endif
diff --git a/src/gl/rwwdgl.h b/src/gl/rwwdgl.h
new file mode 100644
index 0000000..b336ba3
--- /dev/null
+++ b/src/gl/rwwdgl.h
@@ -0,0 +1,88 @@
+
+namespace rw {
+namespace wdgl {
+
+// NOTE: This is not really RW OpenGL! It's specific to WarDrum's GTA ports
+
+void registerPlatformPlugins(void);
+
+struct AttribDesc
+{
+ // arguments to glVertexAttribPointer (should use OpenGL types here)
+ // Vertex = 0, TexCoord, Normal, Color, Weight, Bone Index, Extra Color
+ uint32 index;
+ // float = 0, byte, ubyte, short, ushort
+ int32 type;
+ bool32 normalized;
+ int32 size;
+ uint32 stride;
+ uint32 offset;
+};
+
+struct InstanceDataHeader : rw::InstanceDataHeader
+{
+ int32 numAttribs;
+ AttribDesc *attribs;
+ uint32 dataSize;
+ uint8 *data;
+
+ // needed for rendering
+ uint32 vbo;
+ uint32 ibo;
+};
+
+// only RW_OPENGL
+void uploadGeo(Geometry *geo);
+void setAttribPointers(InstanceDataHeader *inst);
+
+void packattrib(uint8 *dst, float32 *src, AttribDesc *a, float32 scale);
+void unpackattrib(float *dst, uint8 *src, AttribDesc *a, float32 scale);
+
+void *destroyNativeData(void *object, int32, int32);
+Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32);
+Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32);
+int32 getSizeNativeData(void *object, int32, int32);
+void registerNativeDataPlugin(void);
+
+void printPipeinfo(Atomic *a);
+
+class ObjPipeline : public rw::ObjPipeline
+{
+public:
+ void init(void);
+ static ObjPipeline *create(void);
+
+ uint32 numCustomAttribs;
+ uint32 (*instanceCB)(Geometry *g, int32 i, uint32 offset);
+ void (*uninstanceCB)(Geometry *g);
+};
+
+ObjPipeline *makeDefaultPipeline(void);
+
+// Skin plugin
+
+void initSkin(void);
+Stream *readNativeSkin(Stream *stream, int32, void *object, int32 offset);
+Stream *writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset);
+int32 getSizeNativeSkin(void *object, int32 offset);
+
+ObjPipeline *makeSkinPipeline(void);
+
+// MatFX plugin
+
+void initMatFX(void);
+ObjPipeline *makeMatFXPipeline(void);
+
+// Raster
+
+struct Texture : rw::Texture
+{
+ void upload(void);
+ void bind(int n);
+};
+
+extern int32 nativeRasterOffset;
+void registerNativeRaster(void);
+
+}
+}
diff --git a/src/gl/shaders/Makefile b/src/gl/shaders/Makefile
new file mode 100644
index 0000000..6471f17
--- /dev/null
+++ b/src/gl/shaders/Makefile
@@ -0,0 +1,45 @@
+all: header_vs.inc header_fs.inc im2d_gl.inc im3d_gl.inc default_vs_gl.inc simple_fs_gl.inc matfx_gl.inc skin_gl.inc
+
+header_vs.inc: header.vert
+ (echo 'const char *header_vert_src =';\
+ sed 's/..*/"&\\n"/' header.vert;\
+ echo ';') >header_vs.inc
+
+header_fs.inc: header.frag
+ (echo 'const char *header_frag_src =';\
+ sed 's/..*/"&\\n"/' header.frag;\
+ echo ';') >header_fs.inc
+
+im2d_gl.inc: im2d.vert
+ (echo 'const char *im2d_vert_src =';\
+ sed 's/..*/"&\\n"/' im2d.vert;\
+ echo ';') >im2d_gl.inc
+
+im3d_gl.inc: im3d.vert
+ (echo 'const char *im3d_vert_src =';\
+ sed 's/..*/"&\\n"/' im3d.vert;\
+ echo ';') >im3d_gl.inc
+
+default_vs_gl.inc: default.vert
+ (echo 'const char *default_vert_src =';\
+ sed 's/..*/"&\\n"/' default.vert;\
+ echo ';') >default_vs_gl.inc
+
+simple_fs_gl.inc: simple.frag
+ (echo 'const char *simple_frag_src =';\
+ sed 's/..*/"&\\n"/' simple.frag;\
+ echo ';') >simple_fs_gl.inc
+
+matfx_gl.inc: matfx_env.frag matfx_env.vert
+ (echo 'const char *matfx_env_vert_src =';\
+ sed 's/..*/"&\\n"/' matfx_env.vert;\
+ echo ';';\
+ echo 'const char *matfx_env_frag_src =';\
+ sed 's/..*/"&\\n"/' matfx_env.frag;\
+ echo ';') >matfx_gl.inc
+
+skin_gl.inc: skin.vert
+ (echo 'const char *skin_vert_src =';\
+ sed 's/..*/"&\\n"/' skin.vert;\
+ echo ';') >skin_gl.inc
+
diff --git a/src/gl/shaders/default.vert b/src/gl/shaders/default.vert
new file mode 100644
index 0000000..8e3fad7
--- /dev/null
+++ b/src/gl/shaders/default.vert
@@ -0,0 +1,23 @@
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec4 Vertex = u_world * vec4(in_pos, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * in_normal;
+
+ v_tex0 = in_tex0;
+
+ v_color = in_color;
+ v_color.rgb += u_ambLight.rgb*surfAmbient;
+ v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;
+ v_color = clamp(v_color, 0.0, 1.0);
+ v_color *= u_matColor;
+
+ v_fog = DoFog(gl_Position.w);
+}
diff --git a/src/gl/shaders/default_vs_gl.inc b/src/gl/shaders/default_vs_gl.inc
new file mode 100644
index 0000000..6e3d6c4
--- /dev/null
+++ b/src/gl/shaders/default_vs_gl.inc
@@ -0,0 +1,25 @@
+const char *default_vert_src =
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * in_normal;\n"
+
+" v_tex0 = in_tex0;\n"
+
+" v_color = in_color;\n"
+" v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
+" v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"
+" v_color = clamp(v_color, 0.0, 1.0);\n"
+" v_color *= u_matColor;\n"
+
+" v_fog = DoFog(gl_Position.w);\n"
+"}\n"
+;
diff --git a/src/gl/shaders/header.frag b/src/gl/shaders/header.frag
new file mode 100644
index 0000000..ba50165
--- /dev/null
+++ b/src/gl/shaders/header.frag
@@ -0,0 +1,30 @@
+#ifdef USE_UBOS
+layout(std140) uniform State
+{
+ vec2 u_alphaRef;
+ vec4 u_fogData;
+ vec4 u_fogColor;
+};
+#else
+uniform vec4 u_alphaRef;
+
+uniform vec4 u_fogData;
+uniform vec4 u_fogColor;
+#endif
+
+#define u_fogStart (u_fogData.x)
+#define u_fogEnd (u_fogData.y)
+#define u_fogRange (u_fogData.z)
+#define u_fogDisable (u_fogData.w)
+
+#ifndef GL2
+out vec4 fragColor;
+#endif
+
+void DoAlphaTest(float a)
+{
+#ifndef NO_ALPHATEST
+ if(a < u_alphaRef.x || a >= u_alphaRef.y)
+ discard;
+#endif
+}
diff --git a/src/gl/shaders/header.vert b/src/gl/shaders/header.vert
new file mode 100644
index 0000000..bb9881f
--- /dev/null
+++ b/src/gl/shaders/header.vert
@@ -0,0 +1,128 @@
+
+//#define DIRECTIONALS
+//#define POINTLIGHTS
+//#define SPOTLIGHTS
+
+#define ATTRIB_POS 0
+#define ATTRIB_NORMAL 1
+#define ATTRIB_COLOR 2
+#define ATTRIB_WEIGHTS 3
+#define ATTRIB_INDICES 4
+#define ATTRIB_TEXCOORDS0 5
+#define ATTRIB_TEXCOORDS1 6
+
+
+VSIN(ATTRIB_NORMAL) vec3 in_normal;
+VSIN(ATTRIB_COLOR) vec4 in_color;
+VSIN(ATTRIB_WEIGHTS) vec4 in_weights;
+VSIN(ATTRIB_INDICES) vec4 in_indices;
+VSIN(ATTRIB_TEXCOORDS0) vec2 in_tex0;
+VSIN(ATTRIB_TEXCOORDS1) vec2 in_tex1;
+
+
+#ifdef USE_UBOS
+layout(std140) uniform State
+{
+ vec2 u_alphaRef;
+ vec4 u_fogData;
+ vec4 u_fogColor;
+};
+#else
+uniform vec4 u_alphaRef;
+uniform vec4 u_fogData;
+uniform vec4 u_fogColor;
+#endif
+
+#define u_fogStart (u_fogData.x)
+#define u_fogEnd (u_fogData.y)
+#define u_fogRange (u_fogData.z)
+#define u_fogDisable (u_fogData.w)
+
+#ifdef USE_UBOS
+layout(std140) uniform Scene
+{
+ mat4 u_proj;
+ mat4 u_view;
+};
+#else
+uniform mat4 u_proj;
+uniform mat4 u_view;
+#endif
+
+#define MAX_LIGHTS 8
+
+#ifdef USE_UBOS
+layout(std140) uniform Object
+{
+ mat4 u_world;
+ vec4 u_ambLight;
+ vec4 u_lightParams[MAX_LIGHTS]; // type, radius, minusCosAngle, hardSpot
+ vec4 u_lightPosition[MAX_LIGHTS];
+ vec4 u_lightDirection[MAX_LIGHTS];
+ vec4 u_lightColor[MAX_LIGHTS];
+};
+#else
+uniform mat4 u_world;
+uniform vec4 u_ambLight;
+uniform vec4 u_lightParams[MAX_LIGHTS]; // type, radius, minusCosAngle, hardSpot
+uniform vec4 u_lightPosition[MAX_LIGHTS];
+uniform vec4 u_lightDirection[MAX_LIGHTS];
+uniform vec4 u_lightColor[MAX_LIGHTS];
+#endif
+
+uniform vec4 u_matColor;
+uniform vec4 u_surfProps; // amb, spec, diff, extra
+
+#define surfAmbient (u_surfProps.x)
+#define surfSpecular (u_surfProps.y)
+#define surfDiffuse (u_surfProps.z)
+
+vec3 DoDynamicLight(vec3 V, vec3 N)
+{
+ vec3 color = vec3(0.0, 0.0, 0.0);
+ for(int i = 0; i < MAX_LIGHTS; i++){
+ if(u_lightParams[i].x == 0.0)
+ break;
+#ifdef DIRECTIONALS
+ if(u_lightParams[i].x == 1.0){
+ // direct
+ float l = max(0.0, dot(N, -u_lightDirection[i].xyz));
+ color += l*u_lightColor[i].rgb;
+ }else
+#endif
+#ifdef POINTLIGHTS
+ if(u_lightParams[i].x == 2.0){
+ // point
+ vec3 dir = V - u_lightPosition[i].xyz;
+ float dist = length(dir);
+ float atten = max(0.0, (1.0 - dist/u_lightParams[i].y));
+ float l = max(0.0, dot(N, -normalize(dir)));
+ color += l*u_lightColor[i].rgb*atten;
+ }else
+#endif
+#ifdef SPOTLIGHTS
+ if(u_lightParams[i].x == 3.0){
+ // spot
+ vec3 dir = V - u_lightPosition[i].xyz;
+ float dist = length(dir);
+ float atten = max(0.0, (1.0 - dist/u_lightParams[i].y));
+ dir /= dist;
+ float l = max(0.0, dot(N, -dir));
+ float pcos = dot(dir, u_lightDirection[i].xyz); // cos to point
+ float ccos = -u_lightParams[i].z;
+ float falloff = (pcos-ccos)/(1.0-ccos);
+ if(falloff < 0.0) // outside of cone
+ l = 0.0;
+ l *= max(falloff, u_lightParams[i].w);
+ return l*u_lightColor[i].rgb*atten;
+ }else
+#endif
+ ;
+ }
+ return color;
+}
+
+float DoFog(float w)
+{
+ return clamp((w - u_fogEnd)*u_fogRange, u_fogDisable, 1.0);
+}
diff --git a/src/gl/shaders/header_fs.inc b/src/gl/shaders/header_fs.inc
new file mode 100644
index 0000000..802bb4f
--- /dev/null
+++ b/src/gl/shaders/header_fs.inc
@@ -0,0 +1,32 @@
+const char *header_frag_src =
+"#ifdef USE_UBOS\n"
+"layout(std140) uniform State\n"
+"{\n"
+" vec2 u_alphaRef;\n"
+" vec4 u_fogData;\n"
+" vec4 u_fogColor;\n"
+"};\n"
+"#else\n"
+"uniform vec4 u_alphaRef;\n"
+
+"uniform vec4 u_fogData;\n"
+"uniform vec4 u_fogColor;\n"
+"#endif\n"
+
+"#define u_fogStart (u_fogData.x)\n"
+"#define u_fogEnd (u_fogData.y)\n"
+"#define u_fogRange (u_fogData.z)\n"
+"#define u_fogDisable (u_fogData.w)\n"
+
+"#ifndef GL2\n"
+"out vec4 fragColor;\n"
+"#endif\n"
+
+"void DoAlphaTest(float a)\n"
+"{\n"
+"#ifndef NO_ALPHATEST\n"
+" if(a < u_alphaRef.x || a >= u_alphaRef.y)\n"
+" discard;\n"
+"#endif\n"
+"}\n"
+;
diff --git a/src/gl/shaders/header_vs.inc b/src/gl/shaders/header_vs.inc
new file mode 100644
index 0000000..ffa4683
--- /dev/null
+++ b/src/gl/shaders/header_vs.inc
@@ -0,0 +1,130 @@
+const char *header_vert_src =
+
+"//#define DIRECTIONALS\n"
+"//#define POINTLIGHTS\n"
+"//#define SPOTLIGHTS\n"
+
+"#define ATTRIB_POS 0\n"
+"#define ATTRIB_NORMAL 1\n"
+"#define ATTRIB_COLOR 2\n"
+"#define ATTRIB_WEIGHTS 3\n"
+"#define ATTRIB_INDICES 4\n"
+"#define ATTRIB_TEXCOORDS0 5\n"
+"#define ATTRIB_TEXCOORDS1 6\n"
+
+
+"VSIN(ATTRIB_NORMAL) vec3 in_normal;\n"
+"VSIN(ATTRIB_COLOR) vec4 in_color;\n"
+"VSIN(ATTRIB_WEIGHTS) vec4 in_weights;\n"
+"VSIN(ATTRIB_INDICES) vec4 in_indices;\n"
+"VSIN(ATTRIB_TEXCOORDS0) vec2 in_tex0;\n"
+"VSIN(ATTRIB_TEXCOORDS1) vec2 in_tex1;\n"
+
+
+"#ifdef USE_UBOS\n"
+"layout(std140) uniform State\n"
+"{\n"
+" vec2 u_alphaRef;\n"
+" vec4 u_fogData;\n"
+" vec4 u_fogColor;\n"
+"};\n"
+"#else\n"
+"uniform vec4 u_alphaRef;\n"
+"uniform vec4 u_fogData;\n"
+"uniform vec4 u_fogColor;\n"
+"#endif\n"
+
+"#define u_fogStart (u_fogData.x)\n"
+"#define u_fogEnd (u_fogData.y)\n"
+"#define u_fogRange (u_fogData.z)\n"
+"#define u_fogDisable (u_fogData.w)\n"
+
+"#ifdef USE_UBOS\n"
+"layout(std140) uniform Scene\n"
+"{\n"
+" mat4 u_proj;\n"
+" mat4 u_view;\n"
+"};\n"
+"#else\n"
+"uniform mat4 u_proj;\n"
+"uniform mat4 u_view;\n"
+"#endif\n"
+
+"#define MAX_LIGHTS 8\n"
+
+"#ifdef USE_UBOS\n"
+"layout(std140) uniform Object\n"
+"{\n"
+" mat4 u_world;\n"
+" vec4 u_ambLight;\n"
+" vec4 u_lightParams[MAX_LIGHTS]; // type, radius, minusCosAngle, hardSpot\n"
+" vec4 u_lightPosition[MAX_LIGHTS];\n"
+" vec4 u_lightDirection[MAX_LIGHTS];\n"
+" vec4 u_lightColor[MAX_LIGHTS];\n"
+"};\n"
+"#else\n"
+"uniform mat4 u_world;\n"
+"uniform vec4 u_ambLight;\n"
+"uniform vec4 u_lightParams[MAX_LIGHTS]; // type, radius, minusCosAngle, hardSpot\n"
+"uniform vec4 u_lightPosition[MAX_LIGHTS];\n"
+"uniform vec4 u_lightDirection[MAX_LIGHTS];\n"
+"uniform vec4 u_lightColor[MAX_LIGHTS];\n"
+"#endif\n"
+
+"uniform vec4 u_matColor;\n"
+"uniform vec4 u_surfProps; // amb, spec, diff, extra\n"
+
+"#define surfAmbient (u_surfProps.x)\n"
+"#define surfSpecular (u_surfProps.y)\n"
+"#define surfDiffuse (u_surfProps.z)\n"
+
+"vec3 DoDynamicLight(vec3 V, vec3 N)\n"
+"{\n"
+" vec3 color = vec3(0.0, 0.0, 0.0);\n"
+" for(int i = 0; i < MAX_LIGHTS; i++){\n"
+" if(u_lightParams[i].x == 0.0)\n"
+" break;\n"
+"#ifdef DIRECTIONALS\n"
+" if(u_lightParams[i].x == 1.0){\n"
+" // direct\n"
+" float l = max(0.0, dot(N, -u_lightDirection[i].xyz));\n"
+" color += l*u_lightColor[i].rgb;\n"
+" }else\n"
+"#endif\n"
+"#ifdef POINTLIGHTS\n"
+" if(u_lightParams[i].x == 2.0){\n"
+" // point\n"
+" vec3 dir = V - u_lightPosition[i].xyz;\n"
+" float dist = length(dir);\n"
+" float atten = max(0.0, (1.0 - dist/u_lightParams[i].y));\n"
+" float l = max(0.0, dot(N, -normalize(dir)));\n"
+" color += l*u_lightColor[i].rgb*atten;\n"
+" }else\n"
+"#endif\n"
+"#ifdef SPOTLIGHTS\n"
+" if(u_lightParams[i].x == 3.0){\n"
+" // spot\n"
+" vec3 dir = V - u_lightPosition[i].xyz;\n"
+" float dist = length(dir);\n"
+" float atten = max(0.0, (1.0 - dist/u_lightParams[i].y));\n"
+" dir /= dist;\n"
+" float l = max(0.0, dot(N, -dir));\n"
+" float pcos = dot(dir, u_lightDirection[i].xyz); // cos to point\n"
+" float ccos = -u_lightParams[i].z;\n"
+" float falloff = (pcos-ccos)/(1.0-ccos);\n"
+" if(falloff < 0.0) // outside of cone\n"
+" l = 0.0;\n"
+" l *= max(falloff, u_lightParams[i].w);\n"
+" return l*u_lightColor[i].rgb*atten;\n"
+" }else\n"
+"#endif\n"
+" ;\n"
+" }\n"
+" return color;\n"
+"}\n"
+
+"float DoFog(float w)\n"
+"{\n"
+" return clamp((w - u_fogEnd)*u_fogRange, u_fogDisable, 1.0);\n"
+"}\n"
+;
diff --git a/src/gl/shaders/im2d.vert b/src/gl/shaders/im2d.vert
new file mode 100644
index 0000000..cdb9da8
--- /dev/null
+++ b/src/gl/shaders/im2d.vert
@@ -0,0 +1,18 @@
+uniform vec4 u_xform;
+
+VSIN(ATTRIB_POS) vec4 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ gl_Position = in_pos;
+ gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;
+ v_fog = DoFog(gl_Position.w);
+ gl_Position.xyz *= gl_Position.w;
+ v_color = in_color;
+ v_tex0 = in_tex0;
+}
diff --git a/src/gl/shaders/im2d_gl.inc b/src/gl/shaders/im2d_gl.inc
new file mode 100644
index 0000000..4e1d631
--- /dev/null
+++ b/src/gl/shaders/im2d_gl.inc
@@ -0,0 +1,20 @@
+const char *im2d_vert_src =
+"uniform vec4 u_xform;\n"
+
+"VSIN(ATTRIB_POS) vec4 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" gl_Position = in_pos;\n"
+" gl_Position.xy = gl_Position.xy * u_xform.xy + u_xform.zw;\n"
+" v_fog = DoFog(gl_Position.w);\n"
+" gl_Position.xyz *= gl_Position.w;\n"
+" v_color = in_color;\n"
+" v_tex0 = in_tex0;\n"
+"}\n"
+;
diff --git a/src/gl/shaders/im3d.vert b/src/gl/shaders/im3d.vert
new file mode 100644
index 0000000..280b128
--- /dev/null
+++ b/src/gl/shaders/im3d.vert
@@ -0,0 +1,15 @@
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec4 Vertex = u_world * vec4(in_pos, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ v_color = in_color;
+ v_tex0 = in_tex0;
+ v_fog = DoFog(gl_Position.w);
+}
diff --git a/src/gl/shaders/im3d_gl.inc b/src/gl/shaders/im3d_gl.inc
new file mode 100644
index 0000000..40ec7a1
--- /dev/null
+++ b/src/gl/shaders/im3d_gl.inc
@@ -0,0 +1,17 @@
+const char *im3d_vert_src =
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" v_color = in_color;\n"
+" v_tex0 = in_tex0;\n"
+" v_fog = DoFog(gl_Position.w);\n"
+"}\n"
+;
diff --git a/src/gl/shaders/matfx_env.frag b/src/gl/shaders/matfx_env.frag
new file mode 100644
index 0000000..0ef20d1
--- /dev/null
+++ b/src/gl/shaders/matfx_env.frag
@@ -0,0 +1,34 @@
+uniform sampler2D tex0;
+uniform sampler2D tex1;
+
+uniform vec4 u_fxparams;
+
+#define shininess (u_fxparams.x)
+#define disableFBA (u_fxparams.y)
+
+FSIN vec4 v_color;
+FSIN vec4 v_envColor;
+FSIN vec2 v_tex0;
+FSIN vec2 v_tex1;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 pass1 = v_color;
+ pass1 *= texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+
+ vec4 pass2 = v_envColor*shininess*texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));
+
+ pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog);
+ pass2.rgb = mix(vec3(0.0, 0.0, 0.0), pass2.rgb, v_fog);
+
+ float fba = max(pass1.a, disableFBA);
+ vec4 color;
+ color.rgb = pass1.rgb*pass1.a + pass2.rgb*fba;
+ color.a = pass1.a;
+
+ DoAlphaTest(color.a);
+
+ FRAGCOLOR(color);
+}
diff --git a/src/gl/shaders/matfx_env.vert b/src/gl/shaders/matfx_env.vert
new file mode 100644
index 0000000..580a49f
--- /dev/null
+++ b/src/gl/shaders/matfx_env.vert
@@ -0,0 +1,31 @@
+uniform mat4 u_texMatrix;
+uniform vec4 u_colorClamp;
+uniform vec4 u_envColor;
+
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec4 v_envColor;
+VSOUT vec2 v_tex0;
+VSOUT vec2 v_tex1;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec4 Vertex = u_world * vec4(in_pos, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * in_normal;
+
+ v_tex0 = in_tex0;
+ v_tex1 = (u_texMatrix * vec4(Normal, 1.0)).xy;
+
+ v_color = in_color;
+ v_color.rgb += u_ambLight.rgb*surfAmbient;
+ v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;
+ v_color = clamp(v_color, 0.0, 1.0);
+ v_envColor = max(v_color, u_colorClamp) * u_envColor;
+ v_color *= u_matColor;
+
+ v_fog = DoFog(gl_Position.w);
+}
diff --git a/src/gl/shaders/matfx_gl.inc b/src/gl/shaders/matfx_gl.inc
new file mode 100644
index 0000000..a1dcaf8
--- /dev/null
+++ b/src/gl/shaders/matfx_gl.inc
@@ -0,0 +1,69 @@
+const char *matfx_env_vert_src =
+"uniform mat4 u_texMatrix;\n"
+"uniform vec4 u_colorClamp;\n"
+"uniform vec4 u_envColor;\n"
+
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec4 v_envColor;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT vec2 v_tex1;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 Vertex = u_world * vec4(in_pos, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * in_normal;\n"
+
+" v_tex0 = in_tex0;\n"
+" v_tex1 = (u_texMatrix * vec4(Normal, 1.0)).xy;\n"
+
+" v_color = in_color;\n"
+" v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
+" v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"
+" v_color = clamp(v_color, 0.0, 1.0);\n"
+" v_envColor = max(v_color, u_colorClamp) * u_envColor;\n"
+" v_color *= u_matColor;\n"
+
+" v_fog = DoFog(gl_Position.w);\n"
+"}\n"
+;
+const char *matfx_env_frag_src =
+"uniform sampler2D tex0;\n"
+"uniform sampler2D tex1;\n"
+
+"uniform vec4 u_fxparams;\n"
+
+"#define shininess (u_fxparams.x)\n"
+"#define disableFBA (u_fxparams.y)\n"
+
+"FSIN vec4 v_color;\n"
+"FSIN vec4 v_envColor;\n"
+"FSIN vec2 v_tex0;\n"
+"FSIN vec2 v_tex1;\n"
+"FSIN float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 pass1 = v_color;\n"
+" pass1 *= texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+
+" vec4 pass2 = v_envColor*shininess*texture(tex1, vec2(v_tex1.x, 1.0-v_tex1.y));\n"
+
+" pass1.rgb = mix(u_fogColor.rgb, pass1.rgb, v_fog);\n"
+" pass2.rgb = mix(vec3(0.0, 0.0, 0.0), pass2.rgb, v_fog);\n"
+
+" float fba = max(pass1.a, disableFBA);\n"
+" vec4 color;\n"
+" color.rgb = pass1.rgb*pass1.a + pass2.rgb*fba;\n"
+" color.a = pass1.a;\n"
+
+" DoAlphaTest(color.a);\n"
+
+" FRAGCOLOR(color);\n"
+"}\n"
+;
diff --git a/src/gl/shaders/simple.frag b/src/gl/shaders/simple.frag
new file mode 100644
index 0000000..34c1e43
--- /dev/null
+++ b/src/gl/shaders/simple.frag
@@ -0,0 +1,14 @@
+uniform sampler2D tex0;
+
+FSIN vec4 v_color;
+FSIN vec2 v_tex0;
+FSIN float v_fog;
+
+void
+main(void)
+{
+ vec4 color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));
+ color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);
+ DoAlphaTest(color.a);
+ FRAGCOLOR(color);
+}
diff --git a/src/gl/shaders/simple_fs_gl.inc b/src/gl/shaders/simple_fs_gl.inc
new file mode 100644
index 0000000..a9216ca
--- /dev/null
+++ b/src/gl/shaders/simple_fs_gl.inc
@@ -0,0 +1,17 @@
+const char *simple_frag_src =
+"uniform sampler2D tex0;\n"
+
+"FSIN vec4 v_color;\n"
+"FSIN vec2 v_tex0;\n"
+"FSIN float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec4 color = v_color*texture(tex0, vec2(v_tex0.x, 1.0-v_tex0.y));\n"
+" color.rgb = mix(u_fogColor.rgb, color.rgb, v_fog);\n"
+" DoAlphaTest(color.a);\n"
+" FRAGCOLOR(color);\n"
+"}\n"
+
+;
diff --git a/src/gl/shaders/skin.vert b/src/gl/shaders/skin.vert
new file mode 100644
index 0000000..7408542
--- /dev/null
+++ b/src/gl/shaders/skin.vert
@@ -0,0 +1,32 @@
+uniform mat4 u_boneMatrices[64];
+
+VSIN(ATTRIB_POS) vec3 in_pos;
+
+VSOUT vec4 v_color;
+VSOUT vec2 v_tex0;
+VSOUT float v_fog;
+
+void
+main(void)
+{
+ vec3 SkinVertex = vec3(0.0, 0.0, 0.0);
+ vec3 SkinNormal = vec3(0.0, 0.0, 0.0);
+ for(int i = 0; i < 4; i++){
+ SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i];
+ SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i];
+ }
+
+ vec4 Vertex = u_world * vec4(SkinVertex, 1.0);
+ gl_Position = u_proj * u_view * Vertex;
+ vec3 Normal = mat3(u_world) * SkinNormal;
+
+ v_tex0 = in_tex0;
+
+ v_color = in_color;
+ v_color.rgb += u_ambLight.rgb*surfAmbient;
+ v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;
+ v_color = clamp(v_color, 0.0, 1.0);
+ v_color *= u_matColor;
+
+ v_fog = DoFog(gl_Position.z);
+}
diff --git a/src/gl/shaders/skin_gl.inc b/src/gl/shaders/skin_gl.inc
new file mode 100644
index 0000000..7d1268e
--- /dev/null
+++ b/src/gl/shaders/skin_gl.inc
@@ -0,0 +1,34 @@
+const char *skin_vert_src =
+"uniform mat4 u_boneMatrices[64];\n"
+
+"VSIN(ATTRIB_POS) vec3 in_pos;\n"
+
+"VSOUT vec4 v_color;\n"
+"VSOUT vec2 v_tex0;\n"
+"VSOUT float v_fog;\n"
+
+"void\n"
+"main(void)\n"
+"{\n"
+" vec3 SkinVertex = vec3(0.0, 0.0, 0.0);\n"
+" vec3 SkinNormal = vec3(0.0, 0.0, 0.0);\n"
+" for(int i = 0; i < 4; i++){\n"
+" SkinVertex += (u_boneMatrices[int(in_indices[i])] * vec4(in_pos, 1.0)).xyz * in_weights[i];\n"
+" SkinNormal += (mat3(u_boneMatrices[int(in_indices[i])]) * in_normal) * in_weights[i];\n"
+" }\n"
+
+" vec4 Vertex = u_world * vec4(SkinVertex, 1.0);\n"
+" gl_Position = u_proj * u_view * Vertex;\n"
+" vec3 Normal = mat3(u_world) * SkinNormal;\n"
+
+" v_tex0 = in_tex0;\n"
+
+" v_color = in_color;\n"
+" v_color.rgb += u_ambLight.rgb*surfAmbient;\n"
+" v_color.rgb += DoDynamicLight(Vertex.xyz, Normal)*surfDiffuse;\n"
+" v_color = clamp(v_color, 0.0, 1.0);\n"
+" v_color *= u_matColor;\n"
+
+" v_fog = DoFog(gl_Position.z);\n"
+"}\n"
+;
diff --git a/src/gl/wdgl.cpp b/src/gl/wdgl.cpp
new file mode 100644
index 0000000..d1ac4b8
--- /dev/null
+++ b/src/gl/wdgl.cpp
@@ -0,0 +1,875 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwwdgl.h"
+
+#ifdef RW_OPENGL
+#include "glad/glad.h"
+#endif
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace wdgl {
+
+static void*
+driverOpen(void *o, int32, int32)
+{
+ engine->driver[PLATFORM_WDGL]->defaultPipeline = makeDefaultPipeline();
+ return o;
+}
+
+static void*
+driverClose(void *o, int32, int32)
+{
+ return o;
+}
+
+void
+registerPlatformPlugins(void)
+{
+ Driver::registerPlugin(PLATFORM_WDGL, 0, PLATFORM_WDGL,
+ driverOpen, driverClose);
+}
+
+
+// VC
+// 8733 0 0 0 3
+// 45 1 0 0 2
+// 8657 1 3 0 2
+// 4610 2 1 1 3
+// 4185 3 2 1 4
+// 256 4 2 1 4
+// 201 4 4 1 4
+// 457 5 2 0 4
+
+// SA
+// 20303 0 0 0 3 vertices: 3 floats
+// 53 1 0 0 2 texCoords: 2 floats
+// 20043 1 3 0 2 texCoords: 2 shorts
+// 6954 2 1 1 3 normal: 3 bytes normalized
+// 13527 3 2 1 4 color: 4 ubytes normalized
+// 196 4 2 1 4 weight: 4 ubytes normalized
+// 225 4 4 1 4 weight: 4 ushorts normalized
+// 421 5 2 0 4 indices: 4 ubytes
+// 12887 6 2 1 4 extracolor:4 ubytes normalized
+
+/*
+static void
+printAttribInfo(AttribDesc *attribs, int n)
+{
+ for(int i = 0; i < n; i++)
+ printf("%x %x %x %x\n",
+ attribs[i].index,
+ attribs[i].type,
+ attribs[i].normalized,
+ attribs[i].size);
+}
+*/
+
+#ifdef RW_OPENGL
+void
+uploadGeo(Geometry *geo)
+{
+ InstanceDataHeader *inst = (InstanceDataHeader*)geo->instData;
+ MeshHeader *meshHeader = geo->meshHeader;
+
+ glGenBuffers(1, &inst->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, inst->vbo);
+ glBufferData(GL_ARRAY_BUFFER, inst->dataSize,
+ inst->data, GL_STATIC_DRAW);
+
+ glGenBuffers(1, &inst->ibo);
+ glBindBuffer(GL_ARRAY_BUFFER, inst->ibo);
+ glBufferData(GL_ARRAY_BUFFER, meshHeader->totalIndices*2,
+ 0, GL_STATIC_DRAW);
+ GLintptr offset = 0;
+ for(uint32 i = 0; i < meshHeader->numMeshes; i++){
+ Mesh *mesh = &meshHeader->getMeshes()[i];
+ glBufferSubData(GL_ARRAY_BUFFER, offset, mesh->numIndices*2,
+ mesh->indices);
+ offset += mesh->numIndices*2;
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void
+setAttribPointers(InstanceDataHeader *inst)
+{
+ static GLenum attribType[] = {
+ GL_FLOAT,
+ GL_BYTE, GL_UNSIGNED_BYTE,
+ GL_SHORT, GL_UNSIGNED_SHORT
+ };
+ for(int32 i = 0; i < inst->numAttribs; i++){
+ AttribDesc *a = &inst->attribs[i];
+ glEnableVertexAttribArray(a->index);
+ glVertexAttribPointer(a->index, a->size, attribType[a->type],
+ a->normalized, a->stride,
+ (void*)(uint64)a->offset);
+ }
+}
+#endif
+
+void
+packattrib(uint8 *dst, float32 *src, AttribDesc *a, float32 scale=1.0f)
+{
+ int8 *i8dst;
+ uint16 *u16dst;
+ int16 *i16dst;
+
+ switch(a->type){
+ case 0: // float
+ memcpy(dst, src, a->size*4);
+ break;
+
+ // TODO: maybe have loop inside if?
+ case 1: // byte
+ i8dst = (int8*)dst;
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ i8dst[i] = src[i]*scale;
+ else
+ i8dst[i] = src[i]*127.0f;
+ }
+ break;
+
+ case 2: // ubyte
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ dst[i] = src[i]*scale;
+ else
+ dst[i] = src[i]*255.0f;
+ }
+ break;
+
+ case 3: // short
+ i16dst = (int16*)dst;
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ i16dst[i] = src[i]*scale;
+ else
+ i16dst[i] = src[i]*32767.0f;
+ }
+ break;
+
+ case 4: // ushort
+ u16dst = (uint16*)dst;
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ u16dst[i] = src[i]*scale;
+ else
+ u16dst[i] = src[i]*65535.0f;
+ }
+ break;
+ }
+}
+
+void
+unpackattrib(float *dst, uint8 *src, AttribDesc *a, float32 scale=1.0f)
+{
+ int8 *i8src;
+ uint16 *u16src;
+ int16 *i16src;
+
+ switch(a->type){
+ case 0: // float
+ memcpy(dst, src, a->size*4);
+ break;
+
+ // TODO: maybe have loop inside if?
+ case 1: // byte
+ i8src = (int8*)src;
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ dst[i] = i8src[i]/scale;
+ else
+ dst[i] = i8src[i]/127.0f;
+ }
+ break;
+
+ case 2: // ubyte
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ dst[i] = src[i]/scale;
+ else
+ dst[i] = src[i]/255.0f;
+ }
+ break;
+
+ case 3: // short
+ i16src = (int16*)src;
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ dst[i] = i16src[i]/scale;
+ else
+ dst[i] = i16src[i]/32767.0f;
+ }
+ break;
+
+ case 4: // ushort
+ u16src = (uint16*)src;
+ for(int i = 0; i < a->size; i++){
+ if(!a->normalized)
+ dst[i] = u16src[i]/scale;
+ else
+ dst[i] = u16src[i]/65435.0f;
+ }
+ break;
+ }
+}
+
+void*
+destroyNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_WDGL)
+ return object;
+ InstanceDataHeader *header =
+ (InstanceDataHeader*)geometry->instData;
+ geometry->instData = nil;
+ // TODO: delete ibo and vbo
+ rwFree(header->attribs);
+ rwFree(header->data);
+ rwFree(header);
+ return object;
+}
+
+Stream*
+readNativeData(Stream *stream, int32, void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geometry->instData = header;
+ header->platform = PLATFORM_WDGL;
+ header->vbo = 0;
+ header->ibo = 0;
+ header->numAttribs = stream->readU32();
+ header->attribs = rwNewT(AttribDesc, header->numAttribs, MEMDUR_EVENT | ID_GEOMETRY);
+ stream->read32(header->attribs,
+ header->numAttribs*sizeof(AttribDesc));
+ header->dataSize = header->attribs[0].stride*geometry->numVertices;
+ header->data = rwNewT(uint8, header->dataSize, MEMDUR_EVENT | ID_GEOMETRY);
+ ASSERTLITTLE;
+ stream->read8(header->data, header->dataSize);
+ return stream;
+}
+
+Stream*
+writeNativeData(Stream *stream, int32, void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_WDGL)
+ return stream;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ stream->writeU32(header->numAttribs);
+ stream->write32(header->attribs, header->numAttribs*sizeof(AttribDesc));
+ ASSERTLITTLE;
+ stream->write8(header->data, header->dataSize);
+ return stream;
+}
+
+int32
+getSizeNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_WDGL)
+ return 0;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ return 4 + header->numAttribs*sizeof(AttribDesc) + header->dataSize;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+void
+printPipeinfo(Atomic *a)
+{
+ Geometry *g = a->geometry;
+ if(g->instData == nil || g->instData->platform != PLATFORM_WDGL)
+ return;
+ int32 plgid = 0;
+ if(a->pipeline)
+ plgid = a->pipeline->pluginID;
+ printf("%s %x: ", debugFile, plgid);
+ InstanceDataHeader *h = (InstanceDataHeader*)g->instData;
+ for(int i = 0; i < h->numAttribs; i++)
+ printf("%x(%x) ", h->attribs[i].index, h->attribs[i].type);
+ printf("\n");
+}
+
+static void
+instance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // TODO: allow for REINSTANCE (or not, wdgl can't render)
+ if(geo->instData)
+ return;
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geo->instData = header;
+ header->platform = PLATFORM_WDGL;
+ header->vbo = 0;
+ header->ibo = 0;
+ header->numAttribs =
+ pipe->numCustomAttribs + 1 + (geo->numTexCoordSets > 0);
+ if(geo->flags & Geometry::PRELIT)
+ header->numAttribs++;
+ if(geo->flags & Geometry::NORMALS)
+ header->numAttribs++;
+ int32 offset = 0;
+ header->attribs = rwNewT(AttribDesc, header->numAttribs, MEMDUR_EVENT | ID_GEOMETRY);
+
+ AttribDesc *a = header->attribs;
+ // Vertices
+ a->index = 0;
+ a->type = 0;
+ a->normalized = 0;
+ a->size = 3;
+ a->offset = offset;
+ offset += 12;
+ a++;
+ int32 firstCustom = 1;
+
+ // texCoords, only one set here
+ if(geo->numTexCoordSets){
+ a->index = 1;
+ a->type = 3;
+ a->normalized = 0;
+ a->size = 2;
+ a->offset = offset;
+ offset += 4;
+ a++;
+ firstCustom++;
+ }
+
+ if(geo->flags & Geometry::NORMALS){
+ a->index = 2;
+ a->type = 1;
+ a->normalized = 1;
+ a->size = 3;
+ a->offset = offset;
+ offset += 4;
+ a++;
+ firstCustom++;
+ }
+
+ if(geo->flags & Geometry::PRELIT){
+ a->index = 3;
+ a->type = 2;
+ a->normalized = 1;
+ a->size = 4;
+ a->offset = offset;
+ offset += 4;
+ a++;
+ firstCustom++;
+ }
+
+ if(pipe->instanceCB)
+ offset += pipe->instanceCB(geo, firstCustom, offset);
+ else{
+ header->dataSize = offset*geo->numVertices;
+ header->data = rwNewT(uint8, header->dataSize, MEMDUR_EVENT | ID_GEOMETRY);
+ }
+
+ a = header->attribs;
+ for(int32 i = 0; i < header->numAttribs; i++)
+ a[i].stride = offset;
+
+ uint8 *p = header->data + a->offset;
+ V3d *vert = geo->morphTargets->vertices;
+ for(int32 i = 0; i < geo->numVertices; i++){
+ packattrib(p, (float32*)vert, a);
+ vert++;
+ p += a->stride;
+ }
+ a++;
+
+ if(geo->numTexCoordSets){
+ p = header->data + a->offset;
+ TexCoords *texcoord = geo->texCoords[0];
+ for(int32 i = 0; i < geo->numVertices; i++){
+ packattrib(p, (float32*)texcoord, a, 512.0f);
+ texcoord++;
+ p += a->stride;
+ }
+ a++;
+ }
+
+ if(geo->flags & Geometry::NORMALS){
+ p = header->data + a->offset;
+ V3d *norm = geo->morphTargets->normals;
+ for(int32 i = 0; i < geo->numVertices; i++){
+ packattrib(p, (float32*)norm, a);
+ norm++;
+ p += a->stride;
+ }
+ a++;
+ }
+
+ if(geo->flags & Geometry::PRELIT){
+ // TODO: this seems too complicated
+ p = header->data + a->offset;
+ RGBA *color = geo->colors;
+ float32 f[4];
+ for(int32 i = 0; i < geo->numVertices; i++){
+ f[0] = color->red/255.0f;
+ f[1] = color->green/255.0f;
+ f[2] = color->blue/255.0f;
+ f[3] = color->alpha/255.0f;
+ packattrib(p, (float32*)f, a);
+ color++;
+ p += a->stride;
+ }
+ a++;
+ }
+}
+
+static void
+uninstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ if((geo->flags & Geometry::NATIVE) == 0)
+ return;
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_WDGL);
+ geo->numTriangles = geo->meshHeader->guessNumTriangles();
+ geo->allocateData();
+
+ uint8 *p;
+ TexCoords *texcoord = geo->texCoords[0];
+ RGBA *color = geo->colors;
+ V3d *vert = geo->morphTargets->vertices;
+ V3d *norm = geo->morphTargets->normals;
+ float32 f[4];
+
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ for(int i = 0; i < header->numAttribs; i++){
+ AttribDesc *a = &header->attribs[i];
+ p = header->data + a->offset;
+
+ switch(a->index){
+ case 0: // Vertices
+ for(int32 i = 0; i < geo->numVertices; i++){
+ unpackattrib((float32*)vert, p, a);
+ vert++;
+ p += a->stride;
+ }
+ break;
+
+ case 1: // texCoords
+ for(int32 i = 0; i < geo->numVertices; i++){
+ unpackattrib((float32*)texcoord, p, a, 512.0f);
+ texcoord++;
+ p += a->stride;
+ }
+ break;
+
+ case 2: // normals
+ for(int32 i = 0; i < geo->numVertices; i++){
+ unpackattrib((float32*)norm, p, a);
+ norm++;
+ p += a->stride;
+ }
+ break;
+
+ case 3: // colors
+ for(int32 i = 0; i < geo->numVertices; i++){
+ // TODO: this seems too complicated
+ unpackattrib(f, p, a);
+ color->red = f[0]*255.0f;
+ color->green = f[1]*255.0f;
+ color->blue = f[2]*255.0f;
+ color->alpha = f[3]*255.0f;
+ color++;
+ p += a->stride;
+ }
+ break;
+ }
+ }
+
+ if(pipe->uninstanceCB)
+ pipe->uninstanceCB(geo);
+
+ geo->generateTriangles();
+
+ geo->flags &= ~Geometry::NATIVE;
+ destroyNativeData(geo, 0, 0);
+}
+
+void
+ObjPipeline::init(void)
+{
+ this->rw::ObjPipeline::init(PLATFORM_GL3);
+ this->numCustomAttribs = 0;
+ this->impl.instance = wdgl::instance;
+ this->impl.uninstance = wdgl::uninstance;
+ this->instanceCB = nil;
+ this->uninstanceCB = nil;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+ObjPipeline*
+makeDefaultPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ return pipe;
+}
+
+// Skin
+
+Stream*
+readNativeSkin(Stream *stream, int32, void *object, int32 offset)
+{
+ Geometry *geometry = (Geometry*)object;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_GL){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
+ *PLUGINOFFSET(Skin*, geometry, offset) = skin;
+
+ int32 numBones = stream->readI32();
+ skin->init(numBones, 0, 0);
+ stream->read32(skin->inverseMatrices, skin->numBones*64);
+ return stream;
+}
+
+Stream*
+writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset)
+{
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ stream->writeU32(PLATFORM_GL);
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ stream->writeI32(skin->numBones);
+ stream->write32(skin->inverseMatrices, skin->numBones*64);
+ return stream;
+}
+
+int32
+getSizeNativeSkin(void *object, int32 offset)
+{
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ if(skin == nil)
+ return -1;
+ int32 size = 12 + 4 + 4 + skin->numBones*64;
+ return size;
+}
+
+uint32
+skinInstanceCB(Geometry *g, int32 i, uint32 offset)
+{
+ InstanceDataHeader *header = (InstanceDataHeader*)g->instData;
+ AttribDesc *a = &header->attribs[i];
+ // weights
+ a->index = 4;
+ a->type = 2; /* but also short o_O */
+ a->normalized = 1;
+ a->size = 4;
+ a->offset = offset;
+ offset += 4;
+ a++;
+
+ // indices
+ a->index = 5;
+ a->type = 2;
+ a->normalized = 0;
+ a->size = 4;
+ a->offset = offset;
+ offset += 4;
+
+ header->dataSize = offset*g->numVertices;
+ header->data = rwNewT(uint8, header->dataSize, MEMDUR_EVENT | ID_GEOMETRY);
+
+ Skin *skin = Skin::get(g);
+ if(skin == nil)
+ return 8;
+
+ a = &header->attribs[i];
+ uint8 *wgt = header->data + a[0].offset;
+ uint8 *idx = header->data + a[1].offset;
+ uint8 *indices = skin->indices;
+ float32 *weights = skin->weights;
+ for(int32 i = 0; i < g->numVertices; i++){
+ packattrib(wgt, weights, a);
+ weights += 4;
+ wgt += offset;
+ idx[0] = *indices++;
+ idx[1] = *indices++;
+ idx[2] = *indices++;
+ idx[3] = *indices++;
+ idx += offset;
+ }
+
+ return 8;
+}
+
+void
+skinUninstanceCB(Geometry *geo)
+{
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+
+ Skin *skin = Skin::get(geo);
+ if(skin == nil)
+ return;
+
+ uint8 *data = skin->data;
+ float *invMats = skin->inverseMatrices;
+ skin->init(skin->numBones, skin->numBones, geo->numVertices);
+ memcpy(skin->inverseMatrices, invMats, skin->numBones*64);
+ rwFree(data);
+
+ uint8 *p;
+ float *weights = skin->weights;
+ uint8 *indices = skin->indices;
+ for(int i = 0; i < header->numAttribs; i++){
+ AttribDesc *a = &header->attribs[i];
+ p = header->data + a->offset;
+
+ switch(a->index){
+ case 4: // weights
+ for(int32 i = 0; i < geo->numVertices; i++){
+ unpackattrib(weights, p, a);
+float sum = weights[0] + weights[1] + weights[2] + weights[3];
+if(sum){
+ weights[0] /= sum;
+ weights[1] /= sum;
+ weights[2] /= sum;
+ weights[3] /= sum;
+}
+ weights += 4;
+ p += a->stride;
+ }
+ break;
+
+ case 5: // indices
+ for(int32 i = 0; i < geo->numVertices; i++){
+ *indices++ = p[0];
+ *indices++ = p[1];
+ *indices++ = p[2];
+ *indices++ = p[3];
+ p += a->stride;
+ }
+ break;
+ }
+ }
+
+ skin->findNumWeights(geo->numVertices);
+ skin->findUsedBones(geo->numVertices);
+}
+
+// Skin
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+ skinGlobals.pipelines[PLATFORM_WDGL] = makeSkinPipeline();
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_WDGL])->destroy();
+ skinGlobals.pipelines[PLATFORM_WDGL] = nil;
+ return o;
+}
+
+void
+initSkin(void)
+{
+ Driver::registerPlugin(PLATFORM_WDGL, 0, ID_SKIN,
+ skinOpen, skinClose);
+}
+
+ObjPipeline*
+makeSkinPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->pluginID = ID_SKIN;
+ pipe->pluginData = 1;
+ pipe->numCustomAttribs = 2;
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = skinUninstanceCB;
+ return pipe;
+}
+
+// MatFX
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+ matFXGlobals.pipelines[PLATFORM_WDGL] = makeMatFXPipeline();
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_WDGL])->destroy();
+ matFXGlobals.pipelines[PLATFORM_WDGL] = nil;
+ return o;
+}
+
+void
+initMatFX(void)
+{
+ Driver::registerPlugin(PLATFORM_WDGL, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+}
+
+ObjPipeline*
+makeMatFXPipeline(void)
+{
+ ObjPipeline *pipe = ObjPipeline::create();
+ pipe->pluginID = ID_MATFX;
+ pipe->pluginData = 0;
+ return pipe;
+}
+
+// Raster
+
+int32 nativeRasterOffset;
+
+#ifdef RW_OPENGL
+struct GlRaster {
+ GLuint id;
+};
+
+static void*
+createNativeRaster(void *object, int32 offset, int32)
+{
+ GlRaster *raster = PLUGINOFFSET(GlRaster, object, offset);
+ raster->id = 0;
+ return object;
+}
+
+static void*
+destroyNativeRaster(void *object, int32 offset, int32)
+{
+ // TODO:
+ return object;
+}
+
+static void*
+copyNativeRaster(void *dst, void *, int32 offset, int32)
+{
+ GlRaster *raster = PLUGINOFFSET(GlRaster, dst, offset);
+ raster->id = 0;
+ return dst;
+}
+
+void
+registerNativeRaster(void)
+{
+ nativeRasterOffset = Raster::registerPlugin(sizeof(GlRaster),
+ ID_RASTERWDGL,
+ createNativeRaster,
+ destroyNativeRaster,
+ copyNativeRaster);
+}
+
+void
+Texture::upload(void)
+{
+ GLuint id;
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ Raster *r = this->raster;
+ if(r->palette){
+ printf("can't upload paletted raster\n");
+ return;
+ }
+
+ static GLenum filter[] = {
+ 0, GL_NEAREST, GL_LINEAR,
+ GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST,
+ GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR
+ };
+ static GLenum filternomip[] = {
+ 0, GL_NEAREST, GL_LINEAR,
+ GL_NEAREST, GL_LINEAR,
+ GL_NEAREST, GL_LINEAR
+ };
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ filternomip[this->filterAddressing & 0xFF]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ filternomip[this->filterAddressing & 0xFF]);
+
+ static GLenum wrap[] = {
+ 0, GL_REPEAT, GL_MIRRORED_REPEAT,
+ GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER
+ };
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ wrap[(this->filterAddressing >> 8) & 0xF]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+ wrap[(this->filterAddressing >> 12) & 0xF]);
+
+ switch(r->format & 0xF00){
+ case Raster::C8888:
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, r->width, r->height,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, r->pixels);
+ break;
+ default:
+ printf("unsupported raster format: %x\n", r->format);
+ break;
+ }
+ glBindTexture(GL_TEXTURE_2D, 0);
+ GlRaster *glr = PLUGINOFFSET(GlRaster, r, nativeRasterOffset);
+ glr->id = id;
+}
+
+void
+Texture::bind(int n)
+{
+ Raster *r = this->raster;
+ GlRaster *glr = PLUGINOFFSET(GlRaster, r, nativeRasterOffset);
+ glActiveTexture(GL_TEXTURE0+n);
+ if(r){
+ if(glr->id == 0)
+ this->upload();
+ glBindTexture(GL_TEXTURE_2D, glr->id);
+ }else
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glActiveTexture(GL_TEXTURE0);
+
+}
+#endif
+
+}
+}
diff --git a/src/hanim.cpp b/src/hanim.cpp
new file mode 100644
index 0000000..6bc7c5e
--- /dev/null
+++ b/src/hanim.cpp
@@ -0,0 +1,414 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwanim.h"
+#include "rwplugins.h"
+#include "ps2/rwps2.h"
+#include "ps2/rwps2plg.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+#include "gl/rwwdgl.h"
+#include "gl/rwgl3.h"
+
+#define PLUGIN_ID ID_HANIM
+
+namespace rw {
+
+int32 hAnimOffset;
+bool32 hAnimDoStream = 1;
+
+HAnimHierarchy*
+HAnimHierarchy::create(int32 numNodes, int32 *nodeFlags, int32 *nodeIDs,
+ int32 flags, int32 maxKeySize)
+{
+ HAnimHierarchy *hier = (HAnimHierarchy*)rwMalloc(sizeof(*hier), MEMDUR_EVENT | ID_HANIM);
+ if(hier == nil){
+ RWERROR((ERR_ALLOC, sizeof(*hier)));
+ return nil;
+ }
+ hier->interpolator = AnimInterpolator::create(numNodes, maxKeySize);
+
+ hier->numNodes = numNodes;
+ hier->flags = flags;
+ hier->parentFrame = nil;
+ hier->parentHierarchy = hier;
+ if(hier->flags & NOMATRICES){
+ hier->matrices = nil;
+ hier->matricesUnaligned = nil;
+ }else{
+ hier->matricesUnaligned = rwNew(hier->numNodes*64 + 0xF, MEMDUR_EVENT | ID_HANIM);
+ hier->matrices =
+ (Matrix*)(((uintptr)hier->matricesUnaligned + 0xF) & ~0xF);
+ }
+ hier->nodeInfo = rwNewT(HAnimNodeInfo, hier->numNodes, MEMDUR_EVENT | ID_HANIM);
+ for(int32 i = 0; i < hier->numNodes; i++){
+ if(nodeIDs)
+ hier->nodeInfo[i].id = nodeIDs[i];
+ else
+ hier->nodeInfo[i].id = 0;
+ hier->nodeInfo[i].index = i;
+ if(nodeFlags)
+ hier->nodeInfo[i].flags = nodeFlags[i];
+ else
+ hier->nodeInfo[i].flags = 0;
+ hier->nodeInfo[i].frame = nil;
+ }
+ return hier;
+}
+
+void
+HAnimHierarchy::destroy(void)
+{
+ this->interpolator->destroy();
+ rwFree(this->matricesUnaligned);
+ rwFree(this->nodeInfo);
+ rwFree(this);
+}
+
+static Frame*
+findById(Frame *f, int32 id)
+{
+ if(f == nil) return nil;
+ HAnimData *hanim = HAnimData::get(f);
+ if(hanim->id >= 0 && hanim->id == id) return f;
+ Frame *ff = findById(f->next, id);
+ if(ff) return ff;
+ return findById(f->child, id);
+}
+
+static Frame*
+findUnattachedById(HAnimHierarchy *hier, Frame *f, int32 id)
+{
+ if(f == nil) return nil;
+ HAnimData *hanim = HAnimData::get(f);
+ if(hanim->id >= 0 && hanim->id == id && hier->getIndex(f) == -1) return f;
+ Frame *ff = findUnattachedById(hier, f->next, id);
+ if(ff) return ff;
+ return findUnattachedById(hier, f->child, id);
+}
+
+void
+HAnimHierarchy::attachByIndex(int32 idx)
+{
+ int32 id = this->nodeInfo[idx].id;
+// Frame *f = findById(this->parentFrame, id);
+ Frame *f = findUnattachedById(this, this->parentFrame, id);
+ if(f)
+ this->nodeInfo[idx].frame = f;
+}
+
+void
+HAnimHierarchy::attach(void)
+{
+ for(int32 i = 0; i < this->numNodes; i++)
+ this->attachByIndex(i);
+}
+
+int32
+HAnimHierarchy::getIndex(int32 id)
+{
+ for(int32 i = 0; i < this->numNodes; i++)
+ if(this->nodeInfo[i].id == id)
+ return i;
+ return -1;
+}
+
+int32
+HAnimHierarchy::getIndex(Frame *f)
+{
+ for(int32 i = 0; i < this->numNodes; i++)
+ if(this->nodeInfo[i].frame == f)
+ return i;
+ return -1;
+}
+
+HAnimHierarchy*
+HAnimHierarchy::get(Frame *f)
+{
+ return HAnimData::get(f)->hierarchy;
+}
+
+HAnimHierarchy*
+HAnimHierarchy::find(Frame *f)
+{
+ if(f == nil) return nil;
+ HAnimHierarchy *hier = HAnimHierarchy::get(f);
+ if(hier) return hier;
+ hier = HAnimHierarchy::find(f->next);
+ if(hier) return hier;
+ return HAnimHierarchy::find(f->child);
+}
+
+void
+HAnimHierarchy::updateMatrices(void)
+{
+ // TODO: handle more (all!) cases
+
+ Matrix rootMat, animMat;
+ Matrix *curMat, *parentMat;
+ Matrix **sp, *stack[64];
+ Frame *frm, *parfrm;
+ int32 i;
+ AnimInterpolator *anim = this->interpolator;
+
+ sp = stack;
+ curMat = this->matrices;
+
+ frm = this->parentFrame;
+ if(frm && (parfrm = frm->getParent()) && !(this->flags&LOCALSPACEMATRICES))
+ rootMat = *parfrm->getLTM();
+ else
+ rootMat.setIdentity();
+ parentMat = &rootMat;
+ *sp++ = parentMat;
+ HAnimNodeInfo *node = this->nodeInfo;
+ for(i = 0; i < this->numNodes; i++){
+ anim->applyCB(&animMat, anim->getInterpFrame(i));
+
+ // TODO: here we could update local matrices
+
+ Matrix::mult(curMat, &animMat, parentMat);
+
+ // TODO: here we could update LTM
+
+ if(node->flags & PUSH)
+ *sp++ = parentMat;
+ parentMat = curMat;
+ if(node->flags & POP)
+ parentMat = *--sp;
+ assert(sp >= stack);
+ assert(sp <= &stack[64]);
+
+ node++;
+ curMat++;
+ }
+}
+
+HAnimData*
+HAnimData::get(Frame *f)
+{
+ return PLUGINOFFSET(HAnimData, f, hAnimOffset);
+}
+
+static void*
+createHAnim(void *object, int32 offset, int32)
+{
+ HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
+ hanim->id = -1;
+ hanim->hierarchy = nil;
+ return object;
+}
+
+static void*
+destroyHAnim(void *object, int32 offset, int32)
+{
+ int i;
+ HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
+ if(hanim->hierarchy){
+ for(i = 0; i < hanim->hierarchy->numNodes; i++)
+ hanim->hierarchy->nodeInfo[i].frame = nil;
+ if(object == hanim->hierarchy->parentFrame)
+ hanim->hierarchy->destroy();
+ }
+ hanim->id = -1;
+ hanim->hierarchy = nil;
+ return object;
+}
+
+static void*
+copyHAnim(void *dst, void *src, int32 offset, int32)
+{
+ int i;
+ HAnimData *dsthanim = PLUGINOFFSET(HAnimData, dst, offset);
+ HAnimData *srchanim = PLUGINOFFSET(HAnimData, src, offset);
+ HAnimHierarchy *srchier, *dsthier;
+ dsthanim->id = srchanim->id;
+ dsthanim->hierarchy = nil;
+ srchier = srchanim->hierarchy;
+ if(srchier && !(srchier->flags & HAnimHierarchy::SUBHIERARCHY)){
+ dsthier = HAnimHierarchy::create(srchier->numNodes, nil, nil, srchier->flags, srchier->interpolator->maxInterpKeyFrameSize);
+ for(i = 0; i < dsthier->numNodes; i++){
+ dsthier->nodeInfo[i].frame = nil;
+ dsthier->nodeInfo[i].flags = srchier->nodeInfo[i].flags;
+ dsthier->nodeInfo[i].index = srchier->nodeInfo[i].index;
+ dsthier->nodeInfo[i].id = srchier->nodeInfo[i].id;
+ }
+ dsthanim->hierarchy = dsthier;
+ dsthier->parentFrame = (Frame*)dst;
+ }
+ return dst;
+}
+
+static Stream*
+readHAnim(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ int32 ver, numNodes;
+ HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
+ ver = stream->readI32();
+ assert(ver == 0x100);
+ hanim->id = stream->readI32();
+ numNodes = stream->readI32();
+ if(numNodes != 0){
+ int32 flags = stream->readI32();
+ int32 maxKeySize = stream->readI32();
+ // Sizes are fucked for 64 bit pointers but
+ // AnimInterpolator::create() will take care of that
+ int32 *nodeFlags = rwNewT(int32, numNodes,
+ MEMDUR_FUNCTION | ID_HANIM);
+ int32 *nodeIDs = rwNewT(int32, numNodes,
+ MEMDUR_FUNCTION | ID_HANIM);
+ for(int32 i = 0; i < numNodes; i++){
+ nodeIDs[i] = stream->readI32();
+ stream->readI32(); // index...unused
+ nodeFlags[i] = stream->readI32();
+ }
+ hanim->hierarchy = HAnimHierarchy::create(numNodes,
+ nodeFlags, nodeIDs, flags, maxKeySize);
+ hanim->hierarchy->parentFrame = (Frame*)object;
+ rwFree(nodeFlags);
+ rwFree(nodeIDs);
+ }
+ return stream;
+}
+
+static Stream*
+writeHAnim(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
+ stream->writeI32(256);
+ stream->writeI32(hanim->id);
+ if(hanim->hierarchy == nil){
+ stream->writeI32(0);
+ return stream;
+ }
+ HAnimHierarchy *hier = hanim->hierarchy;
+ stream->writeI32(hier->numNodes);
+ stream->writeI32(hier->flags);
+ stream->writeI32(hier->interpolator->maxInterpKeyFrameSize);
+ for(int32 i = 0; i < hier->numNodes; i++){
+ stream->writeI32(hier->nodeInfo[i].id);
+ stream->writeI32(hier->nodeInfo[i].index);
+ stream->writeI32(hier->nodeInfo[i].flags);
+ }
+ return stream;
+}
+
+static int32
+getSizeHAnim(void *object, int32 offset, int32)
+{
+ HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
+ if(!hAnimDoStream ||
+ (version >= 0x35000 && hanim->id == -1 && hanim->hierarchy == nil))
+ return 0;
+ if(hanim->hierarchy)
+ return 12 + 8 + hanim->hierarchy->numNodes*12;
+ return 12;
+}
+
+static void
+hAnimFrameRead(Stream *stream, Animation *anim)
+{
+ HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes;
+ for(int32 i = 0; i < anim->numFrames; i++){
+ frames[i].time = stream->readF32();
+ stream->read32(&frames[i].q, 4*4);
+ stream->read32(&frames[i].t, 3*4);
+ int32 prev = stream->readI32()/0x24;
+ frames[i].prev = &frames[prev];
+ }
+}
+
+static void
+hAnimFrameWrite(Stream *stream, Animation *anim)
+{
+ HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes;
+ for(int32 i = 0; i < anim->numFrames; i++){
+ stream->writeF32(frames[i].time);
+ stream->write32(&frames[i].q, 4*4);
+ stream->write32(&frames[i].t, 3*4);
+ stream->writeI32((frames[i].prev - frames)*0x24);
+ }
+}
+
+static uint32
+hAnimFrameGetSize(Animation *anim)
+{
+ return anim->numFrames*(4 + 4*4 + 3*4 + 4);
+}
+
+//void hanimBlendCB(void *out, void *in1, void *in2, float32 a);
+//void hanimAddCB(void *out, void *in1, void *in2);
+//void hanimMulRecipCB(void *frame, void *start);
+
+static void
+hanimApplyCB(void *result, void *frame)
+{
+ Matrix *m = (Matrix*)result;
+ HAnimInterpFrame *f = (HAnimInterpFrame*)frame;
+ m->rotate(f->q, COMBINEREPLACE);
+ m->pos = f->t;
+}
+
+static void
+hanimInterpCB(void *vout, void *vin1, void *vin2, float32 t, void*)
+{
+ HAnimInterpFrame *out = (HAnimInterpFrame*)vout;
+ HAnimKeyFrame *in1 = (HAnimKeyFrame*)vin1;
+ HAnimKeyFrame *in2 = (HAnimKeyFrame*)vin2;
+assert(t >= in1->time && t <= in2->time);
+ float32 a = (t - in1->time)/(in2->time - in1->time);
+ out->t = lerp(in1->t, in2->t, a);
+ out->q = slerp(in1->q, in2->q, a);
+}
+
+static void*
+hanimOpen(void *object, int32 offset, int32 size)
+{
+ AnimInterpolatorInfo *info = rwNewT(AnimInterpolatorInfo, 1, MEMDUR_GLOBAL | ID_HANIM);
+ info->id = 1;
+ info->interpKeyFrameSize = sizeof(HAnimInterpFrame);
+ info->animKeyFrameSize = sizeof(HAnimKeyFrame);
+ info->customDataSize = 0;
+ info->applyCB = hanimApplyCB;
+ info->blendCB = nil;
+ info->interpCB = hanimInterpCB;
+ info->addCB = nil;
+ info->mulRecipCB = nil;
+ info->streamRead = hAnimFrameRead;
+ info->streamWrite = hAnimFrameWrite;
+ info->streamGetSize = hAnimFrameGetSize;
+ AnimInterpolatorInfo::registerInterp(info);
+ return object;
+}
+
+static void*
+hanimClose(void *object, int32 offset, int32 size)
+{
+ AnimInterpolatorInfo::unregisterInterp(AnimInterpolatorInfo::find(1));
+ return object;
+}
+
+
+void
+registerHAnimPlugin(void)
+{
+ Engine::registerPlugin(0, ID_HANIM, hanimOpen, hanimClose);
+ hAnimOffset = Frame::registerPlugin(sizeof(HAnimData), ID_HANIM,
+ createHAnim,
+ destroyHAnim, copyHAnim);
+ Frame::registerPluginStream(ID_HANIM,
+ readHAnim,
+ writeHAnim,
+ getSizeHAnim);
+}
+
+}
diff --git a/src/image.cpp b/src/image.cpp
new file mode 100644
index 0000000..23877b8
--- /dev/null
+++ b/src/image.cpp
@@ -0,0 +1,1324 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "ps2/rwps2.h"
+#include "d3d/rwd3d.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+
+#define PLUGIN_ID ID_IMAGE
+
+namespace rw {
+
+int32 Image::numAllocated;
+
+struct FileAssociation
+{
+ char *extension;
+ Image *(*read)(const char *afilename);
+ void (*write)(Image *image, const char *filename);
+};
+
+struct ImageGlobals
+{
+ char *searchPaths;
+ int numSearchPaths;
+ FileAssociation fileFormats[10];
+ int numFileFormats;
+};
+int32 imageModuleOffset;
+
+#define IMAGEGLOBAL(v) (PLUGINOFFSET(ImageGlobals, engine, imageModuleOffset)->v)
+
+// Image formats are as follows:
+// 32 bit has 4 bytes: 8888 RGBA
+// 24 bit has 3 bytes: 888 RGB
+// 16 bit has 2 bytes: 1555 ARGB stored in platform native order (TODO?)
+// palettes always have 4 bytes: r, g, b, a
+// 8 bit has 1 byte: x
+// 4 bit has 1 byte per two pixels: 0xLR, where L and R are the left and right pixel resp.
+
+Image*
+Image::create(int32 width, int32 height, int32 depth)
+{
+ Image *img = (Image*)rwMalloc(sizeof(Image), MEMDUR_EVENT | ID_IMAGE);
+ if(img == nil){
+ RWERROR((ERR_ALLOC, sizeof(Image)));
+ return nil;
+ }
+ numAllocated++;
+ img->flags = 0;
+ img->width = width;
+ img->height = height;
+ img->depth = depth;
+ img->bpp = depth < 8 ? 1 : depth/8;
+ img->stride = 0;
+ img->pixels = nil;
+ img->palette = nil;
+ return img;
+}
+
+void
+Image::destroy(void)
+{
+ this->free();
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+Image::allocate(void)
+{
+ if(this->pixels == nil){
+ this->stride = this->width*this->bpp;
+ this->pixels = rwNewT(uint8, this->stride*this->height, MEMDUR_EVENT | ID_IMAGE);
+ this->flags |= 1;
+ }
+ if(this->palette == nil){
+ if(this->depth == 4 || this->depth == 8)
+ this->palette = rwNewT(uint8, (1 << this->depth)*4, MEMDUR_EVENT | ID_IMAGE);
+ this->flags |= 2;
+ }
+}
+
+void
+Image::free(void)
+{
+ if(this->flags&1){
+ rwFree(this->pixels);
+ this->pixels = nil;
+ }
+ if(this->flags&2){
+ rwFree(this->palette);
+ this->palette = nil;
+ }
+ this->flags = 0;
+}
+
+void
+Image::setPixels(uint8 *pixels)
+{
+ this->pixels = pixels;
+ this->flags |= 1;
+}
+
+void
+decompressDXT1(uint8 *adst, int32 w, int32 h, uint8 *src)
+{
+ /* j loops through old texels
+ * x and y loop through new texels */
+ int32 x = 0, y = 0;
+ uint32 c[4][4];
+ uint8 idx[16];
+ uint8 (*dst)[4] = (uint8(*)[4])adst;
+ for(int32 j = 0; j < w*h/2; j += 8){
+ /* calculate colors */
+ uint32 col0 = *((uint16*)&src[j+0]);
+ uint32 col1 = *((uint16*)&src[j+2]);
+ c[0][0] = ((col0>>11) & 0x1F)*0xFF/0x1F;
+ c[0][1] = ((col0>> 5) & 0x3F)*0xFF/0x3F;
+ c[0][2] = ( col0 & 0x1F)*0xFF/0x1F;
+ c[0][3] = 0xFF;
+
+ c[1][0] = ((col1>>11) & 0x1F)*0xFF/0x1F;
+ c[1][1] = ((col1>> 5) & 0x3F)*0xFF/0x3F;
+ c[1][2] = ( col1 & 0x1F)*0xFF/0x1F;
+ c[1][3] = 0xFF;
+ if(col0 > col1){
+ c[2][0] = (2*c[0][0] + 1*c[1][0])/3;
+ c[2][1] = (2*c[0][1] + 1*c[1][1])/3;
+ c[2][2] = (2*c[0][2] + 1*c[1][2])/3;
+ c[2][3] = 0xFF;
+
+ c[3][0] = (1*c[0][0] + 2*c[1][0])/3;
+ c[3][1] = (1*c[0][1] + 2*c[1][1])/3;
+ c[3][2] = (1*c[0][2] + 2*c[1][2])/3;
+ c[3][3] = 0xFF;
+ }else{
+ c[2][0] = (c[0][0] + c[1][0])/2;
+ c[2][1] = (c[0][1] + c[1][1])/2;
+ c[2][2] = (c[0][2] + c[1][2])/2;
+ c[2][3] = 0xFF;
+
+ c[3][0] = 0x00;
+ c[3][1] = 0x00;
+ c[3][2] = 0x00;
+ c[3][3] = 0x00;
+ }
+
+ /* make index list */
+ uint32 indices = *((uint32*)&src[j+4]);
+ for(int32 k = 0; k < 16; k++){
+ idx[k] = indices & 0x3;
+ indices >>= 2;
+ }
+
+ /* write bytes */
+ for(uint32 l = 0; l < 4; l++)
+ for(uint32 k = 0; k < 4; k++){
+ dst[(y+l)*w + x+k][0] = c[idx[l*4+k]][0];
+ dst[(y+l)*w + x+k][1] = c[idx[l*4+k]][1];
+ dst[(y+l)*w + x+k][2] = c[idx[l*4+k]][2];
+ dst[(y+l)*w + x+k][3] = c[idx[l*4+k]][3];
+ }
+ x += 4;
+ if(x >= w){
+ y += 4;
+ x = 0;
+ }
+ }
+}
+
+void
+decompressDXT3(uint8 *adst, int32 w, int32 h, uint8 *src)
+{
+ /* j loops through old texels
+ * x and y loop through new texels */
+ int32 x = 0, y = 0;
+ uint32 c[4][4];
+ uint8 idx[16];
+ uint8 a[16];
+ uint8 (*dst)[4] = (uint8(*)[4])adst;
+ for(int32 j = 0; j < w*h; j += 16){
+ /* calculate colors */
+ uint32 col0 = *((uint16*)&src[j+8]);
+ uint32 col1 = *((uint16*)&src[j+10]);
+ c[0][0] = ((col0>>11) & 0x1F)*0xFF/0x1F;
+ c[0][1] = ((col0>> 5) & 0x3F)*0xFF/0x3F;
+ c[0][2] = ( col0 & 0x1F)*0xFF/0x1F;
+
+ c[1][0] = ((col1>>11) & 0x1F)*0xFF/0x1F;
+ c[1][1] = ((col1>> 5) & 0x3F)*0xFF/0x3F;
+ c[1][2] = ( col1 & 0x1F)*0xFF/0x1F;
+
+ c[2][0] = (2*c[0][0] + 1*c[1][0])/3;
+ c[2][1] = (2*c[0][1] + 1*c[1][1])/3;
+ c[2][2] = (2*c[0][2] + 1*c[1][2])/3;
+
+ c[3][0] = (1*c[0][0] + 2*c[1][0])/3;
+ c[3][1] = (1*c[0][1] + 2*c[1][1])/3;
+ c[3][2] = (1*c[0][2] + 2*c[1][2])/3;
+
+ /* make index list */
+ uint32 indices = *((uint32*)&src[j+12]);
+ for(int32 k = 0; k < 16; k++){
+ idx[k] = indices & 0x3;
+ indices >>= 2;
+ }
+ uint64 alphas = *((uint64*)&src[j+0]);
+ for(int32 k = 0; k < 16; k++){
+ a[k] = (alphas & 0xF)*17;
+ alphas >>= 4;
+ }
+
+ /* write bytes */
+ for(uint32 l = 0; l < 4; l++)
+ for(uint32 k = 0; k < 4; k++){
+ dst[(y+l)*w + x+k][0] = c[idx[l*4+k]][0];
+ dst[(y+l)*w + x+k][1] = c[idx[l*4+k]][1];
+ dst[(y+l)*w + x+k][2] = c[idx[l*4+k]][2];
+ dst[(y+l)*w + x+k][3] = a[l*4+k];
+ }
+ x += 4;
+ if(x >= w){
+ y += 4;
+ x = 0;
+ }
+ }
+}
+
+void
+decompressDXT5(uint8 *adst, int32 w, int32 h, uint8 *src)
+{
+ /* j loops through old texels
+ * x and y loop through new texels */
+ int32 x = 0, y = 0;
+ uint32 c[4][4];
+ uint32 a[8];
+ uint8 idx[16];
+ uint8 aidx[16];
+ uint8 (*dst)[4] = (uint8(*)[4])adst;
+ for(int32 j = 0; j < w*h; j += 16){
+ /* calculate colors */
+ uint32 col0 = *((uint16*)&src[j+8]);
+ uint32 col1 = *((uint16*)&src[j+10]);
+ c[0][0] = ((col0>>11) & 0x1F)*0xFF/0x1F;
+ c[0][1] = ((col0>> 5) & 0x3F)*0xFF/0x3F;
+ c[0][2] = ( col0 & 0x1F)*0xFF/0x1F;
+
+ c[1][0] = ((col1>>11) & 0x1F)*0xFF/0x1F;
+ c[1][1] = ((col1>> 5) & 0x3F)*0xFF/0x3F;
+ c[1][2] = ( col1 & 0x1F)*0xFF/0x1F;
+ if(col0 > col1){
+ c[2][0] = (2*c[0][0] + 1*c[1][0])/3;
+ c[2][1] = (2*c[0][1] + 1*c[1][1])/3;
+ c[2][2] = (2*c[0][2] + 1*c[1][2])/3;
+
+ c[3][0] = (1*c[0][0] + 2*c[1][0])/3;
+ c[3][1] = (1*c[0][1] + 2*c[1][1])/3;
+ c[3][2] = (1*c[0][2] + 2*c[1][2])/3;
+ }else{
+ c[2][0] = (c[0][0] + c[1][0])/2;
+ c[2][1] = (c[0][1] + c[1][1])/2;
+ c[2][2] = (c[0][2] + c[1][2])/2;
+
+ c[3][0] = 0x00;
+ c[3][1] = 0x00;
+ c[3][2] = 0x00;
+ }
+
+ a[0] = src[j+0];
+ a[1] = src[j+1];
+ if(a[0] > a[1]){
+ a[2] = (6*a[0] + 1*a[1])/7;
+ a[3] = (5*a[0] + 2*a[1])/7;
+ a[4] = (4*a[0] + 3*a[1])/7;
+ a[5] = (3*a[0] + 4*a[1])/7;
+ a[6] = (2*a[0] + 5*a[1])/7;
+ a[7] = (1*a[0] + 6*a[1])/7;
+ }else{
+ a[2] = (4*a[0] + 1*a[1])/5;
+ a[3] = (3*a[0] + 2*a[1])/5;
+ a[4] = (2*a[0] + 3*a[1])/5;
+ a[5] = (1*a[0] + 4*a[1])/5;
+ a[6] = 0;
+ a[7] = 0xFF;
+ }
+
+ /* make index list */
+ uint32 indices = *((uint32*)&src[j+12]);
+ for(int32 k = 0; k < 16; k++){
+ idx[k] = indices & 0x3;
+ indices >>= 2;
+ }
+ // only 6 indices
+ uint64 alphas = *((uint64*)&src[j+2]);
+ for(int32 k = 0; k < 16; k++){
+ aidx[k] = alphas & 0x7;
+ alphas >>= 3;
+ }
+
+ /* write bytes */
+ for(uint32 l = 0; l < 4; l++)
+ for(uint32 k = 0; k < 4; k++){
+ dst[(y+l)*w + x+k][0] = c[idx[l*4+k]][0];
+ dst[(y+l)*w + x+k][1] = c[idx[l*4+k]][1];
+ dst[(y+l)*w + x+k][2] = c[idx[l*4+k]][2];
+ dst[(y+l)*w + x+k][3] = a[aidx[l*4+k]];
+ }
+ x += 4;
+ if(x >= w){
+ y += 4;
+ x = 0;
+ }
+ }
+}
+
+// not strictly image but related
+
+// flip a DXT 2-bit block
+static void
+flipBlock(uint8 *dst, uint8 *src)
+{
+ // color
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ // bits
+ dst[4] = src[7];
+ dst[5] = src[6];
+ dst[6] = src[5];
+ dst[7] = src[4];
+}
+
+// flip top 2 rows of a DXT 2-bit block
+static void
+flipBlock_half(uint8 *dst, uint8 *src)
+{
+ // color
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ // bits
+ dst[4] = src[5];
+ dst[5] = src[4];
+ dst[6] = src[6];
+ dst[7] = src[7];
+}
+
+// flip a DXT3 4-bit alpha block
+static void
+flipAlphaBlock3(uint8 *dst, uint8 *src)
+{
+ dst[6] = src[0];
+ dst[7] = src[1];
+ dst[4] = src[2];
+ dst[5] = src[3];
+ dst[2] = src[4];
+ dst[3] = src[5];
+ dst[0] = src[6];
+ dst[1] = src[7];
+}
+
+// flip top 2 rows of a DXT3 4-bit alpha block
+static void
+flipAlphaBlock3_half(uint8 *dst, uint8 *src)
+{
+ dst[0] = src[2];
+ dst[1] = src[3];
+ dst[2] = src[0];
+ dst[3] = src[1];
+ dst[4] = src[4];
+ dst[5] = src[5];
+ dst[6] = src[6];
+ dst[7] = src[7];
+}
+
+// flip a DXT5 3-bit alpha block
+static void
+flipAlphaBlock5(uint8 *dst, uint8 *src)
+{
+ // color
+ dst[0] = src[0];
+ dst[1] = src[1];
+ // bits
+ uint64 bits = *(uint64*)&src[2];
+ uint64 flipbits = 0;
+ for(int i = 0; i < 4; i++){
+ flipbits <<= 12;
+ flipbits |= bits & 0xFFF;
+ bits >>= 12;
+ }
+ memcpy(dst+2, &flipbits, 6);
+}
+
+// flip top 2 rows of a DXT5 3-bit alpha block
+static void
+flipAlphaBlock5_half(uint8 *dst, uint8 *src)
+{
+ // color
+ dst[0] = src[0];
+ dst[1] = src[1];
+ // bits
+ uint64 bits = *(uint64*)&src[2];
+ uint64 flipbits = bits & 0xFFFFFF000000;
+ flipbits |= (bits>>12) & 0xFFF;
+ flipbits |= (bits<<12) & 0xFFF000;
+ memcpy(dst+2, &flipbits, 6);
+}
+
+void
+flipDXT1(uint8 *dst, uint8 *src, uint32 width, uint32 height)
+{
+ int x, y;
+ int bw = (width+3)/4;
+ int bh = (height+3)/4;
+ if(height < 4){
+ // used pixels are always at the top
+ // so don't swap the full 4 rows
+ if(height == 2){
+ uint8 *s = src;
+ uint8 *d = dst;
+ for(x = 0; x < bw; x++){
+ flipBlock_half(dst, src);
+ s += 8;
+ d += 8;
+ }
+ }else
+ memcpy(dst, src, 8*bw);
+ return;
+ }
+ dst += 8*bw*bh;
+ for(y = 0; y < bh; y++){
+ dst -= 8*bw;
+ uint8 *s = src;
+ uint8 *d = dst;
+ for(x = 0; x < bw; x++){
+ flipBlock(d, s);
+ s += 8;
+ d += 8;
+ }
+ src += 8*bw;
+ }
+}
+
+void
+flipDXT3(uint8 *dst, uint8 *src, uint32 width, uint32 height)
+{
+ int x, y;
+ int bw = (width+3)/4;
+ int bh = (height+3)/4;
+ if(height < 4){
+ // used pixels are always at the top
+ // so don't swap the full 4 rows
+ if(height == 2){
+ uint8 *s = src;
+ uint8 *d = dst;
+ for(x = 0; x < bw; x++){
+ flipAlphaBlock3_half(d, s);
+ flipBlock_half(d+8, s+8);
+ s += 16;
+ d += 16;
+ }
+ }else
+ memcpy(dst, src, 16*bw);
+ return;
+ }
+ dst += 16*bw*bh;
+ for(y = 0; y < bh; y++){
+ dst -= 16*bw;
+ uint8 *s = src;
+ uint8 *d = dst;
+ for(x = 0; x < bw; x++){
+ flipAlphaBlock3(d, s);
+ flipBlock(d+8, s+8);
+ s += 16;
+ d += 16;
+ }
+ src += 16*bw;
+ }
+}
+
+void
+flipDXT5(uint8 *dst, uint8 *src, uint32 width, uint32 height)
+{
+ int x, y;
+ int bw = (width+3)/4;
+ int bh = (height+3)/4;
+ if(height < 4){
+ // used pixels are always at the top
+ // so don't swap the full 4 rows
+ if(height == 2){
+ uint8 *s = src;
+ uint8 *d = dst;
+ for(x = 0; x < bw; x++){
+ flipAlphaBlock5_half(d, s);
+ flipBlock_half(d+8, s+8);
+ s += 16;
+ d += 16;
+ }
+ }else
+ memcpy(dst, src, 16*bw);
+ return;
+ }
+ dst += 16*bw*bh;
+ for(y = 0; y < bh; y++){
+ dst -= 16*bw;
+ uint8 *s = src;
+ uint8 *d = dst;
+ for(x = 0; x < bw; x++){
+ flipAlphaBlock5(d, s);
+ flipBlock(d+8, s+8);
+ s += 16;
+ d += 16;
+ }
+ src += 16*bw;
+ }
+}
+
+void
+flipDXT(int32 type, uint8 *dst, uint8 *src, uint32 width, uint32 height)
+{
+ switch(type){
+ case 1:
+ flipDXT1(dst, src, width, height);
+ break;
+ case 3:
+ flipDXT3(dst, src, width, height);
+ break;
+ case 5:
+ flipDXT5(dst, src, width, height);
+ break;
+ }
+}
+
+void
+Image::setPixelsDXT(int32 type, uint8 *pixels)
+{
+ switch(type){
+ case 1:
+ decompressDXT1(this->pixels, this->width, this->height, pixels);
+ break;
+ case 3:
+ decompressDXT3(this->pixels, this->width, this->height, pixels);
+ break;
+ case 5:
+ decompressDXT5(this->pixels, this->width, this->height, pixels);
+ break;
+ }
+}
+
+void
+Image::setPalette(uint8 *palette)
+{
+ this->palette = palette;
+ this->flags |= 2;
+}
+
+void
+Image::compressPalette(void)
+{
+ if(this->depth != 8)
+ return;
+ uint8 *pixels = this->pixels;
+ for(int y = 0; y < this->height; y++){
+ uint8 *line = pixels;
+ for(int x = 0; x < this->width; x++){
+ if(*line > 0xF) return;
+ line += this->bpp;
+ }
+ pixels += this->stride;
+ }
+ this->depth = 4;
+}
+
+bool32
+Image::hasAlpha(void)
+{
+ uint8 ret = 0xFF;
+ uint8 *pixels = this->pixels;
+ if(this->depth == 32){
+ for(int y = 0; y < this->height; y++){
+ uint8 *line = pixels;
+ for(int x = 0; x < this->width; x++){
+ ret &= line[3];
+ line += this->bpp;
+ }
+ pixels += this->stride;
+ }
+ }else if(this->depth == 24){
+ return 0;
+ }else if(this->depth == 16){
+ for(int y = 0; y < this->height; y++){
+ uint8 *line = pixels;
+ for(int x = 0; x < this->width; x++){
+ ret &= line[1] & 0x80;
+ line += this->bpp;
+ }
+ pixels += this->stride;
+ }
+ return ret != 0x80;
+ }else if(this->depth <= 8){
+ for(int y = 0; y < this->height; y++){
+ uint8 *line = pixels;
+ for(int x = 0; x < this->width; x++){
+ ret &= this->palette[*line*4+3];
+ line += this->bpp;
+ }
+ pixels += this->stride;
+ }
+ }
+ return ret != 0xFF;
+}
+
+void
+Image::convertTo32(void)
+{
+ assert(this->pixels);
+ uint8 *pixels = this->pixels;
+ int32 newstride = this->width*4;
+ uint8 *newpixels;
+
+ void (*fun)(uint8 *out, uint8 *in) = nil;
+ switch(this->depth){
+ case 4:
+ case 8:
+ assert(this->palette);
+ this->unpalettize(true);
+ return;
+ case 16:
+ fun = conv_RGBA8888_from_ARGB1555;
+ break;
+ case 24:
+ fun = conv_RGBA8888_from_RGB888;
+ break;
+ default:
+ return;
+ }
+
+ newpixels = rwNewT(uint8, newstride*this->height, MEMDUR_EVENT | ID_IMAGE);
+ uint8 *pixels32 = newpixels;
+ for(int y = 0; y < this->height; y++){
+ uint8 *line = pixels;
+ uint8 *newline = newpixels;
+ for(int x = 0; x < this->width; x++){
+ fun(newline, line);
+ line += this->bpp;
+ newline += 4;
+ }
+ pixels += this->stride;
+ newpixels += newstride;
+ }
+
+ this->free();
+ this->depth = 32;
+ this->bpp = 4;
+ this->stride = newstride;
+ this->pixels = nil;
+ this->palette = nil;
+ this->setPixels(pixels32);
+}
+
+void
+Image::palettize(int32 depth)
+{
+ RGBA colors[256];
+ ColorQuant quant;
+ uint8 *newpixels;
+ uint32 newstride;
+
+ quant.init();
+ quant.addImage(this);
+ assert(depth <= 8);
+ quant.makePalette(1<<depth, colors);
+
+ newstride = this->width;
+ newpixels = rwNewT(uint8, newstride*this->height, MEMDUR_EVENT | ID_IMAGE);
+ // TODO: maybe do floyd-steinberg dithering?
+ quant.matchImage(newpixels, newstride, this);
+
+ this->free();
+ this->depth = depth;
+ this->bpp = depth < 8 ? 1 : depth/8;
+ this->stride = newstride;
+ this->pixels = nil;
+ this->palette = nil;
+ this->setPixels(newpixels);
+ this->allocate();
+ memcpy(this->palette, colors, 4*(1<<depth));
+
+ quant.destroy();
+}
+
+void
+Image::unpalettize(bool forceAlpha)
+{
+ if(this->depth > 8)
+ return;
+ assert(this->pixels);
+ assert(this->palette);
+
+ int32 ndepth = (forceAlpha || this->hasAlpha()) ? 32 : 24;
+ int32 nstride = this->width*ndepth/8;
+ uint8 *npixels = rwNewT(uint8, nstride*this->height, MEMDUR_EVENT | ID_IMAGE);
+
+ uint8 *line = this->pixels;
+ uint8 *nline = npixels;
+ uint8 *p, *np;
+ for(int32 y = 0; y < this->height; y++){
+ p = line;
+ np = nline;
+ for(int32 x = 0; x < this->width; x++){
+ np[0] = this->palette[*p*4+0];
+ np[1] = this->palette[*p*4+1];
+ np[2] = this->palette[*p*4+2];
+ np += 3;
+ if(ndepth == 32)
+ *np++ = this->palette[*p*4+3];
+ p++;
+ }
+ line += this->stride;
+ nline += nstride;
+ }
+ this->free();
+ this->depth = ndepth;
+ this->bpp = ndepth < 8 ? 1 : ndepth/8;
+ this->stride = nstride;
+ this->setPixels(npixels);
+}
+
+// Copy the biggest channel value to alpha
+void
+Image::makeMask(void)
+{
+ int32 maxcol;
+ switch(this->depth){
+ case 4:
+ case 8: {
+ assert(this->palette);
+ int32 pallen = 1 << this->depth;
+ for(int32 i = 0; i < pallen; i++){
+ maxcol = this->palette[i*4+0];
+ if(this->palette[i*4+1] > maxcol) maxcol = this->palette[i*4+1];
+ if(this->palette[i*4+2] > maxcol) maxcol = this->palette[i*4+2];
+ this->palette[i*4+3] = maxcol;
+ }
+ break;
+ }
+
+ case 16:
+ case 24:
+ this->convertTo32();
+ // fallthrough
+
+ case 32: {
+ assert(this->pixels);
+ uint8 *line = this->pixels;
+ uint8 *p;
+ for(int32 y = 0; y < this->height; y++){
+ p = line;
+ for(int32 x = 0; x < this->width; x++){
+ maxcol = p[0];
+ if(p[1] > maxcol) maxcol = p[1];
+ if(p[2] > maxcol) maxcol = p[2];
+ p[3] = maxcol;
+ p += this->bpp;
+ }
+ line += this->stride;
+ }
+ break;
+ }
+ }
+}
+
+void
+Image::applyMask(Image *mask)
+{
+ if(this->width != mask->width || this->height != mask->height)
+ return; // TODO: set an error
+ // we could use alpha with 16 bits but what's the point?
+ if(mask->depth == 16 || mask->depth == 24)
+ return;
+
+ this->convertTo32();
+ assert(this->depth == 32);
+
+ uint8 *line = this->pixels;
+ uint8 *mline = mask->pixels;
+ uint8 *p, *m;
+ for(int32 y = 0; y < this->height; y++){
+ p = line;
+ m = mline;
+ for(int32 x = 0; x < this->width; x++){
+ if(mask->depth == 32)
+ p[3] = m[3];
+ else if(mask->depth <= 8)
+ p[3] = mask->palette[m[0]*4+3];
+ p += this->bpp;
+ m += mask->bpp;
+ }
+ line += this->stride;
+ mline += mask->stride;
+ }
+}
+
+void
+Image::removeMask(void)
+{
+ if(this->depth <= 8){
+ assert(this->palette);
+ int32 pallen = 4*(1 << this->depth);
+ for(int32 i = 0; i < pallen; i += 4)
+ this->palette[i+3] = 0xFF;
+ return;
+ }
+ if(this->depth == 24)
+ return;
+ assert(this->pixels);
+ uint8 *line = this->pixels;
+ uint8 *p;
+ for(int32 y = 0; y < this->height; y++){
+ p = line;
+ for(int32 x = 0; x < this->width; x++){
+ switch(this->depth){
+ case 16:
+ p[1] |= 0x80;
+ p += 2;
+ break;
+ case 32:
+ p[3] = 0xFF;
+ p += 4;
+ break;
+ }
+ }
+ line += this->stride;
+ }
+}
+
+Image*
+Image::extractMask(void)
+{
+ Image *img = Image::create(this->width, this->height, 8);
+ img->allocate();
+
+ // use an 8bit palette to store all shades of grey
+ for(int32 i = 0; i < 256; i++){
+ img->palette[i*4+0] = i;
+ img->palette[i*4+1] = i;
+ img->palette[i*4+2] = i;
+ img->palette[i*4+3] = 0xFF;
+ }
+
+ // Then use the alpha value as palette index
+ uint8 *line = this->pixels;
+ uint8 *nline = img->pixels;
+ uint8 *p, *np;
+ for(int32 y = 0; y < this->height; y++){
+ p = line;
+ np = nline;
+ for(int32 x = 0; x < this->width; x++){
+ switch(this->depth){
+ case 4:
+ case 8:
+ *np++ = this->palette[*p*4+3];
+ p++;
+ break;
+ case 16:
+ *np++ = 0xFF*!!(p[1]&0x80);
+ p += 2;
+ break;
+ case 24:
+ *np++ = 0xFF;
+ p += 3;
+ break;
+ case 32:
+ *np++ = p[3];
+ p += 4;
+ break;
+ }
+ }
+ line += this->stride;
+ nline += img->stride;
+ }
+ return img;
+}
+
+void
+Image::setSearchPath(const char *path)
+{
+ char *p, *end;
+ ImageGlobals *g = PLUGINOFFSET(ImageGlobals, engine, imageModuleOffset);
+ rwFree(g->searchPaths);
+ g->numSearchPaths = 0;
+ if(path)
+ g->searchPaths = p = rwStrdup(path, MEMDUR_EVENT);
+ else{
+ g->searchPaths = nil;
+ return;
+ }
+ while(p && *p){
+ end = strchr(p, ';');
+ if(end)
+ *end++ = '\0';
+ g->numSearchPaths++;
+ p = end;
+ }
+}
+
+void
+Image::printSearchPath(void)
+{
+ ImageGlobals *g = PLUGINOFFSET(ImageGlobals, engine, imageModuleOffset);
+ char *p = g->searchPaths;
+ for(int i = 0; i < g->numSearchPaths; i++){
+ printf("%s\n", p);
+ p += strlen(p) + 1;
+ }
+}
+
+char*
+Image::getFilename(const char *name)
+{
+ ImageGlobals *g = PLUGINOFFSET(ImageGlobals, engine, imageModuleOffset);
+ void *f;
+ char *s, *p = g->searchPaths;
+ size_t len = strlen(name)+1;
+ if(g->numSearchPaths == 0){
+ s = rwStrdup(name, MEMDUR_EVENT);
+ makePath(s);
+ f = engine->filefuncs.rwfopen(s, "rb");
+ if(f){
+ engine->filefuncs.rwfclose(f);
+// printf("found %s\n", s);
+ return s;
+ }
+ rwFree(s);
+ return nil;
+ }else
+ for(int i = 0; i < g->numSearchPaths; i++){
+ s = (char*)rwMalloc(strlen(p)+len, MEMDUR_EVENT | ID_IMAGE);
+ if(s == nil){
+ RWERROR((ERR_ALLOC, strlen(p)+len));
+ return nil;
+ }
+ strcpy(s, p);
+ strcat(s, name);
+ makePath(s);
+ f = engine->filefuncs.rwfopen(s, "r");
+ if(f){
+ engine->filefuncs.rwfclose(f);
+// printf("found %s\n", name);
+ return s;
+ }
+ rwFree(s);
+ p += strlen(p) + 1;
+ }
+ return nil;
+}
+
+Image*
+Image::readMasked(const char *imageName, const char *maskName)
+{
+ Image *img, *mask;
+
+ img = read(imageName);
+ if(img == nil)
+ return nil;
+ if(maskName && maskName[0]){
+ mask = read(maskName);
+ if(mask == nil)
+ return img;
+ mask->makeMask();
+ int32 origDepth = img->depth;
+ img->applyMask(mask);
+ mask->destroy();
+ if(origDepth <= 8 && img->depth != origDepth)
+ img->palettize(origDepth);
+ }
+ return img;
+}
+
+Image*
+Image::read(const char *imageName)
+{
+ int i;
+ char *filename, *ext, *found;
+ Image *img;
+
+ filename = rwNewT(char, strlen(imageName) + 20, MEMDUR_FUNCTION | ID_IMAGE);
+ strcpy(filename, imageName);
+ ext = filename + strlen(filename);
+ *ext++ = '.';
+ // Try all supported extensions
+ for(i = 0; i < IMAGEGLOBAL(numFileFormats); i++){
+ if(IMAGEGLOBAL(fileFormats)[i].read == nil)
+ continue;
+ strncpy(ext, IMAGEGLOBAL(fileFormats)[i].extension, 19);
+ found = getFilename(filename);
+ // Found a file
+ if(found){
+ img = IMAGEGLOBAL(fileFormats)[i].read(found);
+ rwFree(found);
+ // It was a valid image of that format
+ if(img){
+ rwFree(filename);
+ return img;
+ }
+ }
+ }
+ rwFree(filename);
+ return nil;
+}
+
+bool32
+Image::registerFileFormat(const char *ext, fileRead read, fileWrite write)
+{
+ ImageGlobals *g = PLUGINOFFSET(ImageGlobals, engine, imageModuleOffset);
+ if(g->numFileFormats >= (int)nelem(g->fileFormats))
+ return 0;
+ g->fileFormats[g->numFileFormats].extension = rwStrdup(ext, MEMDUR_EVENT);
+ g->fileFormats[g->numFileFormats].read = read;
+ g->fileFormats[g->numFileFormats].write = write;
+ g->numFileFormats++;
+ return 1;
+}
+
+static void*
+imageOpen(void *object, int32 offset, int32 size)
+{
+ imageModuleOffset = offset;
+ ImageGlobals *g = PLUGINOFFSET(ImageGlobals, engine, imageModuleOffset);
+ g->searchPaths = nil;
+ g->numSearchPaths = 0;
+ g->numFileFormats = 0;
+ return object;
+}
+
+static void*
+imageClose(void *object, int32 offset, int32 size)
+{
+ ImageGlobals *g = PLUGINOFFSET(ImageGlobals, engine, imageModuleOffset);
+ int i;
+ rwFree(g->searchPaths);
+ g->searchPaths = nil;
+ g->numSearchPaths = 0;
+ for(i = 0; i < g->numFileFormats; i++)
+ rwFree(g->fileFormats[i].extension);
+ g->numFileFormats = 0;
+ return object;
+}
+
+void
+Image::registerModule(void)
+{
+ Engine::registerPlugin(sizeof(ImageGlobals), ID_IMAGEMODULE, imageOpen, imageClose);
+}
+
+
+
+/*
+ * Color Quantization
+ */
+
+// An address for a single level is 4 bits.
+// Since we have 8 bpp that is 32 bits to address any tree node.
+// The lower bits address the higher level tree nodes.
+// This is essentially a bit reverse and swizzle.
+static uint32
+makeTreeAddr(RGBA color)
+{
+ int32 i;
+ uint32 addr = 0;
+ uint32 r = 1;
+ uint32 g = 2;
+ uint32 b = 4;
+ uint32 a = 8;
+ for(i = 0; i < 8; i++){
+ uint32 mask = 0x80>>i;
+ if(color.red & mask) addr |= r;
+ if(color.green & mask) addr |= g;
+ if(color.blue & mask) addr |= b;
+ if(color.alpha & mask) addr |= a;
+ r <<= 4;
+ g <<= 4;
+ b <<= 4;
+ a <<= 4;
+ }
+ return addr;
+}
+
+void
+ColorQuant::Node::destroy(void)
+{
+ int i;
+ for(i = 0; i < 16; i++)
+ if(this->children[i])
+ this->children[i]->destroy();
+ if(this->link.next)
+ this->link.remove();
+ rwFree(this);
+}
+
+ColorQuant::Node*
+ColorQuant::createNode(int32 level)
+{
+ int i;
+ ColorQuant::Node *node = rwNewT(ColorQuant::Node, 1, MEMDUR_EVENT | ID_IMAGE);
+ node->parent = nil;
+ for(i = 0; i < 16; i++)
+ node->children[i] = nil;
+ node->r = 0;
+ node->g = 0;
+ node->b = 0;
+ node->a = 0;
+ node->numPixels = 0;
+ node->link.init();
+
+ if(level == 0)
+ this->leaves.append(&node->link);
+
+ return node;
+}
+
+ColorQuant::Node*
+ColorQuant::getNode(ColorQuant::Node *root, uint32 addr, int32 level)
+{
+ if(level == 0)
+ return root;
+
+ uint32 a = addr & 0xF;
+ if(root->children[a] == nil){
+ root->children[a] = this->createNode(level-1);
+ root->children[a]->parent = root;
+ }
+
+ return this->getNode(root->children[a], addr>>4, level-1);
+}
+
+ColorQuant::Node*
+ColorQuant::findNode(ColorQuant::Node *root, uint32 addr, int32 level)
+{
+ if(level == 0)
+ return root;
+
+ uint32 a = addr & 0xF;
+ if(root->children[a] == nil)
+ return root;
+
+ return this->findNode(root->children[a], addr>>4, level-1);
+}
+
+void
+ColorQuant::reduceNode(Node *node)
+{
+ int i;
+ assert(node->numPixels == 0);
+ for(i = 0; i < 16; i++)
+ if(node->children[i]){
+ node->r += node->children[i]->r;
+ node->g += node->children[i]->g;
+ node->b += node->children[i]->b;
+ node->a += node->children[i]->a;
+ node->numPixels += node->children[i]->numPixels;
+ node->children[i]->destroy();
+ node->children[i] = nil;
+ }
+ assert(node->link.next == nil);
+ assert(node->link.prev == nil);
+ this->leaves.append(&node->link);
+}
+
+void
+ColorQuant::Node::addColor(RGBA color)
+{
+ this->r += color.red;
+ this->g += color.green;
+ this->b += color.blue;
+ this->a += color.alpha;
+ this->numPixels++;
+}
+
+void
+ColorQuant::init(void)
+{
+ this->leaves.init();
+ this->root = this->createNode(QUANTDEPTH);
+}
+
+void
+ColorQuant::destroy(void)
+{
+ this->root->destroy();
+}
+
+void
+ColorQuant::addColor(RGBA color)
+{
+ uint32 addr = makeTreeAddr(color);
+ ColorQuant::Node *node = this->getNode(root, addr, QUANTDEPTH);
+ node->addColor(color);
+}
+
+uint8
+ColorQuant::findColor(RGBA color)
+{
+ uint32 addr = makeTreeAddr(color);
+ ColorQuant::Node *node = this->findNode(root, addr, QUANTDEPTH);
+ return node->numPixels;
+}
+
+void
+ColorQuant::addImage(Image *img)
+{
+ RGBA col;
+ uint8 rgba[4];
+ uint8 *pixels = img->pixels;
+ for(int y = 0; y < img->height; y++){
+ uint8 *line = pixels;
+ for(int x = 0; x < img->width; x++){
+ uint8 *p = line;
+ switch(img->depth){
+ case 4: case 8:
+ conv_RGBA8888_from_RGBA8888(rgba, &img->palette[p[0]*4]);
+ break;
+ case 32:
+ conv_RGBA8888_from_RGBA8888(rgba, p);
+ break;
+ case 24:
+ conv_RGBA8888_from_RGB888(rgba, p);
+ break;
+ case 16:
+ conv_RGBA8888_from_ARGB1555(rgba, p);
+ break;
+ default: assert(0 && "invalid depth");
+ }
+ col.red = rgba[0];
+ col.green = rgba[1];
+ col.blue = rgba[2];
+ col.alpha = rgba[3];
+ this->addColor(col);
+ line += img->bpp;
+ }
+ pixels += img->stride;
+ }
+}
+
+void
+ColorQuant::makePalette(int32 numColors, RGBA *colors)
+{
+ while(this->leaves.count() > numColors){
+ Node *n = LLLinkGetData(this->leaves.link.next, Node, link);
+ this->reduceNode(n->parent);
+ }
+
+ int i = 0;
+ FORLIST(lnk, this->leaves){
+ Node *n = LLLinkGetData(lnk, Node, link);
+ n->r /= n->numPixels;
+ n->g /= n->numPixels;
+ n->b /= n->numPixels;
+ n->a /= n->numPixels;
+ colors[i].red = n->r;
+ colors[i].green = n->g;
+ colors[i].blue = n->b;
+ colors[i].alpha = n->a;
+ n->numPixels = i++;
+ }
+}
+
+void
+ColorQuant::matchImage(uint8 *dstPixels, uint32 dstStride, Image *img)
+{
+ RGBA col;
+ uint8 rgba[4];
+ uint8 *pixels = img->pixels;
+ for(int y = 0; y < img->height; y++){
+ uint8 *line = pixels;
+ uint8 *dline = dstPixels;
+ for(int x = 0; x < img->width; x++){
+ uint8 *p = line;
+ uint8 *d = dline;
+ switch(img->depth){
+ case 4: case 8:
+ conv_RGBA8888_from_RGBA8888(rgba, &img->palette[p[0]*4]);
+ break;
+ case 32:
+ conv_RGBA8888_from_RGBA8888(rgba, p);
+ break;
+ case 24:
+ conv_RGBA8888_from_RGB888(rgba, p);
+ break;
+ case 16:
+ conv_RGBA8888_from_ARGB1555(rgba, p);
+ break;
+ default: assert(0 && "invalid depth");
+ }
+
+ col.red = rgba[0];
+ col.green = rgba[1];
+ col.blue = rgba[2];
+ col.alpha = rgba[3];
+ *d = this->findColor(col);
+
+ line += img->bpp;
+ dline++;
+ }
+ pixels += img->stride;
+ dstPixels += dstStride;
+ }
+}
+
+
+
+}
diff --git a/src/light.cpp b/src/light.cpp
new file mode 100644
index 0000000..ca68cda
--- /dev/null
+++ b/src/light.cpp
@@ -0,0 +1,163 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID ID_LIGHT
+
+namespace rw {
+
+int32 Light::numAllocated;
+
+PluginList Light::s_plglist(sizeof(Light));
+
+static void
+lightSync(ObjectWithFrame*)
+{
+}
+
+static void
+worldLightSync(ObjectWithFrame *obj)
+{
+ Light *light = (Light*)obj;
+ light->originalSync(obj);
+}
+
+Light*
+Light::create(int32 type)
+{
+ Light *light = (Light*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_LIGHT);
+ if(light == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ light->object.object.init(Light::ID, type);
+ light->object.syncCB = lightSync;
+ light->radius = 0.0f;
+ light->color.red = 1.0f;
+ light->color.green = 1.0f;
+ light->color.blue = 1.0f;
+ light->color.alpha = 1.0f;
+ light->minusCosAngle = 1.0f;
+ light->object.object.privateFlags = 1;
+ light->object.object.flags = LIGHTATOMICS | LIGHTWORLD;
+ light->inWorld.init();
+
+ // clump extension
+ light->clump = nil;
+ light->inClump.init();
+
+ // world extension
+ light->world = nil;
+ light->originalSync = light->object.syncCB;
+ light->object.syncCB = worldLightSync;
+
+ s_plglist.construct(light);
+ return light;
+}
+
+void
+Light::destroy(void)
+{
+ s_plglist.destruct(this);
+ assert(this->clump == nil);
+ assert(this->world == nil);
+ this->setFrame(nil);
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+Light::setAngle(float32 angle)
+{
+ this->minusCosAngle = -cosf(angle);
+}
+
+float32
+Light::getAngle(void)
+{
+ return acosf(-this->minusCosAngle);
+}
+
+void
+Light::setColor(float32 r, float32 g, float32 b)
+{
+ this->color.red = r;
+ this->color.green = g;
+ this->color.blue = b;
+ this->object.object.privateFlags = r == g && r == b;
+}
+
+struct LightChunkData
+{
+ float32 radius;
+ float32 red, green, blue;
+ float32 minusCosAngle;
+ uint32 type_flags;
+};
+
+Light*
+Light::streamRead(Stream *stream)
+{
+ uint32 version;
+ LightChunkData buf;
+
+ if(!findChunk(stream, ID_STRUCT, nil, &version)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ stream->read32(&buf, sizeof(LightChunkData));
+ Light *light = Light::create(buf.type_flags>>16);
+ if(light == nil)
+ return nil;
+ light->radius = buf.radius;
+ light->setColor(buf.red, buf.green, buf.blue);
+ float32 a = buf.minusCosAngle;
+ if(version >= 0x30300)
+ light->minusCosAngle = a;
+ else
+ // tan -> -cos
+ light->minusCosAngle = -1.0f/sqrtf(a*a+1.0f);
+ light->object.object.flags = (uint8)buf.type_flags;
+ if(s_plglist.streamRead(stream, light))
+ return light;
+ light->destroy();
+ return nil;
+}
+
+bool
+Light::streamWrite(Stream *stream)
+{
+ LightChunkData buf;
+ writeChunkHeader(stream, ID_LIGHT, this->streamGetSize());
+ writeChunkHeader(stream, ID_STRUCT, sizeof(LightChunkData));
+ buf.radius = this->radius;
+ buf.red = this->color.red;
+ buf.green = this->color.green;
+ buf.blue = this->color.blue;
+ if(version >= 0x30300)
+ buf.minusCosAngle = this->minusCosAngle;
+ else
+ buf.minusCosAngle = tanf(acosf(-this->minusCosAngle));
+ buf.type_flags = (uint32)this->object.object.flags |
+ (uint32)this->object.object.subType << 16;
+ stream->write32(&buf, sizeof(LightChunkData));
+
+ s_plglist.streamWrite(stream, this);
+ return true;
+}
+
+uint32
+Light::streamGetSize(void)
+{
+ return 12 + sizeof(LightChunkData) + 12 + s_plglist.streamGetSize(this);
+}
+
+}
diff --git a/src/lodepng/lodepng.cpp b/src/lodepng/lodepng.cpp
new file mode 100644
index 0000000..ee8cf33
--- /dev/null
+++ b/src/lodepng/lodepng.cpp
@@ -0,0 +1,6410 @@
+/*
+LodePNG version 20200306
+
+Copyright (c) 2005-2020 Lode Vandevenne
+
+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.
+*/
+
+/*
+The manual and changelog are in the header file "lodepng.h"
+Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C.
+*/
+
+#include "lodepng.h"
+
+#ifdef LODEPNG_COMPILE_DISK
+#include <limits.h> /* LONG_MAX */
+#include <stdio.h> /* file handling */
+#endif /* LODEPNG_COMPILE_DISK */
+
+#ifdef LODEPNG_COMPILE_ALLOCATORS
+#include <stdlib.h> /* allocations */
+#endif /* LODEPNG_COMPILE_ALLOCATORS */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/
+#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/
+#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
+#endif /*_MSC_VER */
+
+const char* LODEPNG_VERSION_STRING = "20200306";
+
+/*
+This source file is built up in the following large parts. The code sections
+with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way.
+-Tools for C and common code for PNG and Zlib
+-C Code for Zlib (huffman, deflate, ...)
+-C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...)
+-The C++ wrapper around all of the above
+*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // Tools for C, and common code for PNG and Zlib. // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*The malloc, realloc and free functions defined here with "lodepng_" in front
+of the name, so that you can easily change them to others related to your
+platform if needed. Everything else in the code calls these. Pass
+-DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out
+#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and
+define them in your own project's source files without needing to change
+lodepng source code. Don't forget to remove "static" if you copypaste them
+from here.*/
+
+#ifdef LODEPNG_COMPILE_ALLOCATORS
+static void* lodepng_malloc(size_t size) {
+#ifdef LODEPNG_MAX_ALLOC
+ if(size > LODEPNG_MAX_ALLOC) return 0;
+#endif
+ return malloc(size);
+}
+
+/* NOTE: when realloc returns NULL, it leaves the original memory untouched */
+static void* lodepng_realloc(void* ptr, size_t new_size) {
+#ifdef LODEPNG_MAX_ALLOC
+ if(new_size > LODEPNG_MAX_ALLOC) return 0;
+#endif
+ return realloc(ptr, new_size);
+}
+
+static void lodepng_free(void* ptr) {
+ free(ptr);
+}
+#else /*LODEPNG_COMPILE_ALLOCATORS*/
+/* TODO: support giving additional void* payload to the custom allocators */
+void* lodepng_malloc(size_t size);
+void* lodepng_realloc(void* ptr, size_t new_size);
+void lodepng_free(void* ptr);
+#endif /*LODEPNG_COMPILE_ALLOCATORS*/
+
+/* convince the compiler to inline a function, for use when this measurably improves performance */
+/* inline is not available in C90, but use it when supported by the compiler */
+#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L))
+#define LODEPNG_INLINE inline
+#else
+#define LODEPNG_INLINE /* not available */
+#endif
+
+/* restrict is not available in C90, but use it when supported by the compiler */
+#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
+ (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
+ (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
+#define LODEPNG_RESTRICT __restrict
+#else
+#define LODEPNG_RESTRICT /* not available */
+#endif
+
+/* Replacements for C library functions such as memcpy and strlen, to support platforms
+where a full C library is not available. The compiler can recognize them and compile
+to something as fast. */
+
+static void lodepng_memcpy(void* LODEPNG_RESTRICT dst,
+ const void* LODEPNG_RESTRICT src, size_t size) {
+ size_t i;
+ for(i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i];
+}
+
+static void lodepng_memset(void* LODEPNG_RESTRICT dst,
+ int value, size_t num) {
+ size_t i;
+ for(i = 0; i < num; i++) ((char*)dst)[i] = (char)value;
+}
+
+/* does not check memory out of bounds, do not use on untrusted data */
+static size_t lodepng_strlen(const char* a) {
+ const char* orig = a;
+ /* avoid warning about unused function in case of disabled COMPILE... macros */
+ (void)(&lodepng_strlen);
+ while(*a) a++;
+ return (size_t)(a - orig);
+}
+
+#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x))
+
+#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)
+/* Safely check if adding two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_addofl(size_t a, size_t b, size_t* result) {
+ *result = a + b; /* Unsigned addition is well defined and safe in C90 */
+ return *result < a;
+}
+#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)*/
+
+#ifdef LODEPNG_COMPILE_DECODER
+/* Safely check if multiplying two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_mulofl(size_t a, size_t b, size_t* result) {
+ *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */
+ return (a != 0 && *result / a != b);
+}
+
+#ifdef LODEPNG_COMPILE_ZLIB
+/* Safely check if a + b > c, even if overflow could happen. */
+static int lodepng_gtofl(size_t a, size_t b, size_t c) {
+ size_t d;
+ if(lodepng_addofl(a, b, &d)) return 1;
+ return d > c;
+}
+#endif /*LODEPNG_COMPILE_ZLIB*/
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+
+/*
+Often in case of an error a value is assigned to a variable and then it breaks
+out of a loop (to go to the cleanup phase of a function). This macro does that.
+It makes the error handling code shorter and more readable.
+
+Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83);
+*/
+#define CERROR_BREAK(errorvar, code){\
+ errorvar = code;\
+ break;\
+}
+
+/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/
+#define ERROR_BREAK(code) CERROR_BREAK(error, code)
+
+/*Set error var to the error code, and return it.*/
+#define CERROR_RETURN_ERROR(errorvar, code){\
+ errorvar = code;\
+ return code;\
+}
+
+/*Try the code, if it returns error, also return the error.*/
+#define CERROR_TRY_RETURN(call){\
+ unsigned error = call;\
+ if(error) return error;\
+}
+
+/*Set error var to the error code, and return from the void function.*/
+#define CERROR_RETURN(errorvar, code){\
+ errorvar = code;\
+ return;\
+}
+
+/*
+About uivector, ucvector and string:
+-All of them wrap dynamic arrays or text strings in a similar way.
+-LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version.
+-The string tools are made to avoid problems with compilers that declare things like strncat as deprecated.
+-They're not used in the interface, only internally in this file as static functions.
+-As with many other structs in this file, the init and cleanup functions serve as ctor and dtor.
+*/
+
+#ifdef LODEPNG_COMPILE_ZLIB
+#ifdef LODEPNG_COMPILE_ENCODER
+/*dynamic vector of unsigned ints*/
+typedef struct uivector {
+ unsigned* data;
+ size_t size; /*size in number of unsigned longs*/
+ size_t allocsize; /*allocated size in bytes*/
+} uivector;
+
+static void uivector_cleanup(void* p) {
+ ((uivector*)p)->size = ((uivector*)p)->allocsize = 0;
+ lodepng_free(((uivector*)p)->data);
+ ((uivector*)p)->data = NULL;
+}
+
+/*returns 1 if success, 0 if failure ==> nothing done*/
+static unsigned uivector_resize(uivector* p, size_t size) {
+ size_t allocsize = size * sizeof(unsigned);
+ if(allocsize > p->allocsize) {
+ size_t newsize = allocsize + (p->allocsize >> 1u);
+ void* data = lodepng_realloc(p->data, newsize);
+ if(data) {
+ p->allocsize = newsize;
+ p->data = (unsigned*)data;
+ }
+ else return 0; /*error: not enough memory*/
+ }
+ p->size = size;
+ return 1; /*success*/
+}
+
+static void uivector_init(uivector* p) {
+ p->data = NULL;
+ p->size = p->allocsize = 0;
+}
+
+/*returns 1 if success, 0 if failure ==> nothing done*/
+static unsigned uivector_push_back(uivector* p, unsigned c) {
+ if(!uivector_resize(p, p->size + 1)) return 0;
+ p->data[p->size - 1] = c;
+ return 1;
+}
+#endif /*LODEPNG_COMPILE_ENCODER*/
+#endif /*LODEPNG_COMPILE_ZLIB*/
+
+/* /////////////////////////////////////////////////////////////////////////// */
+
+/*dynamic vector of unsigned chars*/
+typedef struct ucvector {
+ unsigned char* data;
+ size_t size; /*used size*/
+ size_t allocsize; /*allocated size*/
+} ucvector;
+
+/*returns 1 if success, 0 if failure ==> nothing done*/
+static unsigned ucvector_resize(ucvector* p, size_t size) {
+ if(size > p->allocsize) {
+ size_t newsize = size + (p->allocsize >> 1u);
+ void* data = lodepng_realloc(p->data, newsize);
+ if(data) {
+ p->allocsize = newsize;
+ p->data = (unsigned char*)data;
+ }
+ else return 0; /*error: not enough memory*/
+ }
+ p->size = size;
+ return 1; /*success*/
+}
+
+static ucvector ucvector_init(unsigned char* buffer, size_t size) {
+ ucvector v;
+ v.data = buffer;
+ v.allocsize = v.size = size;
+ return v;
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#ifdef LODEPNG_COMPILE_PNG
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+
+/*free string pointer and set it to NULL*/
+static void string_cleanup(char** out) {
+ lodepng_free(*out);
+ *out = NULL;
+}
+
+static char* alloc_string_sized(const char* in, size_t insize) {
+ char* out = (char*)lodepng_malloc(insize + 1);
+ if(out) {
+ lodepng_memcpy(out, in, insize);
+ out[insize] = 0;
+ }
+ return out;
+}
+
+/* dynamically allocates a new string with a copy of the null terminated input text */
+static char* alloc_string(const char* in) {
+ return alloc_string_sized(in, lodepng_strlen(in));
+}
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+#endif /*LODEPNG_COMPILE_PNG*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG)
+static unsigned lodepng_read32bitInt(const unsigned char* buffer) {
+ return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) |
+ ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]);
+}
+#endif /*defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG)*/
+
+#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)
+/*buffer must have at least 4 allocated bytes available*/
+static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) {
+ buffer[0] = (unsigned char)((value >> 24) & 0xff);
+ buffer[1] = (unsigned char)((value >> 16) & 0xff);
+ buffer[2] = (unsigned char)((value >> 8) & 0xff);
+ buffer[3] = (unsigned char)((value ) & 0xff);
+}
+#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / File IO / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#ifdef LODEPNG_COMPILE_DISK
+
+/* returns negative value on error. This should be pure C compatible, so no fstat. */
+static long lodepng_filesize(const char* filename) {
+ FILE* file;
+ long size;
+ file = fopen(filename, "rb");
+ if(!file) return -1;
+
+ if(fseek(file, 0, SEEK_END) != 0) {
+ fclose(file);
+ return -1;
+ }
+
+ size = ftell(file);
+ /* It may give LONG_MAX as directory size, this is invalid for us. */
+ if(size == LONG_MAX) size = -1;
+
+ fclose(file);
+ return size;
+}
+
+/* load file into buffer that already has the correct allocated size. Returns error code.*/
+static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) {
+ FILE* file;
+ size_t readsize;
+ file = fopen(filename, "rb");
+ if(!file) return 78;
+
+ readsize = fread(out, 1, size, file);
+ fclose(file);
+
+ if(readsize != size) return 78;
+ return 0;
+}
+
+unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) {
+ long size = lodepng_filesize(filename);
+ if(size < 0) return 78;
+ *outsize = (size_t)size;
+
+ *out = (unsigned char*)lodepng_malloc((size_t)size);
+ if(!(*out) && size > 0) return 83; /*the above malloc failed*/
+
+ return lodepng_buffer_file(*out, (size_t)size, filename);
+}
+
+/*write given buffer to the file, overwriting the file, it doesn't append to it.*/
+unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) {
+ FILE* file;
+ file = fopen(filename, "wb" );
+ if(!file) return 79;
+ fwrite(buffer, 1, buffersize, file);
+ fclose(file);
+ return 0;
+}
+
+#endif /*LODEPNG_COMPILE_DISK*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // End of common code and tools. Begin of Zlib related code. // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#ifdef LODEPNG_COMPILE_ZLIB
+#ifdef LODEPNG_COMPILE_ENCODER
+
+typedef struct {
+ ucvector* data;
+ unsigned char bp; /*ok to overflow, indicates bit pos inside byte*/
+} LodePNGBitWriter;
+
+static void LodePNGBitWriter_init(LodePNGBitWriter* writer, ucvector* data) {
+ writer->data = data;
+ writer->bp = 0;
+}
+
+/*TODO: this ignores potential out of memory errors*/
+#define WRITEBIT(writer, bit){\
+ /* append new byte */\
+ if(((writer->bp) & 7u) == 0) {\
+ if(!ucvector_resize(writer->data, writer->data->size + 1)) return;\
+ writer->data->data[writer->data->size - 1] = 0;\
+ }\
+ (writer->data->data[writer->data->size - 1]) |= (bit << ((writer->bp) & 7u));\
+ ++writer->bp;\
+}
+
+/* LSB of value is written first, and LSB of bytes is used first */
+static void writeBits(LodePNGBitWriter* writer, unsigned value, size_t nbits) {
+ if(nbits == 1) { /* compiler should statically compile this case if nbits == 1 */
+ WRITEBIT(writer, value);
+ } else {
+ /* TODO: increase output size only once here rather than in each WRITEBIT */
+ size_t i;
+ for(i = 0; i != nbits; ++i) {
+ WRITEBIT(writer, (unsigned char)((value >> i) & 1));
+ }
+ }
+}
+
+/* This one is to use for adding huffman symbol, the value bits are written MSB first */
+static void writeBitsReversed(LodePNGBitWriter* writer, unsigned value, size_t nbits) {
+ size_t i;
+ for(i = 0; i != nbits; ++i) {
+ /* TODO: increase output size only once here rather than in each WRITEBIT */
+ WRITEBIT(writer, (unsigned char)((value >> (nbits - 1u - i)) & 1u));
+ }
+}
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+#ifdef LODEPNG_COMPILE_DECODER
+
+typedef struct {
+ const unsigned char* data;
+ size_t size; /*size of data in bytes*/
+ size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/
+ size_t bp;
+ unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/
+} LodePNGBitReader;
+
+/* data size argument is in bytes. Returns error if size too large causing overflow */
+static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size) {
+ size_t temp;
+ reader->data = data;
+ reader->size = size;
+ /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */
+ if(lodepng_mulofl(size, 8u, &reader->bitsize)) return 105;
+ /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and
+ trying to ensure 32 more bits*/
+ if(lodepng_addofl(reader->bitsize, 64u, &temp)) return 105;
+ reader->bp = 0;
+ reader->buffer = 0;
+ return 0; /*ok*/
+}
+
+/*
+ensureBits functions:
+Ensures the reader can at least read nbits bits in one or more readBits calls,
+safely even if not enough bits are available.
+Returns 1 if there are enough bits available, 0 if not.
+*/
+
+/*See ensureBits documentation above. This one ensures exactly 1 bit */
+/*static unsigned ensureBits1(LodePNGBitReader* reader) {
+ if(reader->bp >= reader->bitsize) return 0;
+ reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u);
+ return 1;
+}*/
+
+/*See ensureBits documentation above. This one ensures up to 9 bits */
+static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) {
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if(start + 1u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if(start + 0u < size) reader->buffer |= reader->data[start + 0];
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+/*See ensureBits documentation above. This one ensures up to 17 bits */
+static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) {
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if(start + 2u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
+ ((unsigned)reader->data[start + 2] << 16u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if(start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+/*See ensureBits documentation above. This one ensures up to 25 bits */
+static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits) {
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if(start + 3u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
+ ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if(start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+/*See ensureBits documentation above. This one ensures up to 32 bits */
+static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits) {
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if(start + 4u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
+ ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u)));
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if(start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
+ if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */
+static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) {
+ /* The shift allows nbits to be only up to 31. */
+ return reader->buffer & ((1u << nbits) - 1u);
+}
+
+/* Must have enough bits available with ensureBits */
+static void advanceBits(LodePNGBitReader* reader, size_t nbits) {
+ reader->buffer >>= nbits;
+ reader->bp += nbits;
+}
+
+/* Must have enough bits available with ensureBits */
+static unsigned readBits(LodePNGBitReader* reader, size_t nbits) {
+ unsigned result = peekBits(reader, nbits);
+ advanceBits(reader, nbits);
+ return result;
+}
+
+/* Public for testing only. steps and result must have numsteps values. */
+unsigned lode_png_test_bitreader(const unsigned char* data, size_t size,
+ size_t numsteps, const size_t* steps, unsigned* result) {
+ size_t i;
+ LodePNGBitReader reader;
+ unsigned error = LodePNGBitReader_init(&reader, data, size);
+ if(error) return 0;
+ for(i = 0; i < numsteps; i++) {
+ size_t step = steps[i];
+ unsigned ok;
+ if(step > 25) ok = ensureBits32(&reader, step);
+ else if(step > 17) ok = ensureBits25(&reader, step);
+ else if(step > 9) ok = ensureBits17(&reader, step);
+ else ok = ensureBits9(&reader, step);
+ if(!ok) return 0;
+ result[i] = readBits(&reader, step);
+ }
+ return 1;
+}
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+static unsigned reverseBits(unsigned bits, unsigned num) {
+ /*TODO: implement faster lookup table based version when needed*/
+ unsigned i, result = 0;
+ for(i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i;
+ return result;
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Deflate - Huffman / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#define FIRST_LENGTH_CODE_INDEX 257
+#define LAST_LENGTH_CODE_INDEX 285
+/*256 literals, the end code, some length codes, and 2 unused codes*/
+#define NUM_DEFLATE_CODE_SYMBOLS 288
+/*the distance codes have their own symbols, 30 used, 2 unused*/
+#define NUM_DISTANCE_SYMBOLS 32
+/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/
+#define NUM_CODE_LENGTH_CODES 19
+
+/*the base lengths represented by codes 257-285*/
+static const unsigned LENGTHBASE[29]
+ = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258};
+
+/*the extra bits used by codes 257-285 (added to base length)*/
+static const unsigned LENGTHEXTRA[29]
+ = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 4, 5, 5, 5, 5, 0};
+
+/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/
+static const unsigned DISTANCEBASE[30]
+ = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+ 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
+
+/*the extra bits of backwards distances (added to base)*/
+static const unsigned DISTANCEEXTRA[30]
+ = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
+ 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
+
+/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman
+tree of the dynamic huffman tree lengths is generated*/
+static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES]
+ = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*
+Huffman tree struct, containing multiple representations of the tree
+*/
+typedef struct HuffmanTree {
+ unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/
+ unsigned* lengths; /*the lengths of the huffman codes*/
+ unsigned maxbitlen; /*maximum number of bits a single code can get*/
+ unsigned numcodes; /*number of symbols in the alphabet = number of codes*/
+ /* for reading only */
+ unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/
+ unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/
+} HuffmanTree;
+
+static void HuffmanTree_init(HuffmanTree* tree) {
+ tree->codes = 0;
+ tree->lengths = 0;
+ tree->table_len = 0;
+ tree->table_value = 0;
+}
+
+static void HuffmanTree_cleanup(HuffmanTree* tree) {
+ lodepng_free(tree->codes);
+ lodepng_free(tree->lengths);
+ lodepng_free(tree->table_len);
+ lodepng_free(tree->table_value);
+}
+
+/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/
+/* values 8u and 9u work the fastest */
+#define FIRSTBITS 9u
+
+/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination,
+which is possible in case of only 0 or 1 present symbols. */
+#define INVALIDSYMBOL 65535u
+
+/* make table for huffman decoding */
+static unsigned HuffmanTree_makeTable(HuffmanTree* tree) {
+ static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/
+ static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u;
+ size_t i, numpresent, pointer, size; /*total table size*/
+ unsigned* maxlens = (unsigned*)lodepng_malloc(headsize * sizeof(unsigned));
+ if(!maxlens) return 83; /*alloc fail*/
+
+ /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/
+ lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens));
+ for(i = 0; i < tree->numcodes; i++) {
+ unsigned symbol = tree->codes[i];
+ unsigned l = tree->lengths[i];
+ unsigned index;
+ if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/
+ /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/
+ index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS);
+ maxlens[index] = LODEPNG_MAX(maxlens[index], l);
+ }
+ /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */
+ size = headsize;
+ for(i = 0; i < headsize; ++i) {
+ unsigned l = maxlens[i];
+ if(l > FIRSTBITS) size += (1u << (l - FIRSTBITS));
+ }
+ tree->table_len = (unsigned char*)lodepng_malloc(size * sizeof(*tree->table_len));
+ tree->table_value = (unsigned short*)lodepng_malloc(size * sizeof(*tree->table_value));
+ if(!tree->table_len || !tree->table_value) {
+ lodepng_free(maxlens);
+ /* freeing tree->table values is done at a higher scope */
+ return 83; /*alloc fail*/
+ }
+ /*initialize with an invalid length to indicate unused entries*/
+ for(i = 0; i < size; ++i) tree->table_len[i] = 16;
+
+ /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/
+ pointer = headsize;
+ for(i = 0; i < headsize; ++i) {
+ unsigned l = maxlens[i];
+ if(l <= FIRSTBITS) continue;
+ tree->table_len[i] = l;
+ tree->table_value[i] = pointer;
+ pointer += (1u << (l - FIRSTBITS));
+ }
+ lodepng_free(maxlens);
+
+ /*fill in the first table for short symbols, or secondary table for long symbols*/
+ numpresent = 0;
+ for(i = 0; i < tree->numcodes; ++i) {
+ unsigned l = tree->lengths[i];
+ unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/
+ /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/
+ unsigned reverse = reverseBits(symbol, l);
+ if(l == 0) continue;
+ numpresent++;
+
+ if(l <= FIRSTBITS) {
+ /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/
+ unsigned num = 1u << (FIRSTBITS - l);
+ unsigned j;
+ for(j = 0; j < num; ++j) {
+ /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/
+ unsigned index = reverse | (j << l);
+ if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
+ tree->table_len[index] = l;
+ tree->table_value[index] = i;
+ }
+ } else {
+ /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/
+ /*the FIRSTBITS MSBs of the symbol are the first table index*/
+ unsigned index = reverse & mask;
+ unsigned maxlen = tree->table_len[index];
+ /*log2 of secondary table length, should be >= l - FIRSTBITS*/
+ unsigned tablelen = maxlen - FIRSTBITS;
+ unsigned start = tree->table_value[index]; /*starting index in secondary table*/
+ unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/
+ unsigned j;
+ if(maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
+ for(j = 0; j < num; ++j) {
+ unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */
+ unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS)));
+ tree->table_len[index2] = l;
+ tree->table_value[index2] = i;
+ }
+ }
+ }
+
+ if(numpresent < 2) {
+ /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits,
+ but deflate uses 1 bit instead. In case of 0 symbols, no symbols can
+ appear at all, but such huffman tree could still exist (e.g. if distance
+ codes are never used). In both cases, not all symbols of the table will be
+ filled in. Fill them in with an invalid symbol value so returning them from
+ huffmanDecodeSymbol will cause error. */
+ for(i = 0; i < size; ++i) {
+ if(tree->table_len[i] == 16) {
+ /* As length, use a value smaller than FIRSTBITS for the head table,
+ and a value larger than FIRSTBITS for the secondary table, to ensure
+ valid behavior for advanceBits when reading this symbol. */
+ tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1);
+ tree->table_value[i] = INVALIDSYMBOL;
+ }
+ }
+ } else {
+ /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes.
+ If that is not the case (due to too long length codes), the table will not
+ have been fully used, and this is an error (not all bit combinations can be
+ decoded): an oversubscribed huffman tree, indicated by error 55. */
+ for(i = 0; i < size; ++i) {
+ if(tree->table_len[i] == 16) return 55;
+ }
+ }
+
+ return 0;
+}
+
+/*
+Second step for the ...makeFromLengths and ...makeFromFrequencies functions.
+numcodes, lengths and maxbitlen must already be filled in correctly. return
+value is error.
+*/
+static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) {
+ unsigned* blcount;
+ unsigned* nextcode;
+ unsigned error = 0;
+ unsigned bits, n;
+
+ tree->codes = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned));
+ blcount = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned));
+ nextcode = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned));
+ if(!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/
+
+ if(!error) {
+ for(n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0;
+ /*step 1: count number of instances of each code length*/
+ for(bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]];
+ /*step 2: generate the nextcode values*/
+ for(bits = 1; bits <= tree->maxbitlen; ++bits) {
+ nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u;
+ }
+ /*step 3: generate all the codes*/
+ for(n = 0; n != tree->numcodes; ++n) {
+ if(tree->lengths[n] != 0) {
+ tree->codes[n] = nextcode[tree->lengths[n]]++;
+ /*remove superfluous bits from the code*/
+ tree->codes[n] &= ((1u << tree->lengths[n]) - 1u);
+ }
+ }
+ }
+
+ lodepng_free(blcount);
+ lodepng_free(nextcode);
+
+ if(!error) error = HuffmanTree_makeTable(tree);
+ return error;
+}
+
+/*
+given the code lengths (as stored in the PNG file), generate the tree as defined
+by Deflate. maxbitlen is the maximum bits that a code in the tree can have.
+return value is error.
+*/
+static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen,
+ size_t numcodes, unsigned maxbitlen) {
+ unsigned i;
+ tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned));
+ if(!tree->lengths) return 83; /*alloc fail*/
+ for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i];
+ tree->numcodes = (unsigned)numcodes; /*number of symbols*/
+ tree->maxbitlen = maxbitlen;
+ return HuffmanTree_makeFromLengths2(tree);
+}
+
+#ifdef LODEPNG_COMPILE_ENCODER
+
+/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding",
+Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/
+
+/*chain node for boundary package merge*/
+typedef struct BPMNode {
+ int weight; /*the sum of all weights in this chain*/
+ unsigned index; /*index of this leaf node (called "count" in the paper)*/
+ struct BPMNode* tail; /*the next nodes in this chain (null if last)*/
+ int in_use;
+} BPMNode;
+
+/*lists of chains*/
+typedef struct BPMLists {
+ /*memory pool*/
+ unsigned memsize;
+ BPMNode* memory;
+ unsigned numfree;
+ unsigned nextfree;
+ BPMNode** freelist;
+ /*two heads of lookahead chains per list*/
+ unsigned listsize;
+ BPMNode** chains0;
+ BPMNode** chains1;
+} BPMLists;
+
+/*creates a new chain node with the given parameters, from the memory in the lists */
+static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) {
+ unsigned i;
+ BPMNode* result;
+
+ /*memory full, so garbage collect*/
+ if(lists->nextfree >= lists->numfree) {
+ /*mark only those that are in use*/
+ for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0;
+ for(i = 0; i != lists->listsize; ++i) {
+ BPMNode* node;
+ for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1;
+ for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1;
+ }
+ /*collect those that are free*/
+ lists->numfree = 0;
+ for(i = 0; i != lists->memsize; ++i) {
+ if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i];
+ }
+ lists->nextfree = 0;
+ }
+
+ result = lists->freelist[lists->nextfree++];
+ result->weight = weight;
+ result->index = index;
+ result->tail = tail;
+ return result;
+}
+
+/*sort the leaves with stable mergesort*/
+static void bpmnode_sort(BPMNode* leaves, size_t num) {
+ BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num);
+ size_t width, counter = 0;
+ for(width = 1; width < num; width *= 2) {
+ BPMNode* a = (counter & 1) ? mem : leaves;
+ BPMNode* b = (counter & 1) ? leaves : mem;
+ size_t p;
+ for(p = 0; p < num; p += 2 * width) {
+ size_t q = (p + width > num) ? num : (p + width);
+ size_t r = (p + 2 * width > num) ? num : (p + 2 * width);
+ size_t i = p, j = q, k;
+ for(k = p; k < r; k++) {
+ if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++];
+ else b[k] = a[j++];
+ }
+ }
+ counter++;
+ }
+ if(counter & 1) lodepng_memcpy(leaves, mem, sizeof(*leaves) * num);
+ lodepng_free(mem);
+}
+
+/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/
+static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) {
+ unsigned lastindex = lists->chains1[c]->index;
+
+ if(c == 0) {
+ if(lastindex >= numpresent) return;
+ lists->chains0[c] = lists->chains1[c];
+ lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0);
+ } else {
+ /*sum of the weights of the head nodes of the previous lookahead chains.*/
+ int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight;
+ lists->chains0[c] = lists->chains1[c];
+ if(lastindex < numpresent && sum > leaves[lastindex].weight) {
+ lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail);
+ return;
+ }
+ lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]);
+ /*in the end we are only interested in the chain of the last list, so no
+ need to recurse if we're at the last one (this gives measurable speedup)*/
+ if(num + 1 < (int)(2 * numpresent - 2)) {
+ boundaryPM(lists, leaves, numpresent, c - 1, num);
+ boundaryPM(lists, leaves, numpresent, c - 1, num);
+ }
+ }
+}
+
+unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
+ size_t numcodes, unsigned maxbitlen) {
+ unsigned error = 0;
+ unsigned i;
+ size_t numpresent = 0; /*number of symbols with non-zero frequency*/
+ BPMNode* leaves; /*the symbols, only those with > 0 frequency*/
+
+ if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/
+ if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/
+
+ leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves));
+ if(!leaves) return 83; /*alloc fail*/
+
+ for(i = 0; i != numcodes; ++i) {
+ if(frequencies[i] > 0) {
+ leaves[numpresent].weight = (int)frequencies[i];
+ leaves[numpresent].index = i;
+ ++numpresent;
+ }
+ }
+
+ lodepng_memset(lengths, 0, numcodes * sizeof(*lengths));
+
+ /*ensure at least two present symbols. There should be at least one symbol
+ according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To
+ make these work as well ensure there are at least two symbols. The
+ Package-Merge code below also doesn't work correctly if there's only one
+ symbol, it'd give it the theoretical 0 bits but in practice zlib wants 1 bit*/
+ if(numpresent == 0) {
+ lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/
+ } else if(numpresent == 1) {
+ lengths[leaves[0].index] = 1;
+ lengths[leaves[0].index == 0 ? 1 : 0] = 1;
+ } else {
+ BPMLists lists;
+ BPMNode* node;
+
+ bpmnode_sort(leaves, numpresent);
+
+ lists.listsize = maxbitlen;
+ lists.memsize = 2 * maxbitlen * (maxbitlen + 1);
+ lists.nextfree = 0;
+ lists.numfree = lists.memsize;
+ lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory));
+ lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*));
+ lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
+ lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
+ if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/
+
+ if(!error) {
+ for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i];
+
+ bpmnode_create(&lists, leaves[0].weight, 1, 0);
+ bpmnode_create(&lists, leaves[1].weight, 2, 0);
+
+ for(i = 0; i != lists.listsize; ++i) {
+ lists.chains0[i] = &lists.memory[0];
+ lists.chains1[i] = &lists.memory[1];
+ }
+
+ /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/
+ for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i);
+
+ for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) {
+ for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index];
+ }
+ }
+
+ lodepng_free(lists.memory);
+ lodepng_free(lists.freelist);
+ lodepng_free(lists.chains0);
+ lodepng_free(lists.chains1);
+ }
+
+ lodepng_free(leaves);
+ return error;
+}
+
+/*Create the Huffman tree given the symbol frequencies*/
+static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies,
+ size_t mincodes, size_t numcodes, unsigned maxbitlen) {
+ unsigned error = 0;
+ while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/
+ tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned));
+ if(!tree->lengths) return 83; /*alloc fail*/
+ tree->maxbitlen = maxbitlen;
+ tree->numcodes = (unsigned)numcodes; /*number of symbols*/
+
+ error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen);
+ if(!error) error = HuffmanTree_makeFromLengths2(tree);
+ return error;
+}
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/
+static unsigned generateFixedLitLenTree(HuffmanTree* tree) {
+ unsigned i, error = 0;
+ unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
+ if(!bitlen) return 83; /*alloc fail*/
+
+ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/
+ for(i = 0; i <= 143; ++i) bitlen[i] = 8;
+ for(i = 144; i <= 255; ++i) bitlen[i] = 9;
+ for(i = 256; i <= 279; ++i) bitlen[i] = 7;
+ for(i = 280; i <= 287; ++i) bitlen[i] = 8;
+
+ error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15);
+
+ lodepng_free(bitlen);
+ return error;
+}
+
+/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/
+static unsigned generateFixedDistanceTree(HuffmanTree* tree) {
+ unsigned i, error = 0;
+ unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
+ if(!bitlen) return 83; /*alloc fail*/
+
+ /*there are 32 distance codes, but 30-31 are unused*/
+ for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5;
+ error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15);
+
+ lodepng_free(bitlen);
+ return error;
+}
+
+#ifdef LODEPNG_COMPILE_DECODER
+
+/*
+returns the code. The bit reader must already have been ensured at least 15 bits
+*/
+static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree) {
+ unsigned short code = peekBits(reader, FIRSTBITS);
+ unsigned short l = codetree->table_len[code];
+ unsigned short value = codetree->table_value[code];
+ if(l <= FIRSTBITS) {
+ advanceBits(reader, l);
+ return value;
+ } else {
+ unsigned index2;
+ advanceBits(reader, FIRSTBITS);
+ index2 = value + peekBits(reader, l - FIRSTBITS);
+ advanceBits(reader, codetree->table_len[index2] - FIRSTBITS);
+ return codetree->table_value[index2];
+ }
+}
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#ifdef LODEPNG_COMPILE_DECODER
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Inflator (Decompressor) / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*get the tree of a deflated block with fixed tree, as specified in the deflate specification
+Returns error code.*/
+static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) {
+ unsigned error = generateFixedLitLenTree(tree_ll);
+ if(error) return error;
+ return generateFixedDistanceTree(tree_d);
+}
+
+/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/
+static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
+ LodePNGBitReader* reader) {
+ /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/
+ unsigned error = 0;
+ unsigned n, HLIT, HDIST, HCLEN, i;
+
+ /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/
+ unsigned* bitlen_ll = 0; /*lit,len code lengths*/
+ unsigned* bitlen_d = 0; /*dist code lengths*/
+ /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/
+ unsigned* bitlen_cl = 0;
+ HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/
+
+ if(!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/
+
+ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/
+ HLIT = readBits(reader, 5) + 257;
+ /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/
+ HDIST = readBits(reader, 5) + 1;
+ /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/
+ HCLEN = readBits(reader, 4) + 4;
+
+ bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned));
+ if(!bitlen_cl) return 83 /*alloc fail*/;
+
+ HuffmanTree_init(&tree_cl);
+
+ while(!error) {
+ /*read the code length codes out of 3 * (amount of code length codes) bits*/
+ if(lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) {
+ ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/
+ }
+ for(i = 0; i != HCLEN; ++i) {
+ ensureBits9(reader, 3); /*out of bounds already checked above */
+ bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3);
+ }
+ for(i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) {
+ bitlen_cl[CLCL_ORDER[i]] = 0;
+ }
+
+ error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7);
+ if(error) break;
+
+ /*now we can use this tree to read the lengths for the tree that this function will return*/
+ bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
+ bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
+ if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/);
+ lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll));
+ lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d));
+
+ /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/
+ i = 0;
+ while(i < HLIT + HDIST) {
+ unsigned code;
+ ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/
+ code = huffmanDecodeSymbol(reader, &tree_cl);
+ if(code <= 15) /*a length code*/ {
+ if(i < HLIT) bitlen_ll[i] = code;
+ else bitlen_d[i - HLIT] = code;
+ ++i;
+ } else if(code == 16) /*repeat previous*/ {
+ unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/
+ unsigned value; /*set value to the previous code*/
+
+ if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
+
+ replength += readBits(reader, 2);
+
+ if(i < HLIT + 1) value = bitlen_ll[i - 1];
+ else value = bitlen_d[i - HLIT - 1];
+ /*repeat this value in the next lengths*/
+ for(n = 0; n < replength; ++n) {
+ if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/
+ if(i < HLIT) bitlen_ll[i] = value;
+ else bitlen_d[i - HLIT] = value;
+ ++i;
+ }
+ } else if(code == 17) /*repeat "0" 3-10 times*/ {
+ unsigned replength = 3; /*read in the bits that indicate repeat length*/
+ replength += readBits(reader, 3);
+
+ /*repeat this value in the next lengths*/
+ for(n = 0; n < replength; ++n) {
+ if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/
+
+ if(i < HLIT) bitlen_ll[i] = 0;
+ else bitlen_d[i - HLIT] = 0;
+ ++i;
+ }
+ } else if(code == 18) /*repeat "0" 11-138 times*/ {
+ unsigned replength = 11; /*read in the bits that indicate repeat length*/
+ replength += readBits(reader, 7);
+
+ /*repeat this value in the next lengths*/
+ for(n = 0; n < replength; ++n) {
+ if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/
+
+ if(i < HLIT) bitlen_ll[i] = 0;
+ else bitlen_d[i - HLIT] = 0;
+ ++i;
+ }
+ } else /*if(code == INVALIDSYMBOL)*/ {
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ /*check if any of the ensureBits above went out of bounds*/
+ if(reader->bp > reader->bitsize) {
+ /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
+ (10=no endcode, 11=wrong jump outside of tree)*/
+ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
+ ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
+ }
+ }
+ if(error) break;
+
+ if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/
+
+ /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/
+ error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15);
+ if(error) break;
+ error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15);
+
+ break; /*end of error-while*/
+ }
+
+ lodepng_free(bitlen_cl);
+ lodepng_free(bitlen_ll);
+ lodepng_free(bitlen_d);
+ HuffmanTree_cleanup(&tree_cl);
+
+ return error;
+}
+
+/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
+static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
+ unsigned btype) {
+ unsigned error = 0;
+ HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
+ HuffmanTree tree_d; /*the huffman tree for distance codes*/
+
+ HuffmanTree_init(&tree_ll);
+ HuffmanTree_init(&tree_d);
+
+ if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d);
+ else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader);
+
+ while(!error) /*decode all symbols until end reached, breaks at end code*/ {
+ /*code_ll is literal, length or end code*/
+ unsigned code_ll;
+ ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */
+ code_ll = huffmanDecodeSymbol(reader, &tree_ll);
+ if(code_ll <= 255) /*literal symbol*/ {
+ if(!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/);
+ out->data[out->size - 1] = (unsigned char)code_ll;
+ } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ {
+ unsigned code_d, distance;
+ unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/
+ size_t start, backward, length;
+
+ /*part 1: get length base*/
+ length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX];
+
+ /*part 2: get extra bits and add the value of that to length*/
+ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX];
+ if(numextrabits_l != 0) {
+ /* bits already ensured above */
+ length += readBits(reader, numextrabits_l);
+ }
+
+ /*part 3: get distance code*/
+ ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */
+ code_d = huffmanDecodeSymbol(reader, &tree_d);
+ if(code_d > 29) {
+ if(code_d <= 31) {
+ ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/
+ } else /* if(code_d == INVALIDSYMBOL) */{
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ }
+ distance = DISTANCEBASE[code_d];
+
+ /*part 4: get extra bits from distance*/
+ numextrabits_d = DISTANCEEXTRA[code_d];
+ if(numextrabits_d != 0) {
+ /* bits already ensured above */
+ distance += readBits(reader, numextrabits_d);
+ }
+
+ /*part 5: fill in all the out[n] values based on the length and dist*/
+ start = out->size;
+ if(distance > start) ERROR_BREAK(52); /*too long backward distance*/
+ backward = start - distance;
+
+ if(!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/);
+ if(distance < length) {
+ size_t forward;
+ lodepng_memcpy(out->data + start, out->data + backward, distance);
+ start += distance;
+ for(forward = distance; forward < length; ++forward) {
+ out->data[start++] = out->data[backward++];
+ }
+ } else {
+ lodepng_memcpy(out->data + start, out->data + backward, length);
+ }
+ } else if(code_ll == 256) {
+ break; /*end code, break the loop*/
+ } else /*if(code_ll == INVALIDSYMBOL)*/ {
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ /*check if any of the ensureBits above went out of bounds*/
+ if(reader->bp > reader->bitsize) {
+ /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
+ (10=no endcode, 11=wrong jump outside of tree)*/
+ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
+ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
+ }
+ }
+
+ HuffmanTree_cleanup(&tree_ll);
+ HuffmanTree_cleanup(&tree_d);
+
+ return error;
+}
+
+static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader,
+ const LodePNGDecompressSettings* settings) {
+ size_t bytepos;
+ size_t size = reader->size;
+ unsigned LEN, NLEN, error = 0;
+
+ /*go to first boundary of byte*/
+ bytepos = (reader->bp + 7u) >> 3u;
+
+ /*read LEN (2 bytes) and NLEN (2 bytes)*/
+ if(bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/
+ LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
+ NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
+
+ /*check if 16-bit NLEN is really the one's complement of LEN*/
+ if(!settings->ignore_nlen && LEN + NLEN != 65535) {
+ return 21; /*error: NLEN is not one's complement of LEN*/
+ }
+
+ if(!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/
+
+ /*read the literal data: LEN bytes are now stored in the out buffer*/
+ if(bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/
+
+ lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN);
+ bytepos += LEN;
+
+ reader->bp = bytepos << 3u;
+
+ return error;
+}
+
+static unsigned lodepng_inflatev(ucvector* out,
+ const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings* settings) {
+ unsigned BFINAL = 0;
+ LodePNGBitReader reader;
+ unsigned error = LodePNGBitReader_init(&reader, in, insize);
+
+ if(error) return error;
+
+ while(!BFINAL) {
+ unsigned BTYPE;
+ if(!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/
+ BFINAL = readBits(&reader, 1);
+ BTYPE = readBits(&reader, 2);
+
+ if(BTYPE == 3) return 20; /*error: invalid BTYPE*/
+ else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
+ else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/
+
+ if(error) return error;
+ }
+
+ return error;
+}
+
+unsigned lodepng_inflate(unsigned char** out, size_t* outsize,
+ const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings* settings) {
+ ucvector v = ucvector_init(*out, *outsize);
+ unsigned error = lodepng_inflatev(&v, in, insize, settings);
+ *out = v.data;
+ *outsize = v.size;
+ return error;
+}
+
+static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings* settings) {
+ if(settings->custom_inflate) {
+ unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
+ out->allocsize = out->size;
+ return error;
+ } else {
+ return lodepng_inflatev(out, in, insize, settings);
+ }
+}
+
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#ifdef LODEPNG_COMPILE_ENCODER
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Deflator (Compressor) / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258;
+
+/*search the index in the array, that has the largest value smaller than or equal to the given value,
+given array must be sorted (if no value is smaller, it returns the size of the given array)*/
+static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) {
+ /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/
+ size_t left = 1;
+ size_t right = array_size - 1;
+
+ while(left <= right) {
+ size_t mid = (left + right) >> 1;
+ if(array[mid] >= value) right = mid - 1;
+ else left = mid + 1;
+ }
+ if(left >= array_size || array[left] > value) left--;
+ return left;
+}
+
+static void addLengthDistance(uivector* values, size_t length, size_t distance) {
+ /*values in encoded vector are those used by deflate:
+ 0-255: literal bytes
+ 256: end
+ 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits)
+ 286-287: invalid*/
+
+ unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length);
+ unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]);
+ unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance);
+ unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]);
+
+ size_t pos = values->size;
+ /*TODO: return error when this fails (out of memory)*/
+ unsigned ok = uivector_resize(values, values->size + 4);
+ if(ok) {
+ values->data[pos + 0] = length_code + FIRST_LENGTH_CODE_INDEX;
+ values->data[pos + 1] = extra_length;
+ values->data[pos + 2] = dist_code;
+ values->data[pos + 3] = extra_distance;
+ }
+}
+
+/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3
+bytes as input because 3 is the minimum match length for deflate*/
+static const unsigned HASH_NUM_VALUES = 65536;
+static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/
+
+typedef struct Hash {
+ int* head; /*hash value to head circular pos - can be outdated if went around window*/
+ /*circular pos to prev circular pos*/
+ unsigned short* chain;
+ int* val; /*circular pos to hash value*/
+
+ /*TODO: do this not only for zeros but for any repeated byte. However for PNG
+ it's always going to be the zeros that dominate, so not important for PNG*/
+ int* headz; /*similar to head, but for chainz*/
+ unsigned short* chainz; /*those with same amount of zeros*/
+ unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/
+} Hash;
+
+static unsigned hash_init(Hash* hash, unsigned windowsize) {
+ unsigned i;
+ hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES);
+ hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize);
+ hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
+
+ hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
+ hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1));
+ hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
+
+ if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) {
+ return 83; /*alloc fail*/
+ }
+
+ /*initialize hash table*/
+ for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1;
+ for(i = 0; i != windowsize; ++i) hash->val[i] = -1;
+ for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/
+
+ for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1;
+ for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/
+
+ return 0;
+}
+
+static void hash_cleanup(Hash* hash) {
+ lodepng_free(hash->head);
+ lodepng_free(hash->val);
+ lodepng_free(hash->chain);
+
+ lodepng_free(hash->zeros);
+ lodepng_free(hash->headz);
+ lodepng_free(hash->chainz);
+}
+
+
+
+static unsigned getHash(const unsigned char* data, size_t size, size_t pos) {
+ unsigned result = 0;
+ if(pos + 2 < size) {
+ /*A simple shift and xor hash is used. Since the data of PNGs is dominated
+ by zeroes due to the filters, a better hash does not have a significant
+ effect on speed in traversing the chain, and causes more time spend on
+ calculating the hash.*/
+ result ^= ((unsigned)data[pos + 0] << 0u);
+ result ^= ((unsigned)data[pos + 1] << 4u);
+ result ^= ((unsigned)data[pos + 2] << 8u);
+ } else {
+ size_t amount, i;
+ if(pos >= size) return 0;
+ amount = size - pos;
+ for(i = 0; i != amount; ++i) result ^= ((unsigned)data[pos + i] << (i * 8u));
+ }
+ return result & HASH_BIT_MASK;
+}
+
+static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) {
+ const unsigned char* start = data + pos;
+ const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH;
+ if(end > data + size) end = data + size;
+ data = start;
+ while(data != end && *data == 0) ++data;
+ /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/
+ return (unsigned)(data - start);
+}
+
+/*wpos = pos & (windowsize - 1)*/
+static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) {
+ hash->val[wpos] = (int)hashval;
+ if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval];
+ hash->head[hashval] = (int)wpos;
+
+ hash->zeros[wpos] = numzeros;
+ if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros];
+ hash->headz[numzeros] = (int)wpos;
+}
+
+/*
+LZ77-encode the data. Return value is error code. The input are raw bytes, the output
+is in the form of unsigned integers with codes representing for example literal bytes, or
+length/distance pairs.
+It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a
+sliding window (of windowsize) is used, and all past bytes in that window can be used as
+the "dictionary". A brute force search through all possible distances would be slow, and
+this hash technique is one out of several ways to speed this up.
+*/
+static unsigned encodeLZ77(uivector* out, Hash* hash,
+ const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize,
+ unsigned minmatch, unsigned nicematch, unsigned lazymatching) {
+ size_t pos;
+ unsigned i, error = 0;
+ /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/
+ unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8u;
+ unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64;
+
+ unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/
+ unsigned numzeros = 0;
+
+ unsigned offset; /*the offset represents the distance in LZ77 terminology*/
+ unsigned length;
+ unsigned lazy = 0;
+ unsigned lazylength = 0, lazyoffset = 0;
+ unsigned hashval;
+ unsigned current_offset, current_length;
+ unsigned prev_offset;
+ const unsigned char *lastptr, *foreptr, *backptr;
+ unsigned hashpos;
+
+ if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/
+ if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/
+
+ if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH;
+
+ for(pos = inpos; pos < insize; ++pos) {
+ size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/
+ unsigned chainlength = 0;
+
+ hashval = getHash(in, insize, pos);
+
+ if(usezeros && hashval == 0) {
+ if(numzeros == 0) numzeros = countZeros(in, insize, pos);
+ else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
+ } else {
+ numzeros = 0;
+ }
+
+ updateHashChain(hash, wpos, hashval, numzeros);
+
+ /*the length and offset found for the current position*/
+ length = 0;
+ offset = 0;
+
+ hashpos = hash->chain[wpos];
+
+ lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH];
+
+ /*search for the longest string*/
+ prev_offset = 0;
+ for(;;) {
+ if(chainlength++ >= maxchainlength) break;
+ current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize);
+
+ if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/
+ prev_offset = current_offset;
+ if(current_offset > 0) {
+ /*test the next characters*/
+ foreptr = &in[pos];
+ backptr = &in[pos - current_offset];
+
+ /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/
+ if(numzeros >= 3) {
+ unsigned skip = hash->zeros[hashpos];
+ if(skip > numzeros) skip = numzeros;
+ backptr += skip;
+ foreptr += skip;
+ }
+
+ while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ {
+ ++backptr;
+ ++foreptr;
+ }
+ current_length = (unsigned)(foreptr - &in[pos]);
+
+ if(current_length > length) {
+ length = current_length; /*the longest length*/
+ offset = current_offset; /*the offset that is related to this longest length*/
+ /*jump out once a length of max length is found (speed gain). This also jumps
+ out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/
+ if(current_length >= nicematch) break;
+ }
+ }
+
+ if(hashpos == hash->chain[hashpos]) break;
+
+ if(numzeros >= 3 && length > numzeros) {
+ hashpos = hash->chainz[hashpos];
+ if(hash->zeros[hashpos] != numzeros) break;
+ } else {
+ hashpos = hash->chain[hashpos];
+ /*outdated hash value, happens if particular value was not encountered in whole last window*/
+ if(hash->val[hashpos] != (int)hashval) break;
+ }
+ }
+
+ if(lazymatching) {
+ if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) {
+ lazy = 1;
+ lazylength = length;
+ lazyoffset = offset;
+ continue; /*try the next byte*/
+ }
+ if(lazy) {
+ lazy = 0;
+ if(pos == 0) ERROR_BREAK(81);
+ if(length > lazylength + 1) {
+ /*push the previous character as literal*/
+ if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/);
+ } else {
+ length = lazylength;
+ offset = lazyoffset;
+ hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/
+ hash->headz[numzeros] = -1; /*idem*/
+ --pos;
+ }
+ }
+ }
+ if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/);
+
+ /*encode it as length/distance pair or literal value*/
+ if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ {
+ if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/);
+ } else if(length < minmatch || (length == 3 && offset > 4096)) {
+ /*compensate for the fact that longer offsets have more extra bits, a
+ length of only 3 may be not worth it then*/
+ if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/);
+ } else {
+ addLengthDistance(out, length, offset);
+ for(i = 1; i < length; ++i) {
+ ++pos;
+ wpos = pos & (windowsize - 1);
+ hashval = getHash(in, insize, pos);
+ if(usezeros && hashval == 0) {
+ if(numzeros == 0) numzeros = countZeros(in, insize, pos);
+ else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
+ } else {
+ numzeros = 0;
+ }
+ updateHashChain(hash, wpos, hashval, numzeros);
+ }
+ }
+ } /*end of the loop through each character of input*/
+
+ return error;
+}
+
+/* /////////////////////////////////////////////////////////////////////////// */
+
+static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) {
+ /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte,
+ 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/
+
+ size_t i, numdeflateblocks = (datasize + 65534u) / 65535u;
+ unsigned datapos = 0;
+ for(i = 0; i != numdeflateblocks; ++i) {
+ unsigned BFINAL, BTYPE, LEN, NLEN;
+ unsigned char firstbyte;
+ size_t pos = out->size;
+
+ BFINAL = (i == numdeflateblocks - 1);
+ BTYPE = 0;
+
+ LEN = 65535;
+ if(datasize - datapos < 65535u) LEN = (unsigned)datasize - datapos;
+ NLEN = 65535 - LEN;
+
+ if(!ucvector_resize(out, out->size + LEN + 5)) return 83; /*alloc fail*/
+
+ firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1u) << 1u) + ((BTYPE & 2u) << 1u));
+ out->data[pos + 0] = firstbyte;
+ out->data[pos + 1] = (unsigned char)(LEN & 255);
+ out->data[pos + 2] = (unsigned char)(LEN >> 8u);
+ out->data[pos + 3] = (unsigned char)(NLEN & 255);
+ out->data[pos + 4] = (unsigned char)(NLEN >> 8u);
+ lodepng_memcpy(out->data + pos + 5, data + datapos, LEN);
+ datapos += LEN;
+ }
+
+ return 0;
+}
+
+/*
+write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees.
+tree_ll: the tree for lit and len codes.
+tree_d: the tree for distance codes.
+*/
+static void writeLZ77data(LodePNGBitWriter* writer, const uivector* lz77_encoded,
+ const HuffmanTree* tree_ll, const HuffmanTree* tree_d) {
+ size_t i = 0;
+ for(i = 0; i != lz77_encoded->size; ++i) {
+ unsigned val = lz77_encoded->data[i];
+ writeBitsReversed(writer, tree_ll->codes[val], tree_ll->lengths[val]);
+ if(val > 256) /*for a length code, 3 more things have to be added*/ {
+ unsigned length_index = val - FIRST_LENGTH_CODE_INDEX;
+ unsigned n_length_extra_bits = LENGTHEXTRA[length_index];
+ unsigned length_extra_bits = lz77_encoded->data[++i];
+
+ unsigned distance_code = lz77_encoded->data[++i];
+
+ unsigned distance_index = distance_code;
+ unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index];
+ unsigned distance_extra_bits = lz77_encoded->data[++i];
+
+ writeBits(writer, length_extra_bits, n_length_extra_bits);
+ writeBitsReversed(writer, tree_d->codes[distance_code], tree_d->lengths[distance_code]);
+ writeBits(writer, distance_extra_bits, n_distance_extra_bits);
+ }
+ }
+}
+
+/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/
+static unsigned deflateDynamic(LodePNGBitWriter* writer, Hash* hash,
+ const unsigned char* data, size_t datapos, size_t dataend,
+ const LodePNGCompressSettings* settings, unsigned final) {
+ unsigned error = 0;
+
+ /*
+ A block is compressed as follows: The PNG data is lz77 encoded, resulting in
+ literal bytes and length/distance pairs. This is then huffman compressed with
+ two huffman trees. One huffman tree is used for the lit and len values ("ll"),
+ another huffman tree is used for the dist values ("d"). These two trees are
+ stored using their code lengths, and to compress even more these code lengths
+ are also run-length encoded and huffman compressed. This gives a huffman tree
+ of code lengths "cl". The code lengths used to describe this third tree are
+ the code length code lengths ("clcl").
+ */
+
+ /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/
+ uivector lz77_encoded;
+ HuffmanTree tree_ll; /*tree for lit,len values*/
+ HuffmanTree tree_d; /*tree for distance codes*/
+ HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/
+ unsigned* frequencies_ll = 0; /*frequency of lit,len codes*/
+ unsigned* frequencies_d = 0; /*frequency of dist codes*/
+ unsigned* frequencies_cl = 0; /*frequency of code length codes*/
+ unsigned* bitlen_lld = 0; /*lit,len,dist code lengths (int bits), literally (without repeat codes).*/
+ unsigned* bitlen_lld_e = 0; /*bitlen_lld encoded with repeat codes (this is a rudimentary run length compression)*/
+ size_t datasize = dataend - datapos;
+
+ /*
+ If we could call "bitlen_cl" the the code length code lengths ("clcl"), that is the bit lengths of codes to represent
+ tree_cl in CLCL_ORDER, then due to the huffman compression of huffman tree representations ("two levels"), there are
+ some analogies:
+ bitlen_lld is to tree_cl what data is to tree_ll and tree_d.
+ bitlen_lld_e is to bitlen_lld what lz77_encoded is to data.
+ bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded.
+ */
+
+ unsigned BFINAL = final;
+ size_t i;
+ size_t numcodes_ll, numcodes_d, numcodes_lld, numcodes_lld_e, numcodes_cl;
+ unsigned HLIT, HDIST, HCLEN;
+
+ uivector_init(&lz77_encoded);
+ HuffmanTree_init(&tree_ll);
+ HuffmanTree_init(&tree_d);
+ HuffmanTree_init(&tree_cl);
+ /* could fit on stack, but >1KB is on the larger side so allocate instead */
+ frequencies_ll = (unsigned*)lodepng_malloc(286 * sizeof(*frequencies_ll));
+ frequencies_d = (unsigned*)lodepng_malloc(30 * sizeof(*frequencies_d));
+ frequencies_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl));
+
+ if(!frequencies_ll || !frequencies_d || !frequencies_cl) error = 83; /*alloc fail*/
+
+ /*This while loop never loops due to a break at the end, it is here to
+ allow breaking out of it to the cleanup phase on error conditions.*/
+ while(!error) {
+ lodepng_memset(frequencies_ll, 0, 286 * sizeof(*frequencies_ll));
+ lodepng_memset(frequencies_d, 0, 30 * sizeof(*frequencies_d));
+ lodepng_memset(frequencies_cl, 0, NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl));
+
+ if(settings->use_lz77) {
+ error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize,
+ settings->minmatch, settings->nicematch, settings->lazymatching);
+ if(error) break;
+ } else {
+ if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/);
+ for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/
+ }
+
+ /*Count the frequencies of lit, len and dist codes*/
+ for(i = 0; i != lz77_encoded.size; ++i) {
+ unsigned symbol = lz77_encoded.data[i];
+ ++frequencies_ll[symbol];
+ if(symbol > 256) {
+ unsigned dist = lz77_encoded.data[i + 2];
+ ++frequencies_d[dist];
+ i += 3;
+ }
+ }
+ frequencies_ll[256] = 1; /*there will be exactly 1 end code, at the end of the block*/
+
+ /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/
+ error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll, 257, 286, 15);
+ if(error) break;
+ /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/
+ error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d, 2, 30, 15);
+ if(error) break;
+
+ numcodes_ll = LODEPNG_MIN(tree_ll.numcodes, 286);
+ numcodes_d = LODEPNG_MIN(tree_d.numcodes, 30);
+ /*store the code lengths of both generated trees in bitlen_lld*/
+ numcodes_lld = numcodes_ll + numcodes_d;
+ bitlen_lld = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld));
+ /*numcodes_lld_e never needs more size than bitlen_lld*/
+ bitlen_lld_e = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld_e));
+ if(!bitlen_lld || !bitlen_lld_e) ERROR_BREAK(83); /*alloc fail*/
+ numcodes_lld_e = 0;
+
+ for(i = 0; i != numcodes_ll; ++i) bitlen_lld[i] = tree_ll.lengths[i];
+ for(i = 0; i != numcodes_d; ++i) bitlen_lld[numcodes_ll + i] = tree_d.lengths[i];
+
+ /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times),
+ 17 (3-10 zeroes), 18 (11-138 zeroes)*/
+ for(i = 0; i != numcodes_lld; ++i) {
+ unsigned j = 0; /*amount of repetitions*/
+ while(i + j + 1 < numcodes_lld && bitlen_lld[i + j + 1] == bitlen_lld[i]) ++j;
+
+ if(bitlen_lld[i] == 0 && j >= 2) /*repeat code for zeroes*/ {
+ ++j; /*include the first zero*/
+ if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ {
+ bitlen_lld_e[numcodes_lld_e++] = 17;
+ bitlen_lld_e[numcodes_lld_e++] = j - 3;
+ } else /*repeat code 18 supports max 138 zeroes*/ {
+ if(j > 138) j = 138;
+ bitlen_lld_e[numcodes_lld_e++] = 18;
+ bitlen_lld_e[numcodes_lld_e++] = j - 11;
+ }
+ i += (j - 1);
+ } else if(j >= 3) /*repeat code for value other than zero*/ {
+ size_t k;
+ unsigned num = j / 6u, rest = j % 6u;
+ bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i];
+ for(k = 0; k < num; ++k) {
+ bitlen_lld_e[numcodes_lld_e++] = 16;
+ bitlen_lld_e[numcodes_lld_e++] = 6 - 3;
+ }
+ if(rest >= 3) {
+ bitlen_lld_e[numcodes_lld_e++] = 16;
+ bitlen_lld_e[numcodes_lld_e++] = rest - 3;
+ }
+ else j -= rest;
+ i += j;
+ } else /*too short to benefit from repeat code*/ {
+ bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i];
+ }
+ }
+
+ /*generate tree_cl, the huffmantree of huffmantrees*/
+ for(i = 0; i != numcodes_lld_e; ++i) {
+ ++frequencies_cl[bitlen_lld_e[i]];
+ /*after a repeat code come the bits that specify the number of repetitions,
+ those don't need to be in the frequencies_cl calculation*/
+ if(bitlen_lld_e[i] >= 16) ++i;
+ }
+
+ error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl,
+ NUM_CODE_LENGTH_CODES, NUM_CODE_LENGTH_CODES, 7);
+ if(error) break;
+
+ /*compute amount of code-length-code-lengths to output*/
+ numcodes_cl = NUM_CODE_LENGTH_CODES;
+ /*trim zeros at the end (using CLCL_ORDER), but minimum size must be 4 (see HCLEN below)*/
+ while(numcodes_cl > 4u && tree_cl.lengths[CLCL_ORDER[numcodes_cl - 1u]] == 0) {
+ numcodes_cl--;
+ }
+
+ /*
+ Write everything into the output
+
+ After the BFINAL and BTYPE, the dynamic block consists out of the following:
+ - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN
+ - (HCLEN+4)*3 bits code lengths of code length alphabet
+ - HLIT + 257 code lengths of lit/length alphabet (encoded using the code length
+ alphabet, + possible repetition codes 16, 17, 18)
+ - HDIST + 1 code lengths of distance alphabet (encoded using the code length
+ alphabet, + possible repetition codes 16, 17, 18)
+ - compressed data
+ - 256 (end code)
+ */
+
+ /*Write block type*/
+ writeBits(writer, BFINAL, 1);
+ writeBits(writer, 0, 1); /*first bit of BTYPE "dynamic"*/
+ writeBits(writer, 1, 1); /*second bit of BTYPE "dynamic"*/
+
+ /*write the HLIT, HDIST and HCLEN values*/
+ /*all three sizes take trimmed ending zeroes into account, done either by HuffmanTree_makeFromFrequencies
+ or in the loop for numcodes_cl above, which saves space. */
+ HLIT = (unsigned)(numcodes_ll - 257);
+ HDIST = (unsigned)(numcodes_d - 1);
+ HCLEN = (unsigned)(numcodes_cl - 4);
+ writeBits(writer, HLIT, 5);
+ writeBits(writer, HDIST, 5);
+ writeBits(writer, HCLEN, 4);
+
+ /*write the code lengths of the code length alphabet ("bitlen_cl")*/
+ for(i = 0; i != numcodes_cl; ++i) writeBits(writer, tree_cl.lengths[CLCL_ORDER[i]], 3);
+
+ /*write the lengths of the lit/len AND the dist alphabet*/
+ for(i = 0; i != numcodes_lld_e; ++i) {
+ writeBitsReversed(writer, tree_cl.codes[bitlen_lld_e[i]], tree_cl.lengths[bitlen_lld_e[i]]);
+ /*extra bits of repeat codes*/
+ if(bitlen_lld_e[i] == 16) writeBits(writer, bitlen_lld_e[++i], 2);
+ else if(bitlen_lld_e[i] == 17) writeBits(writer, bitlen_lld_e[++i], 3);
+ else if(bitlen_lld_e[i] == 18) writeBits(writer, bitlen_lld_e[++i], 7);
+ }
+
+ /*write the compressed data symbols*/
+ writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d);
+ /*error: the length of the end code 256 must be larger than 0*/
+ if(tree_ll.lengths[256] == 0) ERROR_BREAK(64);
+
+ /*write the end code*/
+ writeBitsReversed(writer, tree_ll.codes[256], tree_ll.lengths[256]);
+
+ break; /*end of error-while*/
+ }
+
+ /*cleanup*/
+ uivector_cleanup(&lz77_encoded);
+ HuffmanTree_cleanup(&tree_ll);
+ HuffmanTree_cleanup(&tree_d);
+ HuffmanTree_cleanup(&tree_cl);
+ lodepng_free(frequencies_ll);
+ lodepng_free(frequencies_d);
+ lodepng_free(frequencies_cl);
+ lodepng_free(bitlen_lld);
+ lodepng_free(bitlen_lld_e);
+
+ return error;
+}
+
+static unsigned deflateFixed(LodePNGBitWriter* writer, Hash* hash,
+ const unsigned char* data,
+ size_t datapos, size_t dataend,
+ const LodePNGCompressSettings* settings, unsigned final) {
+ HuffmanTree tree_ll; /*tree for literal values and length codes*/
+ HuffmanTree tree_d; /*tree for distance codes*/
+
+ unsigned BFINAL = final;
+ unsigned error = 0;
+ size_t i;
+
+ HuffmanTree_init(&tree_ll);
+ HuffmanTree_init(&tree_d);
+
+ error = generateFixedLitLenTree(&tree_ll);
+ if(!error) error = generateFixedDistanceTree(&tree_d);
+
+ if(!error) {
+ writeBits(writer, BFINAL, 1);
+ writeBits(writer, 1, 1); /*first bit of BTYPE*/
+ writeBits(writer, 0, 1); /*second bit of BTYPE*/
+
+ if(settings->use_lz77) /*LZ77 encoded*/ {
+ uivector lz77_encoded;
+ uivector_init(&lz77_encoded);
+ error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize,
+ settings->minmatch, settings->nicematch, settings->lazymatching);
+ if(!error) writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d);
+ uivector_cleanup(&lz77_encoded);
+ } else /*no LZ77, but still will be Huffman compressed*/ {
+ for(i = datapos; i < dataend; ++i) {
+ writeBitsReversed(writer, tree_ll.codes[data[i]], tree_ll.lengths[data[i]]);
+ }
+ }
+ /*add END code*/
+ if(!error) writeBitsReversed(writer,tree_ll.codes[256], tree_ll.lengths[256]);
+ }
+
+ /*cleanup*/
+ HuffmanTree_cleanup(&tree_ll);
+ HuffmanTree_cleanup(&tree_d);
+
+ return error;
+}
+
+static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize,
+ const LodePNGCompressSettings* settings) {
+ unsigned error = 0;
+ size_t i, blocksize, numdeflateblocks;
+ Hash hash;
+ LodePNGBitWriter writer;
+
+ LodePNGBitWriter_init(&writer, out);
+
+ if(settings->btype > 2) return 61;
+ else if(settings->btype == 0) return deflateNoCompression(out, in, insize);
+ else if(settings->btype == 1) blocksize = insize;
+ else /*if(settings->btype == 2)*/ {
+ /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/
+ blocksize = insize / 8u + 8;
+ if(blocksize < 65536) blocksize = 65536;
+ if(blocksize > 262144) blocksize = 262144;
+ }
+
+ numdeflateblocks = (insize + blocksize - 1) / blocksize;
+ if(numdeflateblocks == 0) numdeflateblocks = 1;
+
+ error = hash_init(&hash, settings->windowsize);
+
+ if(!error) {
+ for(i = 0; i != numdeflateblocks && !error; ++i) {
+ unsigned final = (i == numdeflateblocks - 1);
+ size_t start = i * blocksize;
+ size_t end = start + blocksize;
+ if(end > insize) end = insize;
+
+ if(settings->btype == 1) error = deflateFixed(&writer, &hash, in, start, end, settings, final);
+ else if(settings->btype == 2) error = deflateDynamic(&writer, &hash, in, start, end, settings, final);
+ }
+ }
+
+ hash_cleanup(&hash);
+
+ return error;
+}
+
+unsigned lodepng_deflate(unsigned char** out, size_t* outsize,
+ const unsigned char* in, size_t insize,
+ const LodePNGCompressSettings* settings) {
+ ucvector v = ucvector_init(*out, *outsize);
+ unsigned error = lodepng_deflatev(&v, in, insize, settings);
+ *out = v.data;
+ *outsize = v.size;
+ return error;
+}
+
+static unsigned deflate(unsigned char** out, size_t* outsize,
+ const unsigned char* in, size_t insize,
+ const LodePNGCompressSettings* settings) {
+ if(settings->custom_deflate) {
+ return settings->custom_deflate(out, outsize, in, insize, settings);
+ } else {
+ return lodepng_deflate(out, outsize, in, insize, settings);
+ }
+}
+
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Adler32 / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) {
+ unsigned s1 = adler & 0xffffu;
+ unsigned s2 = (adler >> 16u) & 0xffffu;
+
+ while(len != 0u) {
+ unsigned i;
+ /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/
+ unsigned amount = len > 5552u ? 5552u : len;
+ len -= amount;
+ for(i = 0; i != amount; ++i) {
+ s1 += (*data++);
+ s2 += s1;
+ }
+ s1 %= 65521u;
+ s2 %= 65521u;
+ }
+
+ return (s2 << 16u) | s1;
+}
+
+/*Return the adler32 of the bytes data[0..len-1]*/
+static unsigned adler32(const unsigned char* data, unsigned len) {
+ return update_adler32(1u, data, len);
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Zlib / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#ifdef LODEPNG_COMPILE_DECODER
+
+static unsigned lodepng_zlib_decompressv(ucvector* out,
+ const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings* settings) {
+ unsigned error = 0;
+ unsigned CM, CINFO, FDICT;
+
+ if(insize < 2) return 53; /*error, size of zlib data too small*/
+ /*read information from zlib header*/
+ if((in[0] * 256 + in[1]) % 31 != 0) {
+ /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/
+ return 24;
+ }
+
+ CM = in[0] & 15;
+ CINFO = (in[0] >> 4) & 15;
+ /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/
+ FDICT = (in[1] >> 5) & 1;
+ /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/
+
+ if(CM != 8 || CINFO > 7) {
+ /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/
+ return 25;
+ }
+ if(FDICT != 0) {
+ /*error: the specification of PNG says about the zlib stream:
+ "The additional flags shall not specify a preset dictionary."*/
+ return 26;
+ }
+
+ error = inflatev(out, in + 2, insize - 2, settings);
+ if(error) return error;
+
+ if(!settings->ignore_adler32) {
+ unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]);
+ unsigned checksum = adler32(out->data, (unsigned)(out->size));
+ if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/
+ }
+
+ return 0; /*no error*/
+}
+
+
+unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in,
+ size_t insize, const LodePNGDecompressSettings* settings) {
+ ucvector v = ucvector_init(*out, *outsize);
+ unsigned error = lodepng_zlib_decompressv(&v, in, insize, settings);
+ *out = v.data;
+ *outsize = v.size;
+ return error;
+}
+
+/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
+static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
+ const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
+ if(settings->custom_zlib) {
+ return settings->custom_zlib(out, outsize, in, insize, settings);
+ } else {
+ unsigned error;
+ ucvector v = ucvector_init(*out, *outsize);
+ if(expected_size) {
+ /*reserve the memory to avoid intermediate reallocations*/
+ ucvector_resize(&v, *outsize + expected_size);
+ v.size = *outsize;
+ }
+ error = lodepng_zlib_decompressv(&v, in, insize, settings);
+ *out = v.data;
+ *outsize = v.size;
+ return error;
+ }
+}
+
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#ifdef LODEPNG_COMPILE_ENCODER
+
+unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
+ size_t insize, const LodePNGCompressSettings* settings) {
+ size_t i;
+ unsigned error;
+ unsigned char* deflatedata = 0;
+ size_t deflatesize = 0;
+
+ error = deflate(&deflatedata, &deflatesize, in, insize, settings);
+
+ *out = NULL;
+ *outsize = 0;
+ if(!error) {
+ *outsize = deflatesize + 6;
+ *out = (unsigned char*)lodepng_malloc(*outsize);
+ if(!*out) error = 83; /*alloc fail*/
+ }
+
+ if(!error) {
+ unsigned ADLER32 = adler32(in, (unsigned)insize);
+ /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/
+ unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/
+ unsigned FLEVEL = 0;
+ unsigned FDICT = 0;
+ unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64;
+ unsigned FCHECK = 31 - CMFFLG % 31;
+ CMFFLG += FCHECK;
+
+ (*out)[0] = (unsigned char)(CMFFLG >> 8);
+ (*out)[1] = (unsigned char)(CMFFLG & 255);
+ for(i = 0; i != deflatesize; ++i) (*out)[i + 2] = deflatedata[i];
+ lodepng_set32bitInt(&(*out)[*outsize - 4], ADLER32);
+ }
+
+ lodepng_free(deflatedata);
+ return error;
+}
+
+/* compress using the default or custom zlib function */
+static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
+ size_t insize, const LodePNGCompressSettings* settings) {
+ if(settings->custom_zlib) {
+ return settings->custom_zlib(out, outsize, in, insize, settings);
+ } else {
+ return lodepng_zlib_compress(out, outsize, in, insize, settings);
+ }
+}
+
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+#else /*no LODEPNG_COMPILE_ZLIB*/
+
+#ifdef LODEPNG_COMPILE_DECODER
+static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
+ const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
+ if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
+ (void)expected_size;
+ return settings->custom_zlib(out, outsize, in, insize, settings);
+}
+#endif /*LODEPNG_COMPILE_DECODER*/
+#ifdef LODEPNG_COMPILE_ENCODER
+static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
+ size_t insize, const LodePNGCompressSettings* settings) {
+ if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
+ return settings->custom_zlib(out, outsize, in, insize, settings);
+}
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+#endif /*LODEPNG_COMPILE_ZLIB*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#ifdef LODEPNG_COMPILE_ENCODER
+
+/*this is a good tradeoff between speed and compression ratio*/
+#define DEFAULT_WINDOWSIZE 2048
+
+void lodepng_compress_settings_init(LodePNGCompressSettings* settings) {
+ /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/
+ settings->btype = 2;
+ settings->use_lz77 = 1;
+ settings->windowsize = DEFAULT_WINDOWSIZE;
+ settings->minmatch = 3;
+ settings->nicematch = 128;
+ settings->lazymatching = 1;
+
+ settings->custom_zlib = 0;
+ settings->custom_deflate = 0;
+ settings->custom_context = 0;
+}
+
+const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0};
+
+
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+#ifdef LODEPNG_COMPILE_DECODER
+
+void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) {
+ settings->ignore_adler32 = 0;
+ settings->ignore_nlen = 0;
+
+ settings->custom_zlib = 0;
+ settings->custom_inflate = 0;
+ settings->custom_context = 0;
+}
+
+const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0};
+
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // End of Zlib related code. Begin of PNG related code. // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#ifdef LODEPNG_COMPILE_PNG
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / CRC32 / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+
+#ifndef LODEPNG_NO_COMPILE_CRC
+/* CRC polynomial: 0xedb88320 */
+static unsigned lodepng_crc32_table[256] = {
+ 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u,
+ 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u,
+ 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u,
+ 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u,
+ 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u,
+ 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u,
+ 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u,
+ 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u,
+ 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u,
+ 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u,
+ 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u,
+ 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u,
+ 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u,
+ 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u,
+ 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u,
+ 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u,
+ 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u,
+ 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u,
+ 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u,
+ 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u,
+ 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u,
+ 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u,
+ 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u,
+ 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u,
+ 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u,
+ 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u,
+ 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u,
+ 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u,
+ 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u,
+ 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u,
+ 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u,
+ 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u
+};
+
+/*Return the CRC of the bytes buf[0..len-1].*/
+unsigned lodepng_crc32(const unsigned char* data, size_t length) {
+ unsigned r = 0xffffffffu;
+ size_t i;
+ for(i = 0; i < length; ++i) {
+ r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u);
+ }
+ return r ^ 0xffffffffu;
+}
+#else /* !LODEPNG_NO_COMPILE_CRC */
+unsigned lodepng_crc32(const unsigned char* data, size_t length);
+#endif /* !LODEPNG_NO_COMPILE_CRC */
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Reading and writing PNG color channel bits / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first,
+so LodePNGBitWriter and LodePNGBitReader can't be used for those. */
+
+static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) {
+ unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
+ ++(*bitpointer);
+ return result;
+}
+
+/* TODO: make this faster */
+static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) {
+ unsigned result = 0;
+ size_t i;
+ for(i = 0 ; i < nbits; ++i) {
+ result <<= 1u;
+ result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream);
+ }
+ return result;
+}
+
+static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) {
+ /*the current bit in bitstream may be 0 or 1 for this to work*/
+ if(bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u))));
+ else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u)));
+ ++(*bitpointer);
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / PNG chunks / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+unsigned lodepng_chunk_length(const unsigned char* chunk) {
+ return lodepng_read32bitInt(&chunk[0]);
+}
+
+void lodepng_chunk_type(char type[5], const unsigned char* chunk) {
+ unsigned i;
+ for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i];
+ type[4] = 0; /*null termination char*/
+}
+
+unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) {
+ if(lodepng_strlen(type) != 4) return 0;
+ return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
+}
+
+unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) {
+ return((chunk[4] & 32) != 0);
+}
+
+unsigned char lodepng_chunk_private(const unsigned char* chunk) {
+ return((chunk[6] & 32) != 0);
+}
+
+unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) {
+ return((chunk[7] & 32) != 0);
+}
+
+unsigned char* lodepng_chunk_data(unsigned char* chunk) {
+ return &chunk[8];
+}
+
+const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) {
+ return &chunk[8];
+}
+
+unsigned lodepng_chunk_check_crc(const unsigned char* chunk) {
+ unsigned length = lodepng_chunk_length(chunk);
+ unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]);
+ /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
+ unsigned checksum = lodepng_crc32(&chunk[4], length + 4);
+ if(CRC != checksum) return 1;
+ else return 0;
+}
+
+void lodepng_chunk_generate_crc(unsigned char* chunk) {
+ unsigned length = lodepng_chunk_length(chunk);
+ unsigned CRC = lodepng_crc32(&chunk[4], length + 4);
+ lodepng_set32bitInt(chunk + 8 + length, CRC);
+}
+
+unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end) {
+ if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
+ if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
+ && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
+ /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
+ return chunk + 8;
+ } else {
+ size_t total_chunk_length;
+ unsigned char* result;
+ if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
+ result = chunk + total_chunk_length;
+ if(result < chunk) return end; /*pointer overflow*/
+ return result;
+ }
+}
+
+const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) {
+ if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
+ if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
+ && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
+ /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
+ return chunk + 8;
+ } else {
+ size_t total_chunk_length;
+ const unsigned char* result;
+ if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
+ result = chunk + total_chunk_length;
+ if(result < chunk) return end; /*pointer overflow*/
+ return result;
+ }
+}
+
+unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]) {
+ for(;;) {
+ if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
+ if(lodepng_chunk_type_equals(chunk, type)) return chunk;
+ chunk = lodepng_chunk_next(chunk, end);
+ }
+}
+
+const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) {
+ for(;;) {
+ if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
+ if(lodepng_chunk_type_equals(chunk, type)) return chunk;
+ chunk = lodepng_chunk_next_const(chunk, end);
+ }
+}
+
+unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk) {
+ unsigned i;
+ size_t total_chunk_length, new_length;
+ unsigned char *chunk_start, *new_buffer;
+
+ if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77;
+ if(lodepng_addofl(*outsize, total_chunk_length, &new_length)) return 77;
+
+ new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
+ if(!new_buffer) return 83; /*alloc fail*/
+ (*out) = new_buffer;
+ (*outsize) = new_length;
+ chunk_start = &(*out)[new_length - total_chunk_length];
+
+ for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i];
+
+ return 0;
+}
+
+/*Sets length and name and allocates the space for data and crc but does not
+set data or crc yet. Returns the start of the chunk in chunk. The start of
+the data is at chunk + 8. To finalize chunk, add the data, then use
+lodepng_chunk_generate_crc */
+static unsigned lodepng_chunk_init(unsigned char** chunk,
+ ucvector* out,
+ unsigned length, const char* type) {
+ size_t new_length = out->size;
+ if(lodepng_addofl(new_length, length, &new_length)) return 77;
+ if(lodepng_addofl(new_length, 12, &new_length)) return 77;
+ if(!ucvector_resize(out, new_length)) return 83; /*alloc fail*/
+ *chunk = out->data + new_length - length - 12u;
+
+ /*1: length*/
+ lodepng_set32bitInt(*chunk, length);
+
+ /*2: chunk name (4 letters)*/
+ lodepng_memcpy(*chunk + 4, type, 4);
+
+ return 0;
+}
+
+/* like lodepng_chunk_create but with custom allocsize */
+static unsigned lodepng_chunk_createv(ucvector* out,
+ unsigned length, const char* type, const unsigned char* data) {
+ unsigned char* chunk;
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, length, type));
+
+ /*3: the data*/
+ lodepng_memcpy(chunk + 8, data, length);
+
+ /*4: CRC (of the chunkname characters and the data)*/
+ lodepng_chunk_generate_crc(chunk);
+
+ return 0;
+}
+
+unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize,
+ unsigned length, const char* type, const unsigned char* data) {
+ ucvector v = ucvector_init(*out, *outsize);
+ unsigned error = lodepng_chunk_createv(&v, length, type, data);
+ *out = v.data;
+ *outsize = v.size;
+ return error;
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Color types, channels, bits / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype.
+Return value is a LodePNG error code.*/
+static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) {
+ switch(colortype) {
+ case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break;
+ case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break;
+ case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */
+ default: return 31; /* invalid color type */
+ }
+ return 0; /*allowed color type / bits combination*/
+}
+
+static unsigned getNumColorChannels(LodePNGColorType colortype) {
+ switch(colortype) {
+ case LCT_GREY: return 1;
+ case LCT_RGB: return 3;
+ case LCT_PALETTE: return 1;
+ case LCT_GREY_ALPHA: return 2;
+ case LCT_RGBA: return 4;
+ case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */
+ default: return 0; /*invalid color type*/
+ }
+}
+
+static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) {
+ /*bits per pixel is amount of channels * bits per channel*/
+ return getNumColorChannels(colortype) * bitdepth;
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+void lodepng_color_mode_init(LodePNGColorMode* info) {
+ info->key_defined = 0;
+ info->key_r = info->key_g = info->key_b = 0;
+ info->colortype = LCT_RGBA;
+ info->bitdepth = 8;
+ info->palette = 0;
+ info->palettesize = 0;
+}
+
+/*allocates palette memory if needed, and initializes all colors to black*/
+static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info) {
+ size_t i;
+ /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/
+ /*the palette must have room for up to 256 colors with 4 bytes each.*/
+ if(!info->palette) info->palette = (unsigned char*)lodepng_malloc(1024);
+ if(!info->palette) return; /*alloc fail*/
+ for(i = 0; i != 256; ++i) {
+ /*Initialize all unused colors with black, the value used for invalid palette indices.
+ This is an error according to the PNG spec, but common PNG decoders make it black instead.
+ That makes color conversion slightly faster due to no error handling needed.*/
+ info->palette[i * 4 + 0] = 0;
+ info->palette[i * 4 + 1] = 0;
+ info->palette[i * 4 + 2] = 0;
+ info->palette[i * 4 + 3] = 255;
+ }
+}
+
+void lodepng_color_mode_cleanup(LodePNGColorMode* info) {
+ lodepng_palette_clear(info);
+}
+
+unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) {
+ lodepng_color_mode_cleanup(dest);
+ lodepng_memcpy(dest, source, sizeof(LodePNGColorMode));
+ if(source->palette) {
+ dest->palette = (unsigned char*)lodepng_malloc(1024);
+ if(!dest->palette && source->palettesize) return 83; /*alloc fail*/
+ lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4);
+ }
+ return 0;
+}
+
+LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth) {
+ LodePNGColorMode result;
+ lodepng_color_mode_init(&result);
+ result.colortype = colortype;
+ result.bitdepth = bitdepth;
+ return result;
+}
+
+static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) {
+ size_t i;
+ if(a->colortype != b->colortype) return 0;
+ if(a->bitdepth != b->bitdepth) return 0;
+ if(a->key_defined != b->key_defined) return 0;
+ if(a->key_defined) {
+ if(a->key_r != b->key_r) return 0;
+ if(a->key_g != b->key_g) return 0;
+ if(a->key_b != b->key_b) return 0;
+ }
+ if(a->palettesize != b->palettesize) return 0;
+ for(i = 0; i != a->palettesize * 4; ++i) {
+ if(a->palette[i] != b->palette[i]) return 0;
+ }
+ return 1;
+}
+
+void lodepng_palette_clear(LodePNGColorMode* info) {
+ if(info->palette) lodepng_free(info->palette);
+ info->palette = 0;
+ info->palettesize = 0;
+}
+
+unsigned lodepng_palette_add(LodePNGColorMode* info,
+ unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
+ if(!info->palette) /*allocate palette if empty*/ {
+ lodepng_color_mode_alloc_palette(info);
+ if(!info->palette) return 83; /*alloc fail*/
+ }
+ if(info->palettesize >= 256) {
+ return 108; /*too many palette values*/
+ }
+ info->palette[4 * info->palettesize + 0] = r;
+ info->palette[4 * info->palettesize + 1] = g;
+ info->palette[4 * info->palettesize + 2] = b;
+ info->palette[4 * info->palettesize + 3] = a;
+ ++info->palettesize;
+ return 0;
+}
+
+/*calculate bits per pixel out of colortype and bitdepth*/
+unsigned lodepng_get_bpp(const LodePNGColorMode* info) {
+ return lodepng_get_bpp_lct(info->colortype, info->bitdepth);
+}
+
+unsigned lodepng_get_channels(const LodePNGColorMode* info) {
+ return getNumColorChannels(info->colortype);
+}
+
+unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) {
+ return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA;
+}
+
+unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) {
+ return (info->colortype & 4) != 0; /*4 or 6*/
+}
+
+unsigned lodepng_is_palette_type(const LodePNGColorMode* info) {
+ return info->colortype == LCT_PALETTE;
+}
+
+unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) {
+ size_t i;
+ for(i = 0; i != info->palettesize; ++i) {
+ if(info->palette[i * 4 + 3] < 255) return 1;
+ }
+ return 0;
+}
+
+unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) {
+ return info->key_defined
+ || lodepng_is_alpha_type(info)
+ || lodepng_has_palette_alpha(info);
+}
+
+static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) {
+ size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
+ size_t n = (size_t)w * (size_t)h;
+ return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u;
+}
+
+size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) {
+ return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth);
+}
+
+
+#ifdef LODEPNG_COMPILE_PNG
+
+/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer,
+and in addition has one extra byte per line: the filter byte. So this gives a larger
+result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */
+static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) {
+ /* + 1 for the filter byte, and possibly plus padding bits per line. */
+ /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */
+ size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u;
+ return (size_t)h * line;
+}
+
+#ifdef LODEPNG_COMPILE_DECODER
+/*Safely checks whether size_t overflow can be caused due to amount of pixels.
+This check is overcautious rather than precise. If this check indicates no overflow,
+you can safely compute in a size_t (but not an unsigned):
+-(size_t)w * (size_t)h * 8
+-amount of bytes in IDAT (including filter, padding and Adam7 bytes)
+-amount of bytes in raw color model
+Returns 1 if overflow possible, 0 if not.
+*/
+static int lodepng_pixel_overflow(unsigned w, unsigned h,
+ const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) {
+ size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor));
+ size_t numpixels, total;
+ size_t line; /* bytes per line in worst case */
+
+ if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1;
+ if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */
+
+ /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */
+ if(lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1;
+ if(lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1;
+
+ if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */
+ if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */
+
+ return 0; /* no overflow */
+}
+#endif /*LODEPNG_COMPILE_DECODER*/
+#endif /*LODEPNG_COMPILE_PNG*/
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+
+static void LodePNGUnknownChunks_init(LodePNGInfo* info) {
+ unsigned i;
+ for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0;
+ for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0;
+}
+
+static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) {
+ unsigned i;
+ for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]);
+}
+
+static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) {
+ unsigned i;
+
+ LodePNGUnknownChunks_cleanup(dest);
+
+ for(i = 0; i != 3; ++i) {
+ size_t j;
+ dest->unknown_chunks_size[i] = src->unknown_chunks_size[i];
+ dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]);
+ if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/
+ for(j = 0; j < src->unknown_chunks_size[i]; ++j) {
+ dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j];
+ }
+ }
+
+ return 0;
+}
+
+/******************************************************************************/
+
+static void LodePNGText_init(LodePNGInfo* info) {
+ info->text_num = 0;
+ info->text_keys = NULL;
+ info->text_strings = NULL;
+}
+
+static void LodePNGText_cleanup(LodePNGInfo* info) {
+ size_t i;
+ for(i = 0; i != info->text_num; ++i) {
+ string_cleanup(&info->text_keys[i]);
+ string_cleanup(&info->text_strings[i]);
+ }
+ lodepng_free(info->text_keys);
+ lodepng_free(info->text_strings);
+}
+
+static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
+ size_t i = 0;
+ dest->text_keys = 0;
+ dest->text_strings = 0;
+ dest->text_num = 0;
+ for(i = 0; i != source->text_num; ++i) {
+ CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i]));
+ }
+ return 0;
+}
+
+static unsigned lodepng_add_text_sized(LodePNGInfo* info, const char* key, const char* str, size_t size) {
+ char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1)));
+ char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1)));
+
+ if(new_keys) info->text_keys = new_keys;
+ if(new_strings) info->text_strings = new_strings;
+
+ if(!new_keys || !new_strings) return 83; /*alloc fail*/
+
+ ++info->text_num;
+ info->text_keys[info->text_num - 1] = alloc_string(key);
+ info->text_strings[info->text_num - 1] = alloc_string_sized(str, size);
+ if(!info->text_keys[info->text_num - 1] || !info->text_strings[info->text_num - 1]) return 83; /*alloc fail*/
+
+ return 0;
+}
+
+unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) {
+ return lodepng_add_text_sized(info, key, str, lodepng_strlen(str));
+}
+
+void lodepng_clear_text(LodePNGInfo* info) {
+ LodePNGText_cleanup(info);
+}
+
+/******************************************************************************/
+
+static void LodePNGIText_init(LodePNGInfo* info) {
+ info->itext_num = 0;
+ info->itext_keys = NULL;
+ info->itext_langtags = NULL;
+ info->itext_transkeys = NULL;
+ info->itext_strings = NULL;
+}
+
+static void LodePNGIText_cleanup(LodePNGInfo* info) {
+ size_t i;
+ for(i = 0; i != info->itext_num; ++i) {
+ string_cleanup(&info->itext_keys[i]);
+ string_cleanup(&info->itext_langtags[i]);
+ string_cleanup(&info->itext_transkeys[i]);
+ string_cleanup(&info->itext_strings[i]);
+ }
+ lodepng_free(info->itext_keys);
+ lodepng_free(info->itext_langtags);
+ lodepng_free(info->itext_transkeys);
+ lodepng_free(info->itext_strings);
+}
+
+static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
+ size_t i = 0;
+ dest->itext_keys = 0;
+ dest->itext_langtags = 0;
+ dest->itext_transkeys = 0;
+ dest->itext_strings = 0;
+ dest->itext_num = 0;
+ for(i = 0; i != source->itext_num; ++i) {
+ CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i],
+ source->itext_transkeys[i], source->itext_strings[i]));
+ }
+ return 0;
+}
+
+void lodepng_clear_itext(LodePNGInfo* info) {
+ LodePNGIText_cleanup(info);
+}
+
+static unsigned lodepng_add_itext_sized(LodePNGInfo* info, const char* key, const char* langtag,
+ const char* transkey, const char* str, size_t size) {
+ char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1)));
+ char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1)));
+ char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1)));
+ char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1)));
+
+ if(new_keys) info->itext_keys = new_keys;
+ if(new_langtags) info->itext_langtags = new_langtags;
+ if(new_transkeys) info->itext_transkeys = new_transkeys;
+ if(new_strings) info->itext_strings = new_strings;
+
+ if(!new_keys || !new_langtags || !new_transkeys || !new_strings) return 83; /*alloc fail*/
+
+ ++info->itext_num;
+
+ info->itext_keys[info->itext_num - 1] = alloc_string(key);
+ info->itext_langtags[info->itext_num - 1] = alloc_string(langtag);
+ info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey);
+ info->itext_strings[info->itext_num - 1] = alloc_string_sized(str, size);
+
+ return 0;
+}
+
+unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag,
+ const char* transkey, const char* str) {
+ return lodepng_add_itext_sized(info, key, langtag, transkey, str, lodepng_strlen(str));
+}
+
+/* same as set but does not delete */
+static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) {
+ if(profile_size == 0) return 100; /*invalid ICC profile size*/
+
+ info->iccp_name = alloc_string(name);
+ info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size);
+
+ if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/
+
+ lodepng_memcpy(info->iccp_profile, profile, profile_size);
+ info->iccp_profile_size = profile_size;
+
+ return 0; /*ok*/
+}
+
+unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) {
+ if(info->iccp_name) lodepng_clear_icc(info);
+ info->iccp_defined = 1;
+
+ return lodepng_assign_icc(info, name, profile, profile_size);
+}
+
+void lodepng_clear_icc(LodePNGInfo* info) {
+ string_cleanup(&info->iccp_name);
+ lodepng_free(info->iccp_profile);
+ info->iccp_profile = NULL;
+ info->iccp_profile_size = 0;
+ info->iccp_defined = 0;
+}
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+void lodepng_info_init(LodePNGInfo* info) {
+ lodepng_color_mode_init(&info->color);
+ info->interlace_method = 0;
+ info->compression_method = 0;
+ info->filter_method = 0;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ info->background_defined = 0;
+ info->background_r = info->background_g = info->background_b = 0;
+
+ LodePNGText_init(info);
+ LodePNGIText_init(info);
+
+ info->time_defined = 0;
+ info->phys_defined = 0;
+
+ info->gama_defined = 0;
+ info->chrm_defined = 0;
+ info->srgb_defined = 0;
+ info->iccp_defined = 0;
+ info->iccp_name = NULL;
+ info->iccp_profile = NULL;
+
+ LodePNGUnknownChunks_init(info);
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+}
+
+void lodepng_info_cleanup(LodePNGInfo* info) {
+ lodepng_color_mode_cleanup(&info->color);
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ LodePNGText_cleanup(info);
+ LodePNGIText_cleanup(info);
+
+ lodepng_clear_icc(info);
+
+ LodePNGUnknownChunks_cleanup(info);
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+}
+
+unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
+ lodepng_info_cleanup(dest);
+ lodepng_memcpy(dest, source, sizeof(LodePNGInfo));
+ lodepng_color_mode_init(&dest->color);
+ CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color));
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ CERROR_TRY_RETURN(LodePNGText_copy(dest, source));
+ CERROR_TRY_RETURN(LodePNGIText_copy(dest, source));
+ if(source->iccp_defined) {
+ CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size));
+ }
+
+ LodePNGUnknownChunks_init(dest);
+ CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source));
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ return 0;
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/
+static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) {
+ unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/
+ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/
+ unsigned p = index & m;
+ in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/
+ in = in << (bits * (m - p));
+ if(p == 0) out[index * bits / 8u] = in;
+ else out[index * bits / 8u] |= in;
+}
+
+typedef struct ColorTree ColorTree;
+
+/*
+One node of a color tree
+This is the data structure used to count the number of unique colors and to get a palette
+index for a color. It's like an octree, but because the alpha channel is used too, each
+node has 16 instead of 8 children.
+*/
+struct ColorTree {
+ ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/
+ int index; /*the payload. Only has a meaningful value if this is in the last level*/
+};
+
+static void color_tree_init(ColorTree* tree) {
+ lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children));
+ tree->index = -1;
+}
+
+static void color_tree_cleanup(ColorTree* tree) {
+ int i;
+ for(i = 0; i != 16; ++i) {
+ if(tree->children[i]) {
+ color_tree_cleanup(tree->children[i]);
+ lodepng_free(tree->children[i]);
+ }
+ }
+}
+
+/*returns -1 if color not present, its index otherwise*/
+static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
+ int bit = 0;
+ for(bit = 0; bit < 8; ++bit) {
+ int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
+ if(!tree->children[i]) return -1;
+ else tree = tree->children[i];
+ }
+ return tree ? tree->index : -1;
+}
+
+#ifdef LODEPNG_COMPILE_ENCODER
+static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
+ return color_tree_get(tree, r, g, b, a) >= 0;
+}
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+/*color is not allowed to already exist.
+Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")
+Returns error code, or 0 if ok*/
+static unsigned color_tree_add(ColorTree* tree,
+ unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) {
+ int bit;
+ for(bit = 0; bit < 8; ++bit) {
+ int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
+ if(!tree->children[i]) {
+ tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree));
+ if(!tree->children[i]) return 83; /*alloc fail*/
+ color_tree_init(tree->children[i]);
+ }
+ tree = tree->children[i];
+ }
+ tree->index = (int)index;
+ return 0;
+}
+
+/*put a pixel, given its RGBA color, into image of any color type*/
+static unsigned rgba8ToPixel(unsigned char* out, size_t i,
+ const LodePNGColorMode* mode, ColorTree* tree /*for palette*/,
+ unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
+ if(mode->colortype == LCT_GREY) {
+ unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
+ if(mode->bitdepth == 8) out[i] = gray;
+ else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray;
+ else {
+ /*take the most significant bits of gray*/
+ gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u);
+ addColorBits(out, i, mode->bitdepth, gray);
+ }
+ } else if(mode->colortype == LCT_RGB) {
+ if(mode->bitdepth == 8) {
+ out[i * 3 + 0] = r;
+ out[i * 3 + 1] = g;
+ out[i * 3 + 2] = b;
+ } else {
+ out[i * 6 + 0] = out[i * 6 + 1] = r;
+ out[i * 6 + 2] = out[i * 6 + 3] = g;
+ out[i * 6 + 4] = out[i * 6 + 5] = b;
+ }
+ } else if(mode->colortype == LCT_PALETTE) {
+ int index = color_tree_get(tree, r, g, b, a);
+ if(index < 0) return 82; /*color not in palette*/
+ if(mode->bitdepth == 8) out[i] = index;
+ else addColorBits(out, i, mode->bitdepth, (unsigned)index);
+ } else if(mode->colortype == LCT_GREY_ALPHA) {
+ unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
+ if(mode->bitdepth == 8) {
+ out[i * 2 + 0] = gray;
+ out[i * 2 + 1] = a;
+ } else if(mode->bitdepth == 16) {
+ out[i * 4 + 0] = out[i * 4 + 1] = gray;
+ out[i * 4 + 2] = out[i * 4 + 3] = a;
+ }
+ } else if(mode->colortype == LCT_RGBA) {
+ if(mode->bitdepth == 8) {
+ out[i * 4 + 0] = r;
+ out[i * 4 + 1] = g;
+ out[i * 4 + 2] = b;
+ out[i * 4 + 3] = a;
+ } else {
+ out[i * 8 + 0] = out[i * 8 + 1] = r;
+ out[i * 8 + 2] = out[i * 8 + 3] = g;
+ out[i * 8 + 4] = out[i * 8 + 5] = b;
+ out[i * 8 + 6] = out[i * 8 + 7] = a;
+ }
+ }
+
+ return 0; /*no error*/
+}
+
+/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/
+static void rgba16ToPixel(unsigned char* out, size_t i,
+ const LodePNGColorMode* mode,
+ unsigned short r, unsigned short g, unsigned short b, unsigned short a) {
+ if(mode->colortype == LCT_GREY) {
+ unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
+ out[i * 2 + 0] = (gray >> 8) & 255;
+ out[i * 2 + 1] = gray & 255;
+ } else if(mode->colortype == LCT_RGB) {
+ out[i * 6 + 0] = (r >> 8) & 255;
+ out[i * 6 + 1] = r & 255;
+ out[i * 6 + 2] = (g >> 8) & 255;
+ out[i * 6 + 3] = g & 255;
+ out[i * 6 + 4] = (b >> 8) & 255;
+ out[i * 6 + 5] = b & 255;
+ } else if(mode->colortype == LCT_GREY_ALPHA) {
+ unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
+ out[i * 4 + 0] = (gray >> 8) & 255;
+ out[i * 4 + 1] = gray & 255;
+ out[i * 4 + 2] = (a >> 8) & 255;
+ out[i * 4 + 3] = a & 255;
+ } else if(mode->colortype == LCT_RGBA) {
+ out[i * 8 + 0] = (r >> 8) & 255;
+ out[i * 8 + 1] = r & 255;
+ out[i * 8 + 2] = (g >> 8) & 255;
+ out[i * 8 + 3] = g & 255;
+ out[i * 8 + 4] = (b >> 8) & 255;
+ out[i * 8 + 5] = b & 255;
+ out[i * 8 + 6] = (a >> 8) & 255;
+ out[i * 8 + 7] = a & 255;
+ }
+}
+
+/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/
+static void getPixelColorRGBA8(unsigned char* r, unsigned char* g,
+ unsigned char* b, unsigned char* a,
+ const unsigned char* in, size_t i,
+ const LodePNGColorMode* mode) {
+ if(mode->colortype == LCT_GREY) {
+ if(mode->bitdepth == 8) {
+ *r = *g = *b = in[i];
+ if(mode->key_defined && *r == mode->key_r) *a = 0;
+ else *a = 255;
+ } else if(mode->bitdepth == 16) {
+ *r = *g = *b = in[i * 2 + 0];
+ if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
+ else *a = 255;
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
+ size_t j = i * mode->bitdepth;
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ *r = *g = *b = (value * 255) / highest;
+ if(mode->key_defined && value == mode->key_r) *a = 0;
+ else *a = 255;
+ }
+ } else if(mode->colortype == LCT_RGB) {
+ if(mode->bitdepth == 8) {
+ *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2];
+ if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0;
+ else *a = 255;
+ } else {
+ *r = in[i * 6 + 0];
+ *g = in[i * 6 + 2];
+ *b = in[i * 6 + 4];
+ if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
+ else *a = 255;
+ }
+ } else if(mode->colortype == LCT_PALETTE) {
+ unsigned index;
+ if(mode->bitdepth == 8) index = in[i];
+ else {
+ size_t j = i * mode->bitdepth;
+ index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ }
+ /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
+ *r = mode->palette[index * 4 + 0];
+ *g = mode->palette[index * 4 + 1];
+ *b = mode->palette[index * 4 + 2];
+ *a = mode->palette[index * 4 + 3];
+ } else if(mode->colortype == LCT_GREY_ALPHA) {
+ if(mode->bitdepth == 8) {
+ *r = *g = *b = in[i * 2 + 0];
+ *a = in[i * 2 + 1];
+ } else {
+ *r = *g = *b = in[i * 4 + 0];
+ *a = in[i * 4 + 2];
+ }
+ } else if(mode->colortype == LCT_RGBA) {
+ if(mode->bitdepth == 8) {
+ *r = in[i * 4 + 0];
+ *g = in[i * 4 + 1];
+ *b = in[i * 4 + 2];
+ *a = in[i * 4 + 3];
+ } else {
+ *r = in[i * 8 + 0];
+ *g = in[i * 8 + 2];
+ *b = in[i * 8 + 4];
+ *a = in[i * 8 + 6];
+ }
+ }
+}
+
+/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color
+mode test cases, optimized to convert the colors much faster, when converting
+to the common case of RGBA with 8 bit per channel. buffer must be RGBA with
+enough memory.*/
+static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels,
+ const unsigned char* LODEPNG_RESTRICT in,
+ const LodePNGColorMode* mode) {
+ unsigned num_channels = 4;
+ size_t i;
+ if(mode->colortype == LCT_GREY) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i];
+ buffer[3] = 255;
+ }
+ if(mode->key_defined) {
+ buffer -= numpixels * num_channels;
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ if(buffer[0] == mode->key_r) buffer[3] = 0;
+ }
+ }
+ } else if(mode->bitdepth == 16) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2];
+ buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255;
+ }
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
+ size_t j = 0;
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
+ buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255;
+ }
+ }
+ } else if(mode->colortype == LCT_RGB) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ lodepng_memcpy(buffer, &in[i * 3], 3);
+ buffer[3] = 255;
+ }
+ if(mode->key_defined) {
+ buffer -= numpixels * num_channels;
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ if(buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0;
+ }
+ }
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 6 + 0];
+ buffer[1] = in[i * 6 + 2];
+ buffer[2] = in[i * 6 + 4];
+ buffer[3] = mode->key_defined
+ && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255;
+ }
+ }
+ } else if(mode->colortype == LCT_PALETTE) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = in[i];
+ /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
+ }
+ } else {
+ size_t j = 0;
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
+ }
+ }
+ } else if(mode->colortype == LCT_GREY_ALPHA) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
+ buffer[3] = in[i * 2 + 1];
+ }
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
+ buffer[3] = in[i * 4 + 2];
+ }
+ }
+ } else if(mode->colortype == LCT_RGBA) {
+ if(mode->bitdepth == 8) {
+ lodepng_memcpy(buffer, in, numpixels * 4);
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 8 + 0];
+ buffer[1] = in[i * 8 + 2];
+ buffer[2] = in[i * 8 + 4];
+ buffer[3] = in[i * 8 + 6];
+ }
+ }
+ }
+}
+
+/*Similar to getPixelColorsRGBA8, but with 3-channel RGB output.*/
+static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels,
+ const unsigned char* LODEPNG_RESTRICT in,
+ const LodePNGColorMode* mode) {
+ const unsigned num_channels = 3;
+ size_t i;
+ if(mode->colortype == LCT_GREY) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i];
+ }
+ } else if(mode->bitdepth == 16) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2];
+ }
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
+ size_t j = 0;
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
+ }
+ }
+ } else if(mode->colortype == LCT_RGB) {
+ if(mode->bitdepth == 8) {
+ lodepng_memcpy(buffer, in, numpixels * 3);
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 6 + 0];
+ buffer[1] = in[i * 6 + 2];
+ buffer[2] = in[i * 6 + 4];
+ }
+ }
+ } else if(mode->colortype == LCT_PALETTE) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = in[i];
+ /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
+ }
+ } else {
+ size_t j = 0;
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
+ }
+ }
+ } else if(mode->colortype == LCT_GREY_ALPHA) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
+ }
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
+ }
+ }
+ } else if(mode->colortype == LCT_RGBA) {
+ if(mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ lodepng_memcpy(buffer, &in[i * 4], 3);
+ }
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 8 + 0];
+ buffer[1] = in[i * 8 + 2];
+ buffer[2] = in[i * 8 + 4];
+ }
+ }
+ }
+}
+
+/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with
+given color type, but the given color type must be 16-bit itself.*/
+static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a,
+ const unsigned char* in, size_t i, const LodePNGColorMode* mode) {
+ if(mode->colortype == LCT_GREY) {
+ *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1];
+ if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
+ else *a = 65535;
+ } else if(mode->colortype == LCT_RGB) {
+ *r = 256u * in[i * 6 + 0] + in[i * 6 + 1];
+ *g = 256u * in[i * 6 + 2] + in[i * 6 + 3];
+ *b = 256u * in[i * 6 + 4] + in[i * 6 + 5];
+ if(mode->key_defined
+ && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
+ else *a = 65535;
+ } else if(mode->colortype == LCT_GREY_ALPHA) {
+ *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1];
+ *a = 256u * in[i * 4 + 2] + in[i * 4 + 3];
+ } else if(mode->colortype == LCT_RGBA) {
+ *r = 256u * in[i * 8 + 0] + in[i * 8 + 1];
+ *g = 256u * in[i * 8 + 2] + in[i * 8 + 3];
+ *b = 256u * in[i * 8 + 4] + in[i * 8 + 5];
+ *a = 256u * in[i * 8 + 6] + in[i * 8 + 7];
+ }
+}
+
+unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
+ const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in,
+ unsigned w, unsigned h) {
+ size_t i;
+ ColorTree tree;
+ size_t numpixels = (size_t)w * (size_t)h;
+ unsigned error = 0;
+
+ if(mode_in->colortype == LCT_PALETTE && !mode_in->palette) {
+ return 107; /* error: must provide palette if input mode is palette */
+ }
+
+ if(lodepng_color_mode_equal(mode_out, mode_in)) {
+ size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
+ lodepng_memcpy(out, in, numbytes);
+ return 0;
+ }
+
+ if(mode_out->colortype == LCT_PALETTE) {
+ size_t palettesize = mode_out->palettesize;
+ const unsigned char* palette = mode_out->palette;
+ size_t palsize = (size_t)1u << mode_out->bitdepth;
+ /*if the user specified output palette but did not give the values, assume
+ they want the values of the input color type (assuming that one is palette).
+ Note that we never create a new palette ourselves.*/
+ if(palettesize == 0) {
+ palettesize = mode_in->palettesize;
+ palette = mode_in->palette;
+ /*if the input was also palette with same bitdepth, then the color types are also
+ equal, so copy literally. This to preserve the exact indices that were in the PNG
+ even in case there are duplicate colors in the palette.*/
+ if(mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) {
+ size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
+ lodepng_memcpy(out, in, numbytes);
+ return 0;
+ }
+ }
+ if(palettesize < palsize) palsize = palettesize;
+ color_tree_init(&tree);
+ for(i = 0; i != palsize; ++i) {
+ const unsigned char* p = &palette[i * 4];
+ error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i);
+ if(error) break;
+ }
+ }
+
+ if(!error) {
+ if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) {
+ for(i = 0; i != numpixels; ++i) {
+ unsigned short r = 0, g = 0, b = 0, a = 0;
+ getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
+ rgba16ToPixel(out, i, mode_out, r, g, b, a);
+ }
+ } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) {
+ getPixelColorsRGBA8(out, numpixels, in, mode_in);
+ } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) {
+ getPixelColorsRGB8(out, numpixels, in, mode_in);
+ } else {
+ unsigned char r = 0, g = 0, b = 0, a = 0;
+ for(i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
+ error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a);
+ if(error) break;
+ }
+ }
+ }
+
+ if(mode_out->colortype == LCT_PALETTE) {
+ color_tree_cleanup(&tree);
+ }
+
+ return error;
+}
+
+
+/* Converts a single rgb color without alpha from one type to another, color bits truncated to
+their bitdepth. In case of single channel (gray or palette), only the r channel is used. Slow
+function, do not use to process all pixels of an image. Alpha channel not supported on purpose:
+this is for bKGD, supporting alpha may prevent it from finding a color in the palette, from the
+specification it looks like bKGD should ignore the alpha values of the palette since it can use
+any palette index but doesn't have an alpha channel. Idem with ignoring color key. */
+unsigned lodepng_convert_rgb(
+ unsigned* r_out, unsigned* g_out, unsigned* b_out,
+ unsigned r_in, unsigned g_in, unsigned b_in,
+ const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in) {
+ unsigned r = 0, g = 0, b = 0;
+ unsigned mul = 65535 / ((1u << mode_in->bitdepth) - 1u); /*65535, 21845, 4369, 257, 1*/
+ unsigned shift = 16 - mode_out->bitdepth;
+
+ if(mode_in->colortype == LCT_GREY || mode_in->colortype == LCT_GREY_ALPHA) {
+ r = g = b = r_in * mul;
+ } else if(mode_in->colortype == LCT_RGB || mode_in->colortype == LCT_RGBA) {
+ r = r_in * mul;
+ g = g_in * mul;
+ b = b_in * mul;
+ } else if(mode_in->colortype == LCT_PALETTE) {
+ if(r_in >= mode_in->palettesize) return 82;
+ r = mode_in->palette[r_in * 4 + 0] * 257u;
+ g = mode_in->palette[r_in * 4 + 1] * 257u;
+ b = mode_in->palette[r_in * 4 + 2] * 257u;
+ } else {
+ return 31;
+ }
+
+ /* now convert to output format */
+ if(mode_out->colortype == LCT_GREY || mode_out->colortype == LCT_GREY_ALPHA) {
+ *r_out = r >> shift ;
+ } else if(mode_out->colortype == LCT_RGB || mode_out->colortype == LCT_RGBA) {
+ *r_out = r >> shift ;
+ *g_out = g >> shift ;
+ *b_out = b >> shift ;
+ } else if(mode_out->colortype == LCT_PALETTE) {
+ unsigned i;
+ /* a 16-bit color cannot be in the palette */
+ if((r >> 8) != (r & 255) || (g >> 8) != (g & 255) || (b >> 8) != (b & 255)) return 82;
+ for(i = 0; i < mode_out->palettesize; i++) {
+ unsigned j = i * 4;
+ if((r >> 8) == mode_out->palette[j + 0] && (g >> 8) == mode_out->palette[j + 1] &&
+ (b >> 8) == mode_out->palette[j + 2]) {
+ *r_out = i;
+ return 0;
+ }
+ }
+ return 82;
+ } else {
+ return 31;
+ }
+
+ return 0;
+}
+
+#ifdef LODEPNG_COMPILE_ENCODER
+
+void lodepng_color_stats_init(LodePNGColorStats* stats) {
+ /*stats*/
+ stats->colored = 0;
+ stats->key = 0;
+ stats->key_r = stats->key_g = stats->key_b = 0;
+ stats->alpha = 0;
+ stats->numcolors = 0;
+ stats->bits = 1;
+ stats->numpixels = 0;
+ /*settings*/
+ stats->allow_palette = 1;
+ stats->allow_greyscale = 1;
+}
+
+/*function used for debug purposes with C++*/
+/*void printColorStats(LodePNGColorStats* p) {
+ std::cout << "colored: " << (int)p->colored << ", ";
+ std::cout << "key: " << (int)p->key << ", ";
+ std::cout << "key_r: " << (int)p->key_r << ", ";
+ std::cout << "key_g: " << (int)p->key_g << ", ";
+ std::cout << "key_b: " << (int)p->key_b << ", ";
+ std::cout << "alpha: " << (int)p->alpha << ", ";
+ std::cout << "numcolors: " << (int)p->numcolors << ", ";
+ std::cout << "bits: " << (int)p->bits << std::endl;
+}*/
+
+/*Returns how many bits needed to represent given value (max 8 bit)*/
+static unsigned getValueRequiredBits(unsigned char value) {
+ if(value == 0 || value == 255) return 1;
+ /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/
+ if(value % 17 == 0) return value % 85 == 0 ? 2 : 4;
+ return 8;
+}
+
+/*stats must already have been inited. */
+unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
+ const unsigned char* in, unsigned w, unsigned h,
+ const LodePNGColorMode* mode_in) {
+ size_t i;
+ ColorTree tree;
+ size_t numpixels = (size_t)w * (size_t)h;
+ unsigned error = 0;
+
+ /* mark things as done already if it would be impossible to have a more expensive case */
+ unsigned colored_done = lodepng_is_greyscale_type(mode_in) ? 1 : 0;
+ unsigned alpha_done = lodepng_can_have_alpha(mode_in) ? 0 : 1;
+ unsigned numcolors_done = 0;
+ unsigned bpp = lodepng_get_bpp(mode_in);
+ unsigned bits_done = (stats->bits == 1 && bpp == 1) ? 1 : 0;
+ unsigned sixteen = 0; /* whether the input image is 16 bit */
+ unsigned maxnumcolors = 257;
+ if(bpp <= 8) maxnumcolors = LODEPNG_MIN(257, stats->numcolors + (1u << bpp));
+
+ stats->numpixels += numpixels;
+
+ /*if palette not allowed, no need to compute numcolors*/
+ if(!stats->allow_palette) numcolors_done = 1;
+
+ color_tree_init(&tree);
+
+ /*If the stats was already filled in from previous data, fill its palette in tree
+ and mark things as done already if we know they are the most expensive case already*/
+ if(stats->alpha) alpha_done = 1;
+ if(stats->colored) colored_done = 1;
+ if(stats->bits == 16) numcolors_done = 1;
+ if(stats->bits >= bpp) bits_done = 1;
+ if(stats->numcolors >= maxnumcolors) numcolors_done = 1;
+
+ if(!numcolors_done) {
+ for(i = 0; i < stats->numcolors; i++) {
+ const unsigned char* color = &stats->palette[i * 4];
+ error = color_tree_add(&tree, color[0], color[1], color[2], color[3], i);
+ if(error) goto cleanup;
+ }
+ }
+
+ /*Check if the 16-bit input is truly 16-bit*/
+ if(mode_in->bitdepth == 16 && !sixteen) {
+ unsigned short r = 0, g = 0, b = 0, a = 0;
+ for(i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
+ if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) ||
+ (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ {
+ stats->bits = 16;
+ sixteen = 1;
+ bits_done = 1;
+ numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/
+ break;
+ }
+ }
+ }
+
+ if(sixteen) {
+ unsigned short r = 0, g = 0, b = 0, a = 0;
+
+ for(i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
+
+ if(!colored_done && (r != g || r != b)) {
+ stats->colored = 1;
+ colored_done = 1;
+ }
+
+ if(!alpha_done) {
+ unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b);
+ if(a != 65535 && (a != 0 || (stats->key && !matchkey))) {
+ stats->alpha = 1;
+ stats->key = 0;
+ alpha_done = 1;
+ } else if(a == 0 && !stats->alpha && !stats->key) {
+ stats->key = 1;
+ stats->key_r = r;
+ stats->key_g = g;
+ stats->key_b = b;
+ } else if(a == 65535 && stats->key && matchkey) {
+ /* Color key cannot be used if an opaque pixel also has that RGB color. */
+ stats->alpha = 1;
+ stats->key = 0;
+ alpha_done = 1;
+ }
+ }
+ if(alpha_done && numcolors_done && colored_done && bits_done) break;
+ }
+
+ if(stats->key && !stats->alpha) {
+ for(i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
+ if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) {
+ /* Color key cannot be used if an opaque pixel also has that RGB color. */
+ stats->alpha = 1;
+ stats->key = 0;
+ alpha_done = 1;
+ }
+ }
+ }
+ } else /* < 16-bit */ {
+ unsigned char r = 0, g = 0, b = 0, a = 0;
+ for(i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
+
+ if(!bits_done && stats->bits < 8) {
+ /*only r is checked, < 8 bits is only relevant for grayscale*/
+ unsigned bits = getValueRequiredBits(r);
+ if(bits > stats->bits) stats->bits = bits;
+ }
+ bits_done = (stats->bits >= bpp);
+
+ if(!colored_done && (r != g || r != b)) {
+ stats->colored = 1;
+ colored_done = 1;
+ if(stats->bits < 8) stats->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/
+ }
+
+ if(!alpha_done) {
+ unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b);
+ if(a != 255 && (a != 0 || (stats->key && !matchkey))) {
+ stats->alpha = 1;
+ stats->key = 0;
+ alpha_done = 1;
+ if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
+ } else if(a == 0 && !stats->alpha && !stats->key) {
+ stats->key = 1;
+ stats->key_r = r;
+ stats->key_g = g;
+ stats->key_b = b;
+ } else if(a == 255 && stats->key && matchkey) {
+ /* Color key cannot be used if an opaque pixel also has that RGB color. */
+ stats->alpha = 1;
+ stats->key = 0;
+ alpha_done = 1;
+ if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
+ }
+ }
+
+ if(!numcolors_done) {
+ if(!color_tree_has(&tree, r, g, b, a)) {
+ error = color_tree_add(&tree, r, g, b, a, stats->numcolors);
+ if(error) goto cleanup;
+ if(stats->numcolors < 256) {
+ unsigned char* p = stats->palette;
+ unsigned n = stats->numcolors;
+ p[n * 4 + 0] = r;
+ p[n * 4 + 1] = g;
+ p[n * 4 + 2] = b;
+ p[n * 4 + 3] = a;
+ }
+ ++stats->numcolors;
+ numcolors_done = stats->numcolors >= maxnumcolors;
+ }
+ }
+
+ if(alpha_done && numcolors_done && colored_done && bits_done) break;
+ }
+
+ if(stats->key && !stats->alpha) {
+ for(i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
+ if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) {
+ /* Color key cannot be used if an opaque pixel also has that RGB color. */
+ stats->alpha = 1;
+ stats->key = 0;
+ alpha_done = 1;
+ if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
+ }
+ }
+ }
+
+ /*make the stats's key always 16-bit for consistency - repeat each byte twice*/
+ stats->key_r += (stats->key_r << 8);
+ stats->key_g += (stats->key_g << 8);
+ stats->key_b += (stats->key_b << 8);
+ }
+
+cleanup:
+ color_tree_cleanup(&tree);
+ return error;
+}
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+/*Adds a single color to the color stats. The stats must already have been inited. The color must be given as 16-bit
+(with 2 bytes repeating for 8-bit and 65535 for opaque alpha channel). This function is expensive, do not call it for
+all pixels of an image but only for a few additional values. */
+static unsigned lodepng_color_stats_add(LodePNGColorStats* stats,
+ unsigned r, unsigned g, unsigned b, unsigned a) {
+ unsigned error = 0;
+ unsigned char image[8];
+ LodePNGColorMode mode;
+ lodepng_color_mode_init(&mode);
+ image[0] = r >> 8; image[1] = r; image[2] = g >> 8; image[3] = g;
+ image[4] = b >> 8; image[5] = b; image[6] = a >> 8; image[7] = a;
+ mode.bitdepth = 16;
+ mode.colortype = LCT_RGBA;
+ error = lodepng_compute_color_stats(stats, image, 1, 1, &mode);
+ lodepng_color_mode_cleanup(&mode);
+ return error;
+}
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+/*Computes a minimal PNG color model that can contain all colors as indicated by the stats.
+The stats should be computed with lodepng_compute_color_stats.
+mode_in is raw color profile of the image the stats were computed on, to copy palette order from when relevant.
+Minimal PNG color model means the color type and bit depth that gives smallest amount of bits in the output image,
+e.g. gray if only grayscale pixels, palette if less than 256 colors, color key if only single transparent color, ...
+This is used if auto_convert is enabled (it is by default).
+*/
+static unsigned auto_choose_color(LodePNGColorMode* mode_out,
+ const LodePNGColorMode* mode_in,
+ const LodePNGColorStats* stats) {
+ unsigned error = 0;
+ unsigned palettebits;
+ size_t i, n;
+ size_t numpixels = stats->numpixels;
+ unsigned palette_ok, gray_ok;
+
+ unsigned alpha = stats->alpha;
+ unsigned key = stats->key;
+ unsigned bits = stats->bits;
+
+ mode_out->key_defined = 0;
+
+ if(key && numpixels <= 16) {
+ alpha = 1; /*too few pixels to justify tRNS chunk overhead*/
+ key = 0;
+ if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
+ }
+
+ gray_ok = !stats->colored;
+ if(!stats->allow_greyscale) gray_ok = 0;
+ if(!gray_ok && bits < 8) bits = 8;
+
+ n = stats->numcolors;
+ palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8));
+ palette_ok = n <= 256 && bits <= 8 && n != 0; /*n==0 means likely numcolors wasn't computed*/
+ if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/
+ if(gray_ok && !alpha && bits <= palettebits) palette_ok = 0; /*gray is less overhead*/
+ if(!stats->allow_palette) palette_ok = 0;
+
+ if(palette_ok) {
+ const unsigned char* p = stats->palette;
+ lodepng_palette_clear(mode_out); /*remove potential earlier palette*/
+ for(i = 0; i != stats->numcolors; ++i) {
+ error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]);
+ if(error) break;
+ }
+
+ mode_out->colortype = LCT_PALETTE;
+ mode_out->bitdepth = palettebits;
+
+ if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize
+ && mode_in->bitdepth == mode_out->bitdepth) {
+ /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/
+ lodepng_color_mode_cleanup(mode_out);
+ lodepng_color_mode_copy(mode_out, mode_in);
+ }
+ } else /*8-bit or 16-bit per channel*/ {
+ mode_out->bitdepth = bits;
+ mode_out->colortype = alpha ? (gray_ok ? LCT_GREY_ALPHA : LCT_RGBA)
+ : (gray_ok ? LCT_GREY : LCT_RGB);
+ if(key) {
+ unsigned mask = (1u << mode_out->bitdepth) - 1u; /*stats always uses 16-bit, mask converts it*/
+ mode_out->key_r = stats->key_r & mask;
+ mode_out->key_g = stats->key_g & mask;
+ mode_out->key_b = stats->key_b & mask;
+ mode_out->key_defined = 1;
+ }
+ }
+
+ return error;
+}
+
+#endif /* #ifdef LODEPNG_COMPILE_ENCODER */
+
+/*
+Paeth predictor, used by PNG filter type 4
+The parameters are of type short, but should come from unsigned chars, the shorts
+are only needed to make the paeth calculation correct.
+*/
+static unsigned char paethPredictor(short a, short b, short c) {
+ short pa = LODEPNG_ABS(b - c);
+ short pb = LODEPNG_ABS(a - c);
+ short pc = LODEPNG_ABS(a + b - c - c);
+ /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */
+ if(pb < pa) { a = b; pa = pb; }
+ return (pc < pa) ? c : a;
+}
+
+/*shared values used by multiple Adam7 related functions*/
+
+static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
+static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
+static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
+static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
+
+/*
+Outputs various dimensions and positions in the image related to the Adam7 reduced images.
+passw: output containing the width of the 7 passes
+passh: output containing the height of the 7 passes
+filter_passstart: output containing the index of the start and end of each
+ reduced image with filter bytes
+padded_passstart output containing the index of the start and end of each
+ reduced image when without filter bytes but with padded scanlines
+passstart: output containing the index of the start and end of each reduced
+ image without padding between scanlines, but still padding between the images
+w, h: width and height of non-interlaced image
+bpp: bits per pixel
+"padded" is only relevant if bpp is less than 8 and a scanline or image does not
+ end at a full byte
+*/
+static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8],
+ size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) {
+ /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/
+ unsigned i;
+
+ /*calculate width and height in pixels of each pass*/
+ for(i = 0; i != 7; ++i) {
+ passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
+ passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
+ if(passw[i] == 0) passh[i] = 0;
+ if(passh[i] == 0) passw[i] = 0;
+ }
+
+ filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
+ for(i = 0; i != 7; ++i) {
+ /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/
+ filter_passstart[i + 1] = filter_passstart[i]
+ + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0);
+ /*bits padded if needed to fill full byte at end of each scanline*/
+ padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u);
+ /*only padded at end of reduced image*/
+ passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u;
+ }
+}
+
+#ifdef LODEPNG_COMPILE_DECODER
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / PNG Decoder / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*read the information from the header and store it in the LodePNGInfo. return value is error*/
+unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state,
+ const unsigned char* in, size_t insize) {
+ unsigned width, height;
+ LodePNGInfo* info = &state->info_png;
+ if(insize == 0 || in == 0) {
+ CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/
+ }
+ if(insize < 33) {
+ CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/
+ }
+
+ /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
+ /* TODO: remove this. One should use a new LodePNGState for new sessions */
+ lodepng_info_cleanup(info);
+ lodepng_info_init(info);
+
+ if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71
+ || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) {
+ CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/
+ }
+ if(lodepng_chunk_length(in + 8) != 13) {
+ CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/
+ }
+ if(!lodepng_chunk_type_equals(in + 8, "IHDR")) {
+ CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/
+ }
+
+ /*read the values given in the header*/
+ width = lodepng_read32bitInt(&in[16]);
+ height = lodepng_read32bitInt(&in[20]);
+ /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/
+ if(w) *w = width;
+ if(h) *h = height;
+ info->color.bitdepth = in[24];
+ info->color.colortype = (LodePNGColorType)in[25];
+ info->compression_method = in[26];
+ info->filter_method = in[27];
+ info->interlace_method = in[28];
+
+ /*errors returned only after the parsing so other values are still output*/
+
+ /*error: invalid image size*/
+ if(width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93);
+ /*error: invalid colortype or bitdepth combination*/
+ state->error = checkColorValidity(info->color.colortype, info->color.bitdepth);
+ if(state->error) return state->error;
+ /*error: only compression method 0 is allowed in the specification*/
+ if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32);
+ /*error: only filter method 0 is allowed in the specification*/
+ if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33);
+ /*error: only interlace methods 0 and 1 exist in the specification*/
+ if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34);
+
+ if(!state->decoder.ignore_crc) {
+ unsigned CRC = lodepng_read32bitInt(&in[29]);
+ unsigned checksum = lodepng_crc32(&in[12], 17);
+ if(CRC != checksum) {
+ CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/
+ }
+ }
+
+ return state->error;
+}
+
+static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon,
+ size_t bytewidth, unsigned char filterType, size_t length) {
+ /*
+ For PNG filter method 0
+ unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte,
+ the filter works byte per byte (bytewidth = 1)
+ precon is the previous unfiltered scanline, recon the result, scanline the current one
+ the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
+ recon and scanline MAY be the same memory address! precon must be disjoint.
+ */
+
+ size_t i;
+ switch(filterType) {
+ case 0:
+ for(i = 0; i != length; ++i) recon[i] = scanline[i];
+ break;
+ case 1:
+ for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
+ for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth];
+ break;
+ case 2:
+ if(precon) {
+ for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
+ } else {
+ for(i = 0; i != length; ++i) recon[i] = scanline[i];
+ }
+ break;
+ case 3:
+ if(precon) {
+ for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
+ for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u);
+ } else {
+ for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
+ for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u);
+ }
+ break;
+ case 4:
+ if(precon) {
+ for(i = 0; i != bytewidth; ++i) {
+ recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
+ }
+
+ /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that
+ adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */
+ if(bytewidth >= 4) {
+ for(; i + 3 < length; i += 4) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
+ recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
+ }
+ } else if(bytewidth >= 3) {
+ for(; i + 2 < length; i += 3) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
+ }
+ } else if(bytewidth >= 2) {
+ for(; i + 1 < length; i += 2) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ }
+ }
+
+ for(; i != length; ++i) {
+ recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
+ }
+ } else {
+ for(i = 0; i != bytewidth; ++i) {
+ recon[i] = scanline[i];
+ }
+ for(i = bytewidth; i < length; ++i) {
+ /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/
+ recon[i] = (scanline[i] + recon[i - bytewidth]);
+ }
+ }
+ break;
+ default: return 36; /*error: invalid filter type given*/
+ }
+ return 0;
+}
+
+static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
+ /*
+ For PNG filter method 0
+ this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times)
+ out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
+ w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
+ in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes)
+ */
+
+ unsigned y;
+ unsigned char* prevline = 0;
+
+ /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
+ size_t bytewidth = (bpp + 7u) / 8u;
+ /*the width of a scanline in bytes, not including the filter type*/
+ size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u;
+
+ for(y = 0; y < h; ++y) {
+ size_t outindex = linebytes * y;
+ size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
+ unsigned char filterType = in[inindex];
+
+ CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes));
+
+ prevline = &out[outindex];
+ }
+
+ return 0;
+}
+
+/*
+in: Adam7 interlaced image, with no padding bits between scanlines, but between
+ reduced images so that each reduced image starts at a byte.
+out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h
+bpp: bits per pixel
+out has the following size in bits: w * h * bpp.
+in is possibly bigger due to padding bits between reduced images.
+out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation
+(because that's likely a little bit faster)
+NOTE: comments about padding bits are only relevant if bpp < 8
+*/
+static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
+ unsigned passw[7], passh[7];
+ size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned i;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ if(bpp >= 8) {
+ for(i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ size_t bytewidth = bpp / 8u;
+ for(y = 0; y < passh[i]; ++y)
+ for(x = 0; x < passw[i]; ++x) {
+ size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
+ size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w
+ + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth;
+ for(b = 0; b < bytewidth; ++b) {
+ out[pixeloutstart + b] = in[pixelinstart + b];
+ }
+ }
+ }
+ } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ {
+ for(i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ unsigned ilinebits = bpp * passw[i];
+ unsigned olinebits = bpp * w;
+ size_t obp, ibp; /*bit pointers (for out and in buffer)*/
+ for(y = 0; y < passh[i]; ++y)
+ for(x = 0; x < passw[i]; ++x) {
+ ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
+ obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp;
+ for(b = 0; b < bpp; ++b) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ }
+ }
+ }
+}
+
+static void removePaddingBits(unsigned char* out, const unsigned char* in,
+ size_t olinebits, size_t ilinebits, unsigned h) {
+ /*
+ After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need
+ to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers
+ for the Adam7 code, the color convert code and the output to the user.
+ in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must
+ have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
+ also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
+ only useful if (ilinebits - olinebits) is a value in the range 1..7
+ */
+ unsigned y;
+ size_t diff = ilinebits - olinebits;
+ size_t ibp = 0, obp = 0; /*input and output bit pointers*/
+ for(y = 0; y < h; ++y) {
+ size_t x;
+ for(x = 0; x < olinebits; ++x) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ ibp += diff;
+ }
+}
+
+/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from
+the IDAT chunks (with filter index bytes and possible padding bits)
+return value is error*/
+static unsigned postProcessScanlines(unsigned char* out, unsigned char* in,
+ unsigned w, unsigned h, const LodePNGInfo* info_png) {
+ /*
+ This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype.
+ Steps:
+ *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8)
+ *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
+ NOTE: the in buffer will be overwritten with intermediate data!
+ */
+ unsigned bpp = lodepng_get_bpp(&info_png->color);
+ if(bpp == 0) return 31; /*error: invalid colortype*/
+
+ if(info_png->interlace_method == 0) {
+ if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) {
+ CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp));
+ removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h);
+ }
+ /*we can immediately filter into the out buffer, no other steps needed*/
+ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp));
+ } else /*interlace_method is 1 (Adam7)*/ {
+ unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned i;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ for(i = 0; i != 7; ++i) {
+ CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp));
+ /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline,
+ move bytes instead of bits or move not at all*/
+ if(bpp < 8) {
+ /*remove padding bits in scanlines; after this there still may be padding
+ bits between the different reduced images: each reduced image still starts nicely at a byte*/
+ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp,
+ ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]);
+ }
+ }
+
+ Adam7_deinterlace(out, in, w, h, bpp);
+ }
+
+ return 0;
+}
+
+static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) {
+ unsigned pos = 0, i;
+ color->palettesize = chunkLength / 3u;
+ if(color->palettesize == 0 || color->palettesize > 256) return 38; /*error: palette too small or big*/
+ lodepng_color_mode_alloc_palette(color);
+ if(!color->palette && color->palettesize) {
+ color->palettesize = 0;
+ return 83; /*alloc fail*/
+ }
+
+ for(i = 0; i != color->palettesize; ++i) {
+ color->palette[4 * i + 0] = data[pos++]; /*R*/
+ color->palette[4 * i + 1] = data[pos++]; /*G*/
+ color->palette[4 * i + 2] = data[pos++]; /*B*/
+ color->palette[4 * i + 3] = 255; /*alpha*/
+ }
+
+ return 0; /* OK */
+}
+
+static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) {
+ unsigned i;
+ if(color->colortype == LCT_PALETTE) {
+ /*error: more alpha values given than there are palette entries*/
+ if(chunkLength > color->palettesize) return 39;
+
+ for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i];
+ } else if(color->colortype == LCT_GREY) {
+ /*error: this chunk must be 2 bytes for grayscale image*/
+ if(chunkLength != 2) return 30;
+
+ color->key_defined = 1;
+ color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1];
+ } else if(color->colortype == LCT_RGB) {
+ /*error: this chunk must be 6 bytes for RGB image*/
+ if(chunkLength != 6) return 41;
+
+ color->key_defined = 1;
+ color->key_r = 256u * data[0] + data[1];
+ color->key_g = 256u * data[2] + data[3];
+ color->key_b = 256u * data[4] + data[5];
+ }
+ else return 42; /*error: tRNS chunk not allowed for other color models*/
+
+ return 0; /* OK */
+}
+
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+/*background color chunk (bKGD)*/
+static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
+ if(info->color.colortype == LCT_PALETTE) {
+ /*error: this chunk must be 1 byte for indexed color image*/
+ if(chunkLength != 1) return 43;
+
+ /*error: invalid palette index, or maybe this chunk appeared before PLTE*/
+ if(data[0] >= info->color.palettesize) return 103;
+
+ info->background_defined = 1;
+ info->background_r = info->background_g = info->background_b = data[0];
+ } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) {
+ /*error: this chunk must be 2 bytes for grayscale image*/
+ if(chunkLength != 2) return 44;
+
+ /*the values are truncated to bitdepth in the PNG file*/
+ info->background_defined = 1;
+ info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1];
+ } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) {
+ /*error: this chunk must be 6 bytes for grayscale image*/
+ if(chunkLength != 6) return 45;
+
+ /*the values are truncated to bitdepth in the PNG file*/
+ info->background_defined = 1;
+ info->background_r = 256u * data[0] + data[1];
+ info->background_g = 256u * data[2] + data[3];
+ info->background_b = 256u * data[4] + data[5];
+ }
+
+ return 0; /* OK */
+}
+
+/*text chunk (tEXt)*/
+static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
+ unsigned error = 0;
+ char *key = 0, *str = 0;
+
+ while(!error) /*not really a while loop, only used to break on error*/ {
+ unsigned length, string2_begin;
+
+ length = 0;
+ while(length < chunkLength && data[length] != 0) ++length;
+ /*even though it's not allowed by the standard, no error is thrown if
+ there's no null termination char, if the text is empty*/
+ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
+
+ key = (char*)lodepng_malloc(length + 1);
+ if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
+
+ lodepng_memcpy(key, data, length);
+ key[length] = 0;
+
+ string2_begin = length + 1; /*skip keyword null terminator*/
+
+ length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin);
+ str = (char*)lodepng_malloc(length + 1);
+ if(!str) CERROR_BREAK(error, 83); /*alloc fail*/
+
+ lodepng_memcpy(str, data + string2_begin, length);
+ str[length] = 0;
+
+ error = lodepng_add_text(info, key, str);
+
+ break;
+ }
+
+ lodepng_free(key);
+ lodepng_free(str);
+
+ return error;
+}
+
+/*compressed text chunk (zTXt)*/
+static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
+ const unsigned char* data, size_t chunkLength) {
+ unsigned error = 0;
+
+ unsigned length, string2_begin;
+ char *key = 0;
+ unsigned char* str = 0;
+ size_t size = 0;
+
+ while(!error) /*not really a while loop, only used to break on error*/ {
+ for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
+ if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
+ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
+
+ key = (char*)lodepng_malloc(length + 1);
+ if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
+
+ lodepng_memcpy(key, data, length);
+ key[length] = 0;
+
+ if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/
+
+ string2_begin = length + 2;
+ if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
+
+ length = (unsigned)chunkLength - string2_begin;
+ /*will fail if zlib error, e.g. if length is too small*/
+ error = zlib_decompress(&str, &size, 0, &data[string2_begin],
+ length, zlibsettings);
+ if(error) break;
+ error = lodepng_add_text_sized(info, key, (char*)str, size);
+
+ break;
+ }
+
+ lodepng_free(key);
+ lodepng_free(str);
+
+ return error;
+}
+
+/*international text chunk (iTXt)*/
+static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
+ const unsigned char* data, size_t chunkLength) {
+ unsigned error = 0;
+ unsigned i;
+
+ unsigned length, begin, compressed;
+ char *key = 0, *langtag = 0, *transkey = 0;
+
+ while(!error) /*not really a while loop, only used to break on error*/ {
+ /*Quick check if the chunk length isn't too small. Even without check
+ it'd still fail with other error checks below if it's too short. This just gives a different error code.*/
+ if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/
+
+ /*read the key*/
+ for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
+ if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/
+ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
+
+ key = (char*)lodepng_malloc(length + 1);
+ if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
+
+ lodepng_memcpy(key, data, length);
+ key[length] = 0;
+
+ /*read the compression method*/
+ compressed = data[length + 1];
+ if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/
+
+ /*even though it's not allowed by the standard, no error is thrown if
+ there's no null termination char, if the text is empty for the next 3 texts*/
+
+ /*read the langtag*/
+ begin = length + 3;
+ length = 0;
+ for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length;
+
+ langtag = (char*)lodepng_malloc(length + 1);
+ if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/
+
+ lodepng_memcpy(langtag, data + begin, length);
+ langtag[length] = 0;
+
+ /*read the transkey*/
+ begin += length + 1;
+ length = 0;
+ for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length;
+
+ transkey = (char*)lodepng_malloc(length + 1);
+ if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/
+
+ lodepng_memcpy(transkey, data + begin, length);
+ transkey[length] = 0;
+
+ /*read the actual text*/
+ begin += length + 1;
+
+ length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin;
+
+ if(compressed) {
+ unsigned char* str = 0;
+ size_t size = 0;
+ /*will fail if zlib error, e.g. if length is too small*/
+ error = zlib_decompress(&str, &size, 0, &data[begin],
+ length, zlibsettings);
+ if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size);
+ lodepng_free(str);
+ } else {
+ error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)(data + begin), length);
+ }
+
+ break;
+ }
+
+ lodepng_free(key);
+ lodepng_free(langtag);
+ lodepng_free(transkey);
+
+ return error;
+}
+
+static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
+ if(chunkLength != 7) return 73; /*invalid tIME chunk size*/
+
+ info->time_defined = 1;
+ info->time.year = 256u * data[0] + data[1];
+ info->time.month = data[2];
+ info->time.day = data[3];
+ info->time.hour = data[4];
+ info->time.minute = data[5];
+ info->time.second = data[6];
+
+ return 0; /* OK */
+}
+
+static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
+ if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/
+
+ info->phys_defined = 1;
+ info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3];
+ info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7];
+ info->phys_unit = data[8];
+
+ return 0; /* OK */
+}
+
+static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
+ if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/
+
+ info->gama_defined = 1;
+ info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3];
+
+ return 0; /* OK */
+}
+
+static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
+ if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/
+
+ info->chrm_defined = 1;
+ info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3];
+ info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7];
+ info->chrm_red_x = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11];
+ info->chrm_red_y = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15];
+ info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19];
+ info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23];
+ info->chrm_blue_x = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27];
+ info->chrm_blue_y = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31];
+
+ return 0; /* OK */
+}
+
+static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
+ if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/
+
+ info->srgb_defined = 1;
+ info->srgb_intent = data[0];
+
+ return 0; /* OK */
+}
+
+static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings,
+ const unsigned char* data, size_t chunkLength) {
+ unsigned error = 0;
+ unsigned i;
+ size_t size = 0;
+
+ unsigned length, string2_begin;
+
+ info->iccp_defined = 1;
+ if(info->iccp_name) lodepng_clear_icc(info);
+
+ for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
+ if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/
+ if(length < 1 || length > 79) return 89; /*keyword too short or long*/
+
+ info->iccp_name = (char*)lodepng_malloc(length + 1);
+ if(!info->iccp_name) return 83; /*alloc fail*/
+
+ info->iccp_name[length] = 0;
+ for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i];
+
+ if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/
+
+ string2_begin = length + 2;
+ if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/
+
+ length = (unsigned)chunkLength - string2_begin;
+ error = zlib_decompress(&info->iccp_profile, &size, 0,
+ &data[string2_begin],
+ length, zlibsettings);
+ info->iccp_profile_size = size;
+ if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/
+ return error;
+}
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
+ const unsigned char* in, size_t insize) {
+ const unsigned char* chunk = in + pos;
+ unsigned chunkLength;
+ const unsigned char* data;
+ unsigned unhandled = 0;
+ unsigned error = 0;
+
+ if(pos + 4 > insize) return 30;
+ chunkLength = lodepng_chunk_length(chunk);
+ if(chunkLength > 2147483647) return 63;
+ data = lodepng_chunk_data_const(chunk);
+ if(data + chunkLength + 4 > in + insize) return 30;
+
+ if(lodepng_chunk_type_equals(chunk, "PLTE")) {
+ error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "tRNS")) {
+ error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ } else if(lodepng_chunk_type_equals(chunk, "bKGD")) {
+ error = readChunk_bKGD(&state->info_png, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
+ error = readChunk_tEXt(&state->info_png, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
+ error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
+ error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
+ error = readChunk_tIME(&state->info_png, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
+ error = readChunk_pHYs(&state->info_png, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "gAMA")) {
+ error = readChunk_gAMA(&state->info_png, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "cHRM")) {
+ error = readChunk_cHRM(&state->info_png, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
+ error = readChunk_sRGB(&state->info_png, data, chunkLength);
+ } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
+ error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ } else {
+ /* unhandled chunk is ok (is not an error) */
+ unhandled = 1;
+ }
+
+ if(!error && !unhandled && !state->decoder.ignore_crc) {
+ if(lodepng_chunk_check_crc(chunk)) return 57; /*invalid CRC*/
+ }
+
+ return error;
+}
+
+/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
+static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
+ LodePNGState* state,
+ const unsigned char* in, size_t insize) {
+ unsigned char IEND = 0;
+ const unsigned char* chunk;
+ unsigned char* idat; /*the data from idat chunks, zlib compressed*/
+ size_t idatsize = 0;
+ unsigned char* scanlines = 0;
+ size_t scanlines_size = 0, expected_size = 0;
+ size_t outsize = 0;
+
+ /*for unknown chunk order*/
+ unsigned unknown = 0;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+
+ /* safe output values in case error happens */
+ *out = 0;
+ *w = *h = 0;
+
+ state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/
+ if(state->error) return;
+
+ if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) {
+ CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/
+ }
+
+ /*the input filesize is a safe upper bound for the sum of idat chunks size*/
+ idat = (unsigned char*)lodepng_malloc(insize);
+ if(!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/
+
+ chunk = &in[33]; /*first byte of the first chunk after the header*/
+
+ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
+ IDAT data is put at the start of the in buffer*/
+ while(!IEND && !state->error) {
+ unsigned chunkLength;
+ const unsigned char* data; /*the data in the chunk*/
+
+ /*error: size of the in buffer too small to contain next chunk*/
+ if((size_t)((chunk - in) + 12) > insize || chunk < in) {
+ if(state->decoder.ignore_end) break; /*other errors may still happen though*/
+ CERROR_BREAK(state->error, 30);
+ }
+
+ /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
+ chunkLength = lodepng_chunk_length(chunk);
+ /*error: chunk length larger than the max PNG chunk size*/
+ if(chunkLength > 2147483647) {
+ if(state->decoder.ignore_end) break; /*other errors may still happen though*/
+ CERROR_BREAK(state->error, 63);
+ }
+
+ if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) {
+ CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/
+ }
+
+ data = lodepng_chunk_data_const(chunk);
+
+ unknown = 0;
+
+ /*IDAT chunk, containing compressed image data*/
+ if(lodepng_chunk_type_equals(chunk, "IDAT")) {
+ size_t newsize;
+ if(lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95);
+ if(newsize > insize) CERROR_BREAK(state->error, 95);
+ lodepng_memcpy(idat + idatsize, data, chunkLength);
+ idatsize += chunkLength;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ critical_pos = 3;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ } else if(lodepng_chunk_type_equals(chunk, "IEND")) {
+ /*IEND chunk*/
+ IEND = 1;
+ } else if(lodepng_chunk_type_equals(chunk, "PLTE")) {
+ /*palette chunk (PLTE)*/
+ state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
+ if(state->error) break;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ critical_pos = 2;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ } else if(lodepng_chunk_type_equals(chunk, "tRNS")) {
+ /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled
+ in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that
+ affects the alpha channel of pixels. */
+ state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
+ if(state->error) break;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ /*background color chunk (bKGD)*/
+ } else if(lodepng_chunk_type_equals(chunk, "bKGD")) {
+ state->error = readChunk_bKGD(&state->info_png, data, chunkLength);
+ if(state->error) break;
+ } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
+ /*text chunk (tEXt)*/
+ if(state->decoder.read_text_chunks) {
+ state->error = readChunk_tEXt(&state->info_png, data, chunkLength);
+ if(state->error) break;
+ }
+ } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
+ /*compressed text chunk (zTXt)*/
+ if(state->decoder.read_text_chunks) {
+ state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
+ if(state->error) break;
+ }
+ } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
+ /*international text chunk (iTXt)*/
+ if(state->decoder.read_text_chunks) {
+ state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
+ if(state->error) break;
+ }
+ } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
+ state->error = readChunk_tIME(&state->info_png, data, chunkLength);
+ if(state->error) break;
+ } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
+ state->error = readChunk_pHYs(&state->info_png, data, chunkLength);
+ if(state->error) break;
+ } else if(lodepng_chunk_type_equals(chunk, "gAMA")) {
+ state->error = readChunk_gAMA(&state->info_png, data, chunkLength);
+ if(state->error) break;
+ } else if(lodepng_chunk_type_equals(chunk, "cHRM")) {
+ state->error = readChunk_cHRM(&state->info_png, data, chunkLength);
+ if(state->error) break;
+ } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
+ state->error = readChunk_sRGB(&state->info_png, data, chunkLength);
+ if(state->error) break;
+ } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
+ state->error = readChunk_iCCP(&state->info_png, &state->decoder.zlibsettings, data, chunkLength);
+ if(state->error) break;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
+ /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
+ if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) {
+ CERROR_BREAK(state->error, 69);
+ }
+
+ unknown = 1;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ if(state->decoder.remember_unknown_chunks) {
+ state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1],
+ &state->info_png.unknown_chunks_size[critical_pos - 1], chunk);
+ if(state->error) break;
+ }
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ }
+
+ if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ {
+ if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
+ }
+
+ if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
+ }
+
+ if(state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
+ state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
+ }
+
+ if(!state->error) {
+ /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation.
+ If the decompressed size does not match the prediction, the image must be corrupt.*/
+ if(state->info_png.interlace_method == 0) {
+ size_t bpp = lodepng_get_bpp(&state->info_png.color);
+ expected_size = lodepng_get_raw_size_idat(*w, *h, bpp);
+ } else {
+ size_t bpp = lodepng_get_bpp(&state->info_png.color);
+ /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/
+ expected_size = 0;
+ expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp);
+ if(*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp);
+ if(*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp);
+ if(*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp);
+ }
+
+ state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings);
+ }
+ if(!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/
+ lodepng_free(idat);
+
+ if(!state->error) {
+ outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color);
+ *out = (unsigned char*)lodepng_malloc(outsize);
+ if(!*out) state->error = 83; /*alloc fail*/
+ }
+ if(!state->error) {
+ lodepng_memset(*out, 0, outsize);
+ state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png);
+ }
+ lodepng_free(scanlines);
+}
+
+unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h,
+ LodePNGState* state,
+ const unsigned char* in, size_t insize) {
+ *out = 0;
+ decodeGeneric(out, w, h, state, in, insize);
+ if(state->error) return state->error;
+ if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) {
+ /*same color type, no copying or converting of data needed*/
+ /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype
+ the raw image has to the end user*/
+ if(!state->decoder.color_convert) {
+ state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color);
+ if(state->error) return state->error;
+ }
+ } else { /*color conversion needed*/
+ unsigned char* data = *out;
+ size_t outsize;
+
+ /*TODO: check if this works according to the statement in the documentation: "The converter can convert
+ from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/
+ if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA)
+ && !(state->info_raw.bitdepth == 8)) {
+ return 56; /*unsupported color mode conversion*/
+ }
+
+ outsize = lodepng_get_raw_size(*w, *h, &state->info_raw);
+ *out = (unsigned char*)lodepng_malloc(outsize);
+ if(!(*out)) {
+ state->error = 83; /*alloc fail*/
+ }
+ else state->error = lodepng_convert(*out, data, &state->info_raw,
+ &state->info_png.color, *w, *h);
+ lodepng_free(data);
+ }
+ return state->error;
+}
+
+unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in,
+ size_t insize, LodePNGColorType colortype, unsigned bitdepth) {
+ unsigned error;
+ LodePNGState state;
+ lodepng_state_init(&state);
+ state.info_raw.colortype = colortype;
+ state.info_raw.bitdepth = bitdepth;
+ error = lodepng_decode(out, w, h, &state, in, insize);
+ lodepng_state_cleanup(&state);
+ return error;
+}
+
+unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) {
+ return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8);
+}
+
+unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) {
+ return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8);
+}
+
+#ifdef LODEPNG_COMPILE_DISK
+unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename,
+ LodePNGColorType colortype, unsigned bitdepth) {
+ unsigned char* buffer = 0;
+ size_t buffersize;
+ unsigned error;
+ /* safe output values in case error happens */
+ *out = 0;
+ *w = *h = 0;
+ error = lodepng_load_file(&buffer, &buffersize, filename);
+ if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth);
+ lodepng_free(buffer);
+ return error;
+}
+
+unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) {
+ return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8);
+}
+
+unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) {
+ return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8);
+}
+#endif /*LODEPNG_COMPILE_DISK*/
+
+void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) {
+ settings->color_convert = 1;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ settings->read_text_chunks = 1;
+ settings->remember_unknown_chunks = 0;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ settings->ignore_crc = 0;
+ settings->ignore_critical = 0;
+ settings->ignore_end = 0;
+ lodepng_decompress_settings_init(&settings->zlibsettings);
+}
+
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
+
+void lodepng_state_init(LodePNGState* state) {
+#ifdef LODEPNG_COMPILE_DECODER
+ lodepng_decoder_settings_init(&state->decoder);
+#endif /*LODEPNG_COMPILE_DECODER*/
+#ifdef LODEPNG_COMPILE_ENCODER
+ lodepng_encoder_settings_init(&state->encoder);
+#endif /*LODEPNG_COMPILE_ENCODER*/
+ lodepng_color_mode_init(&state->info_raw);
+ lodepng_info_init(&state->info_png);
+ state->error = 1;
+}
+
+void lodepng_state_cleanup(LodePNGState* state) {
+ lodepng_color_mode_cleanup(&state->info_raw);
+ lodepng_info_cleanup(&state->info_png);
+}
+
+void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) {
+ lodepng_state_cleanup(dest);
+ *dest = *source;
+ lodepng_color_mode_init(&dest->info_raw);
+ lodepng_info_init(&dest->info_png);
+ dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return;
+ dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return;
+}
+
+#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */
+
+#ifdef LODEPNG_COMPILE_ENCODER
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / PNG Encoder / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+
+static unsigned writeSignature(ucvector* out) {
+ size_t pos = out->size;
+ const unsigned char signature[] = {137, 80, 78, 71, 13, 10, 26, 10};
+ /*8 bytes PNG signature, aka the magic bytes*/
+ if(!ucvector_resize(out, out->size + 8)) return 83; /*alloc fail*/
+ lodepng_memcpy(out->data + pos, signature, 8);
+ return 0;
+}
+
+static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) {
+ unsigned char *chunk, *data;
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 13, "IHDR"));
+ data = chunk + 8;
+
+ lodepng_set32bitInt(data + 0, w); /*width*/
+ lodepng_set32bitInt(data + 4, h); /*height*/
+ data[8] = (unsigned char)bitdepth; /*bit depth*/
+ data[9] = (unsigned char)colortype; /*color type*/
+ data[10] = 0; /*compression method*/
+ data[11] = 0; /*filter method*/
+ data[12] = interlace_method; /*interlace method*/
+
+ lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+/* only adds the chunk if needed (there is a key or palette with alpha) */
+static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) {
+ unsigned char* chunk;
+ size_t i, j = 8;
+
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, info->palettesize * 3, "PLTE"));
+
+ for(i = 0; i != info->palettesize; ++i) {
+ /*add all channels except alpha channel*/
+ chunk[j++] = info->palette[i * 4 + 0];
+ chunk[j++] = info->palette[i * 4 + 1];
+ chunk[j++] = info->palette[i * 4 + 2];
+ }
+
+ lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) {
+ unsigned char* chunk = 0;
+
+ if(info->colortype == LCT_PALETTE) {
+ size_t i, amount = info->palettesize;
+ /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/
+ for(i = info->palettesize; i != 0; --i) {
+ if(info->palette[4 * (i - 1) + 3] != 255) break;
+ --amount;
+ }
+ if(amount) {
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, amount, "tRNS"));
+ /*add the alpha channel values from the palette*/
+ for(i = 0; i != amount; ++i) chunk[8 + i] = info->palette[4 * i + 3];
+ }
+ } else if(info->colortype == LCT_GREY) {
+ if(info->key_defined) {
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "tRNS"));
+ chunk[8] = (unsigned char)(info->key_r >> 8);
+ chunk[9] = (unsigned char)(info->key_r & 255);
+ }
+ } else if(info->colortype == LCT_RGB) {
+ if(info->key_defined) {
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "tRNS"));
+ chunk[8] = (unsigned char)(info->key_r >> 8);
+ chunk[9] = (unsigned char)(info->key_r & 255);
+ chunk[10] = (unsigned char)(info->key_g >> 8);
+ chunk[11] = (unsigned char)(info->key_g & 255);
+ chunk[12] = (unsigned char)(info->key_b >> 8);
+ chunk[13] = (unsigned char)(info->key_b & 255);
+ }
+ }
+
+ if(chunk) lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize,
+ LodePNGCompressSettings* zlibsettings) {
+ unsigned error = 0;
+ unsigned char* zlib = 0;
+ size_t zlibsize = 0;
+
+ error = zlib_compress(&zlib, &zlibsize, data, datasize, zlibsettings);
+ if(!error) {
+ error = lodepng_chunk_createv(out, zlibsize, "IDAT", zlib);
+ }
+ lodepng_free(zlib);
+ return error;
+}
+
+static unsigned addChunk_IEND(ucvector* out) {
+ return lodepng_chunk_createv(out, 0, "IEND", 0);
+}
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+
+static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) {
+ unsigned char* chunk = 0;
+ size_t keysize = lodepng_strlen(keyword), textsize = lodepng_strlen(textstring);
+ size_t size = keysize + 1 + textsize;
+ if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, size, "tEXt"));
+ lodepng_memcpy(chunk + 8, keyword, keysize);
+ chunk[8 + keysize] = 0; /*null termination char*/
+ lodepng_memcpy(chunk + 9 + keysize, textstring, textsize);
+ lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring,
+ LodePNGCompressSettings* zlibsettings) {
+ unsigned error = 0;
+ unsigned char* chunk = 0;
+ unsigned char* compressed = 0;
+ size_t compressedsize = 0;
+ size_t textsize = lodepng_strlen(textstring);
+ size_t keysize = lodepng_strlen(keyword);
+ if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
+
+ error = zlib_compress(&compressed, &compressedsize,
+ (const unsigned char*)textstring, textsize, zlibsettings);
+ if(!error) {
+ size_t size = keysize + 2 + compressedsize;
+ error = lodepng_chunk_init(&chunk, out, size, "zTXt");
+ }
+ if(!error) {
+ lodepng_memcpy(chunk + 8, keyword, keysize);
+ chunk[8 + keysize] = 0; /*null termination char*/
+ chunk[9 + keysize] = 0; /*compression method: 0*/
+ lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize);
+ lodepng_chunk_generate_crc(chunk);
+ }
+
+ lodepng_free(compressed);
+ return error;
+}
+
+static unsigned addChunk_iTXt(ucvector* out, unsigned compress, const char* keyword, const char* langtag,
+ const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) {
+ unsigned error = 0;
+ unsigned char* chunk = 0;
+ unsigned char* compressed = 0;
+ size_t compressedsize = 0;
+ size_t textsize = lodepng_strlen(textstring);
+ size_t keysize = lodepng_strlen(keyword), langsize = lodepng_strlen(langtag), transsize = lodepng_strlen(transkey);
+
+ if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
+
+ if(compress) {
+ error = zlib_compress(&compressed, &compressedsize,
+ (const unsigned char*)textstring, textsize, zlibsettings);
+ }
+ if(!error) {
+ size_t size = keysize + 3 + langsize + 1 + transsize + 1 + (compress ? compressedsize : textsize);
+ error = lodepng_chunk_init(&chunk, out, size, "iTXt");
+ }
+ if(!error) {
+ size_t pos = 8;
+ lodepng_memcpy(chunk + pos, keyword, keysize);
+ pos += keysize;
+ chunk[pos++] = 0; /*null termination char*/
+ chunk[pos++] = (compress ? 1 : 0); /*compression flag*/
+ chunk[pos++] = 0; /*compression method: 0*/
+ lodepng_memcpy(chunk + pos, langtag, langsize);
+ pos += langsize;
+ chunk[pos++] = 0; /*null termination char*/
+ lodepng_memcpy(chunk + pos, transkey, transsize);
+ pos += transsize;
+ chunk[pos++] = 0; /*null termination char*/
+ if(compress) {
+ lodepng_memcpy(chunk + pos, compressed, compressedsize);
+ } else {
+ lodepng_memcpy(chunk + pos, textstring, textsize);
+ }
+ lodepng_chunk_generate_crc(chunk);
+ }
+
+ lodepng_free(compressed);
+ return error;
+}
+
+static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) {
+ unsigned char* chunk = 0;
+ if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) {
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "bKGD"));
+ chunk[8] = (unsigned char)(info->background_r >> 8);
+ chunk[9] = (unsigned char)(info->background_r & 255);
+ } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) {
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "bKGD"));
+ chunk[8] = (unsigned char)(info->background_r >> 8);
+ chunk[9] = (unsigned char)(info->background_r & 255);
+ chunk[10] = (unsigned char)(info->background_g >> 8);
+ chunk[11] = (unsigned char)(info->background_g & 255);
+ chunk[12] = (unsigned char)(info->background_b >> 8);
+ chunk[13] = (unsigned char)(info->background_b & 255);
+ } else if(info->color.colortype == LCT_PALETTE) {
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 1, "bKGD"));
+ chunk[8] = (unsigned char)(info->background_r & 255); /*palette index*/
+ }
+ if(chunk) lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) {
+ unsigned char* chunk;
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 7, "tIME"));
+ chunk[8] = (unsigned char)(time->year >> 8);
+ chunk[9] = (unsigned char)(time->year & 255);
+ chunk[10] = (unsigned char)time->month;
+ chunk[11] = (unsigned char)time->day;
+ chunk[12] = (unsigned char)time->hour;
+ chunk[13] = (unsigned char)time->minute;
+ chunk[14] = (unsigned char)time->second;
+ lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) {
+ unsigned char* chunk;
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 9, "pHYs"));
+ lodepng_set32bitInt(chunk + 8, info->phys_x);
+ lodepng_set32bitInt(chunk + 12, info->phys_y);
+ chunk[16] = info->phys_unit;
+ lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) {
+ unsigned char* chunk;
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 4, "gAMA"));
+ lodepng_set32bitInt(chunk + 8, info->gama_gamma);
+ lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) {
+ unsigned char* chunk;
+ CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 32, "cHRM"));
+ lodepng_set32bitInt(chunk + 8, info->chrm_white_x);
+ lodepng_set32bitInt(chunk + 12, info->chrm_white_y);
+ lodepng_set32bitInt(chunk + 16, info->chrm_red_x);
+ lodepng_set32bitInt(chunk + 20, info->chrm_red_y);
+ lodepng_set32bitInt(chunk + 24, info->chrm_green_x);
+ lodepng_set32bitInt(chunk + 28, info->chrm_green_y);
+ lodepng_set32bitInt(chunk + 32, info->chrm_blue_x);
+ lodepng_set32bitInt(chunk + 36, info->chrm_blue_y);
+ lodepng_chunk_generate_crc(chunk);
+ return 0;
+}
+
+static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) {
+ unsigned char data = info->srgb_intent;
+ return lodepng_chunk_createv(out, 1, "sRGB", &data);
+}
+
+static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) {
+ unsigned error = 0;
+ unsigned char* chunk = 0;
+ unsigned char* compressed = 0;
+ size_t compressedsize = 0;
+ size_t keysize = lodepng_strlen(info->iccp_name);
+
+ if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
+ error = zlib_compress(&compressed, &compressedsize,
+ info->iccp_profile, info->iccp_profile_size, zlibsettings);
+ if(!error) {
+ size_t size = keysize + 2 + compressedsize;
+ error = lodepng_chunk_init(&chunk, out, size, "iCCP");
+ }
+ if(!error) {
+ lodepng_memcpy(chunk + 8, info->iccp_name, keysize);
+ chunk[8 + keysize] = 0; /*null termination char*/
+ chunk[9 + keysize] = 0; /*compression method: 0*/
+ lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize);
+ lodepng_chunk_generate_crc(chunk);
+ }
+
+ lodepng_free(compressed);
+ return error;
+}
+
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline,
+ size_t length, size_t bytewidth, unsigned char filterType) {
+ size_t i;
+ switch(filterType) {
+ case 0: /*None*/
+ for(i = 0; i != length; ++i) out[i] = scanline[i];
+ break;
+ case 1: /*Sub*/
+ for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
+ for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth];
+ break;
+ case 2: /*Up*/
+ if(prevline) {
+ for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i];
+ } else {
+ for(i = 0; i != length; ++i) out[i] = scanline[i];
+ }
+ break;
+ case 3: /*Average*/
+ if(prevline) {
+ for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1);
+ for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1);
+ } else {
+ for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
+ for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1);
+ }
+ break;
+ case 4: /*Paeth*/
+ if(prevline) {
+ /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/
+ for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]);
+ for(i = bytewidth; i < length; ++i) {
+ out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth]));
+ }
+ } else {
+ for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
+ /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/
+ for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]);
+ }
+ break;
+ default: return; /*invalid filter type given*/
+ }
+}
+
+/* integer binary logarithm, max return value is 31 */
+static size_t ilog2(size_t i) {
+ size_t result = 0;
+ if(i >= 65536) { result += 16; i >>= 16; }
+ if(i >= 256) { result += 8; i >>= 8; }
+ if(i >= 16) { result += 4; i >>= 4; }
+ if(i >= 4) { result += 2; i >>= 2; }
+ if(i >= 2) { result += 1; /*i >>= 1;*/ }
+ return result;
+}
+
+/* integer approximation for i * log2(i), helper function for LFS_ENTROPY */
+static size_t ilog2i(size_t i) {
+ size_t l;
+ if(i == 0) return 0;
+ l = ilog2(i);
+ /* approximate i*log2(i): l is integer logarithm, ((i - (1u << l)) << 1u)
+ linearly approximates the missing fractional part multiplied by i */
+ return i * l + ((i - (1u << l)) << 1u);
+}
+
+static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h,
+ const LodePNGColorMode* color, const LodePNGEncoderSettings* settings) {
+ /*
+ For PNG filter method 0
+ out must be a buffer with as size: h + (w * h * bpp + 7u) / 8u, because there are
+ the scanlines with 1 extra byte per scanline
+ */
+
+ unsigned bpp = lodepng_get_bpp(color);
+ /*the width of a scanline in bytes, not including the filter type*/
+ size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u;
+
+ /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
+ size_t bytewidth = (bpp + 7u) / 8u;
+ const unsigned char* prevline = 0;
+ unsigned x, y;
+ unsigned error = 0;
+ LodePNGFilterStrategy strategy = settings->filter_strategy;
+
+ /*
+ There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard:
+ * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e.
+ use fixed filtering, with the filter None).
+ * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is
+ not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply
+ all five filters and select the filter that produces the smallest sum of absolute values per row.
+ This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true.
+
+ If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed,
+ but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum
+ heuristic is used.
+ */
+ if(settings->filter_palette_zero &&
+ (color->colortype == LCT_PALETTE || color->bitdepth < 8)) strategy = LFS_ZERO;
+
+ if(bpp == 0) return 31; /*error: invalid color type*/
+
+ if(strategy >= LFS_ZERO && strategy <= LFS_FOUR) {
+ unsigned char type = (unsigned char)strategy;
+ for(y = 0; y != h; ++y) {
+ size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
+ size_t inindex = linebytes * y;
+ out[outindex] = type; /*filter type byte*/
+ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type);
+ prevline = &in[inindex];
+ }
+ } else if(strategy == LFS_MINSUM) {
+ /*adaptive filtering*/
+ unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
+ size_t smallest = 0;
+ unsigned char type, bestType = 0;
+
+ for(type = 0; type != 5; ++type) {
+ attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
+ if(!attempt[type]) error = 83; /*alloc fail*/
+ }
+
+ if(!error) {
+ for(y = 0; y != h; ++y) {
+ /*try the 5 filter types*/
+ for(type = 0; type != 5; ++type) {
+ size_t sum = 0;
+ filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
+
+ /*calculate the sum of the result*/
+ if(type == 0) {
+ for(x = 0; x != linebytes; ++x) sum += (unsigned char)(attempt[type][x]);
+ } else {
+ for(x = 0; x != linebytes; ++x) {
+ /*For differences, each byte should be treated as signed, values above 127 are negative
+ (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there.
+ This means filtertype 0 is almost never chosen, but that is justified.*/
+ unsigned char s = attempt[type][x];
+ sum += s < 128 ? s : (255U - s);
+ }
+ }
+
+ /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
+ if(type == 0 || sum < smallest) {
+ bestType = type;
+ smallest = sum;
+ }
+ }
+
+ prevline = &in[y * linebytes];
+
+ /*now fill the out values*/
+ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
+ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
+ }
+ }
+
+ for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
+ } else if(strategy == LFS_ENTROPY) {
+ unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
+ size_t bestSum = 0;
+ unsigned type, bestType = 0;
+ unsigned count[256];
+
+ for(type = 0; type != 5; ++type) {
+ attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
+ if(!attempt[type]) error = 83; /*alloc fail*/
+ }
+
+ if(!error) {
+ for(y = 0; y != h; ++y) {
+ /*try the 5 filter types*/
+ for(type = 0; type != 5; ++type) {
+ size_t sum = 0;
+ filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
+ lodepng_memset(count, 0, 256 * sizeof(*count));
+ for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]];
+ ++count[type]; /*the filter type itself is part of the scanline*/
+ for(x = 0; x != 256; ++x) {
+ sum += ilog2i(count[x]);
+ }
+ /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
+ if(type == 0 || sum > bestSum) {
+ bestType = type;
+ bestSum = sum;
+ }
+ }
+
+ prevline = &in[y * linebytes];
+
+ /*now fill the out values*/
+ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
+ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
+ }
+ }
+
+ for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
+ } else if(strategy == LFS_PREDEFINED) {
+ for(y = 0; y != h; ++y) {
+ size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
+ size_t inindex = linebytes * y;
+ unsigned char type = settings->predefined_filters[y];
+ out[outindex] = type; /*filter type byte*/
+ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type);
+ prevline = &in[inindex];
+ }
+ } else if(strategy == LFS_BRUTE_FORCE) {
+ /*brute force filter chooser.
+ deflate the scanline after every filter attempt to see which one deflates best.
+ This is very slow and gives only slightly smaller, sometimes even larger, result*/
+ size_t size[5];
+ unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
+ size_t smallest = 0;
+ unsigned type = 0, bestType = 0;
+ unsigned char* dummy;
+ LodePNGCompressSettings zlibsettings;
+ lodepng_memcpy(&zlibsettings, &settings->zlibsettings, sizeof(LodePNGCompressSettings));
+ /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose,
+ to simulate the true case where the tree is the same for the whole image. Sometimes it gives
+ better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare
+ cases better compression. It does make this a bit less slow, so it's worth doing this.*/
+ zlibsettings.btype = 1;
+ /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG
+ images only, so disable it*/
+ zlibsettings.custom_zlib = 0;
+ zlibsettings.custom_deflate = 0;
+ for(type = 0; type != 5; ++type) {
+ attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
+ if(!attempt[type]) error = 83; /*alloc fail*/
+ }
+ if(!error) {
+ for(y = 0; y != h; ++y) /*try the 5 filter types*/ {
+ for(type = 0; type != 5; ++type) {
+ unsigned testsize = (unsigned)linebytes;
+ /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/
+
+ filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
+ size[type] = 0;
+ dummy = 0;
+ zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings);
+ lodepng_free(dummy);
+ /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/
+ if(type == 0 || size[type] < smallest) {
+ bestType = type;
+ smallest = size[type];
+ }
+ }
+ prevline = &in[y * linebytes];
+ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
+ for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
+ }
+ }
+ for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
+ }
+ else return 88; /* unknown filter strategy */
+
+ return error;
+}
+
+static void addPaddingBits(unsigned char* out, const unsigned char* in,
+ size_t olinebits, size_t ilinebits, unsigned h) {
+ /*The opposite of the removePaddingBits function
+ olinebits must be >= ilinebits*/
+ unsigned y;
+ size_t diff = olinebits - ilinebits;
+ size_t obp = 0, ibp = 0; /*bit pointers*/
+ for(y = 0; y != h; ++y) {
+ size_t x;
+ for(x = 0; x < ilinebits; ++x) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ /*obp += diff; --> no, fill in some value in the padding bits too, to avoid
+ "Use of uninitialised value of size ###" warning from valgrind*/
+ for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0);
+ }
+}
+
+/*
+in: non-interlaced image with size w*h
+out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with
+ no padding bits between scanlines, but between reduced images so that each
+ reduced image starts at a byte.
+bpp: bits per pixel
+there are no padding bits, not between scanlines, not between reduced images
+in has the following size in bits: w * h * bpp.
+out is possibly bigger due to padding bits between reduced images
+NOTE: comments about padding bits are only relevant if bpp < 8
+*/
+static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
+ unsigned passw[7], passh[7];
+ size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned i;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ if(bpp >= 8) {
+ for(i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ size_t bytewidth = bpp / 8u;
+ for(y = 0; y < passh[i]; ++y)
+ for(x = 0; x < passw[i]; ++x) {
+ size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
+ size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth;
+ for(b = 0; b < bytewidth; ++b) {
+ out[pixeloutstart + b] = in[pixelinstart + b];
+ }
+ }
+ }
+ } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ {
+ for(i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ unsigned ilinebits = bpp * passw[i];
+ unsigned olinebits = bpp * w;
+ size_t obp, ibp; /*bit pointers (for out and in buffer)*/
+ for(y = 0; y < passh[i]; ++y)
+ for(x = 0; x < passw[i]; ++x) {
+ ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
+ obp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
+ for(b = 0; b < bpp; ++b) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ }
+ }
+ }
+}
+
+/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image.
+return value is error**/
+static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in,
+ unsigned w, unsigned h,
+ const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) {
+ /*
+ This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps:
+ *) if no Adam7: 1) add padding bits (= possible extra bits per scanline if bpp < 8) 2) filter
+ *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter
+ */
+ unsigned bpp = lodepng_get_bpp(&info_png->color);
+ unsigned error = 0;
+
+ if(info_png->interlace_method == 0) {
+ *outsize = h + (h * ((w * bpp + 7u) / 8u)); /*image size plus an extra byte per scanline + possible padding bits*/
+ *out = (unsigned char*)lodepng_malloc(*outsize);
+ if(!(*out) && (*outsize)) error = 83; /*alloc fail*/
+
+ if(!error) {
+ /*non multiple of 8 bits per scanline, padding bits needed per scanline*/
+ if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) {
+ unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7u) / 8u));
+ if(!padded) error = 83; /*alloc fail*/
+ if(!error) {
+ addPaddingBits(padded, in, ((w * bpp + 7u) / 8u) * 8u, w * bpp, h);
+ error = filter(*out, padded, w, h, &info_png->color, settings);
+ }
+ lodepng_free(padded);
+ } else {
+ /*we can immediately filter into the out buffer, no other steps needed*/
+ error = filter(*out, in, w, h, &info_png->color, settings);
+ }
+ }
+ } else /*interlace_method is 1 (Adam7)*/ {
+ unsigned passw[7], passh[7];
+ size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned char* adam7;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/
+ *out = (unsigned char*)lodepng_malloc(*outsize);
+ if(!(*out)) error = 83; /*alloc fail*/
+
+ adam7 = (unsigned char*)lodepng_malloc(passstart[7]);
+ if(!adam7 && passstart[7]) error = 83; /*alloc fail*/
+
+ if(!error) {
+ unsigned i;
+
+ Adam7_interlace(adam7, in, w, h, bpp);
+ for(i = 0; i != 7; ++i) {
+ if(bpp < 8) {
+ unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]);
+ if(!padded) ERROR_BREAK(83); /*alloc fail*/
+ addPaddingBits(padded, &adam7[passstart[i]],
+ ((passw[i] * bpp + 7u) / 8u) * 8u, passw[i] * bpp, passh[i]);
+ error = filter(&(*out)[filter_passstart[i]], padded,
+ passw[i], passh[i], &info_png->color, settings);
+ lodepng_free(padded);
+ } else {
+ error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]],
+ passw[i], passh[i], &info_png->color, settings);
+ }
+
+ if(error) break;
+ }
+ }
+
+ lodepng_free(adam7);
+ }
+
+ return error;
+}
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) {
+ unsigned char* inchunk = data;
+ while((size_t)(inchunk - data) < datasize) {
+ CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk));
+ out->allocsize = out->size; /*fix the allocsize again*/
+ inchunk = lodepng_chunk_next(inchunk, data + datasize);
+ }
+ return 0;
+}
+
+static unsigned isGrayICCProfile(const unsigned char* profile, unsigned size) {
+ /*
+ It is a gray profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19
+ are "RGB ". We do not perform any full parsing of the ICC profile here, other
+ than check those 4 bytes to grayscale profile. Other than that, validity of
+ the profile is not checked. This is needed only because the PNG specification
+ requires using a non-gray color model if there is an ICC profile with "RGB "
+ (sadly limiting compression opportunities if the input data is grayscale RGB
+ data), and requires using a gray color model if it is "GRAY".
+ */
+ if(size < 20) return 0;
+ return profile[16] == 'G' && profile[17] == 'R' && profile[18] == 'A' && profile[19] == 'Y';
+}
+
+static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) {
+ /* See comment in isGrayICCProfile*/
+ if(size < 20) return 0;
+ return profile[16] == 'R' && profile[17] == 'G' && profile[18] == 'B' && profile[19] == ' ';
+}
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+unsigned lodepng_encode(unsigned char** out, size_t* outsize,
+ const unsigned char* image, unsigned w, unsigned h,
+ LodePNGState* state) {
+ unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/
+ size_t datasize = 0;
+ ucvector outv = ucvector_init(NULL, 0);
+ LodePNGInfo info;
+ const LodePNGInfo* info_png = &state->info_png;
+
+ lodepng_info_init(&info);
+
+ /*provide some proper output values if error will happen*/
+ *out = 0;
+ *outsize = 0;
+ state->error = 0;
+
+ /*check input values validity*/
+ if((info_png->color.colortype == LCT_PALETTE || state->encoder.force_palette)
+ && (info_png->color.palettesize == 0 || info_png->color.palettesize > 256)) {
+ state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/
+ goto cleanup;
+ }
+ if(state->encoder.zlibsettings.btype > 2) {
+ state->error = 61; /*error: invalid btype*/
+ goto cleanup;
+ }
+ if(info_png->interlace_method > 1) {
+ state->error = 71; /*error: invalid interlace mode*/
+ goto cleanup;
+ }
+ state->error = checkColorValidity(info_png->color.colortype, info_png->color.bitdepth);
+ if(state->error) goto cleanup; /*error: invalid color type given*/
+ state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth);
+ if(state->error) goto cleanup; /*error: invalid color type given*/
+
+ /* color convert and compute scanline filter types */
+ lodepng_info_copy(&info, &state->info_png);
+ if(state->encoder.auto_convert) {
+ LodePNGColorStats stats;
+ lodepng_color_stats_init(&stats);
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ if(info_png->iccp_defined &&
+ isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) {
+ /*the PNG specification does not allow to use palette with a GRAY ICC profile, even
+ if the palette has only gray colors, so disallow it.*/
+ stats.allow_palette = 0;
+ }
+ if(info_png->iccp_defined &&
+ isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) {
+ /*the PNG specification does not allow to use grayscale color with RGB ICC profile, so disallow gray.*/
+ stats.allow_greyscale = 0;
+ }
+#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */
+ state->error = lodepng_compute_color_stats(&stats, image, w, h, &state->info_raw);
+ if(state->error) goto cleanup;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ if(info_png->background_defined) {
+ /*the background chunk's color must be taken into account as well*/
+ unsigned r = 0, g = 0, b = 0;
+ LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGB, 16);
+ lodepng_convert_rgb(&r, &g, &b, info_png->background_r, info_png->background_g, info_png->background_b, &mode16, &info_png->color);
+ state->error = lodepng_color_stats_add(&stats, r, g, b, 65535);
+ if(state->error) goto cleanup;
+ }
+#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */
+ state->error = auto_choose_color(&info.color, &state->info_raw, &stats);
+ if(state->error) goto cleanup;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ /*also convert the background chunk*/
+ if(info_png->background_defined) {
+ if(lodepng_convert_rgb(&info.background_r, &info.background_g, &info.background_b,
+ info_png->background_r, info_png->background_g, info_png->background_b, &info.color, &info_png->color)) {
+ state->error = 104;
+ goto cleanup;
+ }
+ }
+#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */
+ }
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ if(info_png->iccp_defined) {
+ unsigned gray_icc = isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size);
+ unsigned rgb_icc = isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size);
+ unsigned gray_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA;
+ if(!gray_icc && !rgb_icc) {
+ state->error = 100; /* Disallowed profile color type for PNG */
+ goto cleanup;
+ }
+ if(gray_icc != gray_png) {
+ /*Not allowed to use RGB/RGBA/palette with GRAY ICC profile or vice versa,
+ or in case of auto_convert, it wasn't possible to find appropriate model*/
+ state->error = state->encoder.auto_convert ? 102 : 101;
+ goto cleanup;
+ }
+ }
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) {
+ unsigned char* converted;
+ size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7u) / 8u;
+
+ converted = (unsigned char*)lodepng_malloc(size);
+ if(!converted && size) state->error = 83; /*alloc fail*/
+ if(!state->error) {
+ state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h);
+ }
+ if(!state->error) {
+ state->error = preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder);
+ }
+ lodepng_free(converted);
+ if(state->error) goto cleanup;
+ } else {
+ state->error = preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder);
+ if(state->error) goto cleanup;
+ }
+
+ /* output all PNG chunks */ {
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ size_t i;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ /*write signature and chunks*/
+ state->error = writeSignature(&outv);
+ if(state->error) goto cleanup;
+ /*IHDR*/
+ state->error = addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method);
+ if(state->error) goto cleanup;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ /*unknown chunks between IHDR and PLTE*/
+ if(info.unknown_chunks_data[0]) {
+ state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]);
+ if(state->error) goto cleanup;
+ }
+ /*color profile chunks must come before PLTE */
+ if(info.iccp_defined) {
+ state->error = addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings);
+ if(state->error) goto cleanup;
+ }
+ if(info.srgb_defined) {
+ state->error = addChunk_sRGB(&outv, &info);
+ if(state->error) goto cleanup;
+ }
+ if(info.gama_defined) {
+ state->error = addChunk_gAMA(&outv, &info);
+ if(state->error) goto cleanup;
+ }
+ if(info.chrm_defined) {
+ state->error = addChunk_cHRM(&outv, &info);
+ if(state->error) goto cleanup;
+ }
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ /*PLTE*/
+ if(info.color.colortype == LCT_PALETTE) {
+ state->error = addChunk_PLTE(&outv, &info.color);
+ if(state->error) goto cleanup;
+ }
+ if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) {
+ /*force_palette means: write suggested palette for truecolor in PLTE chunk*/
+ state->error = addChunk_PLTE(&outv, &info.color);
+ if(state->error) goto cleanup;
+ }
+ /*tRNS (this will only add if when necessary) */
+ state->error = addChunk_tRNS(&outv, &info.color);
+ if(state->error) goto cleanup;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ /*bKGD (must come between PLTE and the IDAt chunks*/
+ if(info.background_defined) {
+ state->error = addChunk_bKGD(&outv, &info);
+ if(state->error) goto cleanup;
+ }
+ /*pHYs (must come before the IDAT chunks)*/
+ if(info.phys_defined) {
+ state->error = addChunk_pHYs(&outv, &info);
+ if(state->error) goto cleanup;
+ }
+
+ /*unknown chunks between PLTE and IDAT*/
+ if(info.unknown_chunks_data[1]) {
+ state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]);
+ if(state->error) goto cleanup;
+ }
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ /*IDAT (multiple IDAT chunks must be consecutive)*/
+ state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings);
+ if(state->error) goto cleanup;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ /*tIME*/
+ if(info.time_defined) {
+ state->error = addChunk_tIME(&outv, &info.time);
+ if(state->error) goto cleanup;
+ }
+ /*tEXt and/or zTXt*/
+ for(i = 0; i != info.text_num; ++i) {
+ if(lodepng_strlen(info.text_keys[i]) > 79) {
+ state->error = 66; /*text chunk too large*/
+ goto cleanup;
+ }
+ if(lodepng_strlen(info.text_keys[i]) < 1) {
+ state->error = 67; /*text chunk too small*/
+ goto cleanup;
+ }
+ if(state->encoder.text_compression) {
+ state->error = addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings);
+ if(state->error) goto cleanup;
+ } else {
+ state->error = addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]);
+ if(state->error) goto cleanup;
+ }
+ }
+ /*LodePNG version id in text chunk*/
+ if(state->encoder.add_id) {
+ unsigned already_added_id_text = 0;
+ for(i = 0; i != info.text_num; ++i) {
+ const char* k = info.text_keys[i];
+ /* Could use strcmp, but we're not calling or reimplementing this C library function for this use only */
+ if(k[0] == 'L' && k[1] == 'o' && k[2] == 'd' && k[3] == 'e' &&
+ k[4] == 'P' && k[5] == 'N' && k[6] == 'G' && k[7] == '\0') {
+ already_added_id_text = 1;
+ break;
+ }
+ }
+ if(already_added_id_text == 0) {
+ state->error = addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/
+ if(state->error) goto cleanup;
+ }
+ }
+ /*iTXt*/
+ for(i = 0; i != info.itext_num; ++i) {
+ if(lodepng_strlen(info.itext_keys[i]) > 79) {
+ state->error = 66; /*text chunk too large*/
+ goto cleanup;
+ }
+ if(lodepng_strlen(info.itext_keys[i]) < 1) {
+ state->error = 67; /*text chunk too small*/
+ goto cleanup;
+ }
+ state->error = addChunk_iTXt(
+ &outv, state->encoder.text_compression,
+ info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i],
+ &state->encoder.zlibsettings);
+ if(state->error) goto cleanup;
+ }
+
+ /*unknown chunks between IDAT and IEND*/
+ if(info.unknown_chunks_data[2]) {
+ state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]);
+ if(state->error) goto cleanup;
+ }
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+ state->error = addChunk_IEND(&outv);
+ if(state->error) goto cleanup;
+ }
+
+cleanup:
+ lodepng_info_cleanup(&info);
+ lodepng_free(data);
+
+ /*instead of cleaning the vector up, give it to the output*/
+ *out = outv.data;
+ *outsize = outv.size;
+
+ return state->error;
+}
+
+unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image,
+ unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) {
+ unsigned error;
+ LodePNGState state;
+ lodepng_state_init(&state);
+ state.info_raw.colortype = colortype;
+ state.info_raw.bitdepth = bitdepth;
+ state.info_png.color.colortype = colortype;
+ state.info_png.color.bitdepth = bitdepth;
+ lodepng_encode(out, outsize, image, w, h, &state);
+ error = state.error;
+ lodepng_state_cleanup(&state);
+ return error;
+}
+
+unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) {
+ return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8);
+}
+
+unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) {
+ return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8);
+}
+
+#ifdef LODEPNG_COMPILE_DISK
+unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth) {
+ unsigned char* buffer;
+ size_t buffersize;
+ unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth);
+ if(!error) error = lodepng_save_file(buffer, buffersize, filename);
+ lodepng_free(buffer);
+ return error;
+}
+
+unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) {
+ return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8);
+}
+
+unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) {
+ return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8);
+}
+#endif /*LODEPNG_COMPILE_DISK*/
+
+void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) {
+ lodepng_compress_settings_init(&settings->zlibsettings);
+ settings->filter_palette_zero = 1;
+ settings->filter_strategy = LFS_MINSUM;
+ settings->auto_convert = 1;
+ settings->force_palette = 0;
+ settings->predefined_filters = 0;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ settings->add_id = 0;
+ settings->text_compression = 1;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+}
+
+#endif /*LODEPNG_COMPILE_ENCODER*/
+#endif /*LODEPNG_COMPILE_PNG*/
+
+#ifdef LODEPNG_COMPILE_ERROR_TEXT
+/*
+This returns the description of a numerical error code in English. This is also
+the documentation of all the error codes.
+*/
+const char* lodepng_error_text(unsigned code) {
+ switch(code) {
+ case 0: return "no error, everything went ok";
+ case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/
+ case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/
+ case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/
+ case 13: return "problem while processing dynamic deflate block";
+ case 14: return "problem while processing dynamic deflate block";
+ case 15: return "problem while processing dynamic deflate block";
+ /*this error could happen if there are only 0 or 1 symbols present in the huffman code:*/
+ case 16: return "invalid code while processing dynamic deflate block";
+ case 17: return "end of out buffer memory reached while inflating";
+ case 18: return "invalid distance code while inflating";
+ case 19: return "end of out buffer memory reached while inflating";
+ case 20: return "invalid deflate block BTYPE encountered while decoding";
+ case 21: return "NLEN is not ones complement of LEN in a deflate block";
+
+ /*end of out buffer memory reached while inflating:
+ This can happen if the inflated deflate data is longer than the amount of bytes required to fill up
+ all the pixels of the image, given the color depth and image dimensions. Something that doesn't
+ happen in a normal, well encoded, PNG image.*/
+ case 22: return "end of out buffer memory reached while inflating";
+ case 23: return "end of in buffer memory reached while inflating";
+ case 24: return "invalid FCHECK in zlib header";
+ case 25: return "invalid compression method in zlib header";
+ case 26: return "FDICT encountered in zlib header while it's not used for PNG";
+ case 27: return "PNG file is smaller than a PNG header";
+ /*Checks the magic file header, the first 8 bytes of the PNG file*/
+ case 28: return "incorrect PNG signature, it's no PNG or corrupted";
+ case 29: return "first chunk is not the header chunk";
+ case 30: return "chunk length too large, chunk broken off at end of file";
+ case 31: return "illegal PNG color type or bpp";
+ case 32: return "illegal PNG compression method";
+ case 33: return "illegal PNG filter method";
+ case 34: return "illegal PNG interlace method";
+ case 35: return "chunk length of a chunk is too large or the chunk too small";
+ case 36: return "illegal PNG filter type encountered";
+ case 37: return "illegal bit depth for this color type given";
+ case 38: return "the palette is too small or too big"; /*0, or more than 256 colors*/
+ case 39: return "tRNS chunk before PLTE or has more entries than palette size";
+ case 40: return "tRNS chunk has wrong size for grayscale image";
+ case 41: return "tRNS chunk has wrong size for RGB image";
+ case 42: return "tRNS chunk appeared while it was not allowed for this color type";
+ case 43: return "bKGD chunk has wrong size for palette image";
+ case 44: return "bKGD chunk has wrong size for grayscale image";
+ case 45: return "bKGD chunk has wrong size for RGB image";
+ case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?";
+ case 49: return "jumped past memory while generating dynamic huffman tree";
+ case 50: return "jumped past memory while generating dynamic huffman tree";
+ case 51: return "jumped past memory while inflating huffman block";
+ case 52: return "jumped past memory while inflating";
+ case 53: return "size of zlib data too small";
+ case 54: return "repeat symbol in tree while there was no value symbol yet";
+ /*jumped past tree while generating huffman tree, this could be when the
+ tree will have more leaves than symbols after generating it out of the
+ given lengths. They call this an oversubscribed dynamic bit lengths tree in zlib.*/
+ case 55: return "jumped past tree while generating huffman tree";
+ case 56: return "given output image colortype or bitdepth not supported for color conversion";
+ case 57: return "invalid CRC encountered (checking CRC can be disabled)";
+ case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)";
+ case 59: return "requested color conversion not supported";
+ case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)";
+ case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)";
+ /*LodePNG leaves the choice of RGB to grayscale conversion formula to the user.*/
+ case 62: return "conversion from color to grayscale not supported";
+ /*(2^31-1)*/
+ case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk";
+ /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/
+ case 64: return "the length of the END symbol 256 in the Huffman tree is 0";
+ case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes";
+ case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte";
+ case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors";
+ case 69: return "unknown chunk type with 'critical' flag encountered by the decoder";
+ case 71: return "invalid interlace mode given to encoder (must be 0 or 1)";
+ case 72: return "while decoding, invalid compression method encountering in zTXt or iTXt chunk (it must be 0)";
+ case 73: return "invalid tIME chunk size";
+ case 74: return "invalid pHYs chunk size";
+ /*length could be wrong, or data chopped off*/
+ case 75: return "no null termination char found while decoding text chunk";
+ case 76: return "iTXt chunk too short to contain required bytes";
+ case 77: return "integer overflow in buffer size";
+ case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/
+ case 79: return "failed to open file for writing";
+ case 80: return "tried creating a tree of 0 symbols";
+ case 81: return "lazy matching at pos 0 is impossible";
+ case 82: return "color conversion to palette requested while a color isn't in palette, or index out of bounds";
+ case 83: return "memory allocation failed";
+ case 84: return "given image too small to contain all pixels to be encoded";
+ case 86: return "impossible offset in lz77 encoding (internal bug)";
+ case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined";
+ case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy";
+ case 89: return "text chunk keyword too short or long: must have size 1-79";
+ /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/
+ case 90: return "windowsize must be a power of two";
+ case 91: return "invalid decompressed idat size";
+ case 92: return "integer overflow due to too many pixels";
+ case 93: return "zero width or height is invalid";
+ case 94: return "header chunk must have a size of 13 bytes";
+ case 95: return "integer overflow with combined idat chunk size";
+ case 96: return "invalid gAMA chunk size";
+ case 97: return "invalid cHRM chunk size";
+ case 98: return "invalid sRGB chunk size";
+ case 99: return "invalid sRGB rendering intent";
+ case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY";
+ case 101: return "PNG specification does not allow RGB ICC profile on gray color types and vice versa";
+ case 102: return "not allowed to set grayscale ICC profile with colored pixels by PNG specification";
+ case 103: return "invalid palette index in bKGD chunk. Maybe it came before PLTE chunk?";
+ case 104: return "invalid bKGD color while encoding (e.g. palette index out of range)";
+ case 105: return "integer overflow of bitsize";
+ case 106: return "PNG file must have PLTE chunk if color type is palette";
+ case 107: return "color convert from palette mode requested without setting the palette data in it";
+ case 108: return "tried to add more than 256 values to a palette";
+ }
+ return "unknown error code";
+}
+#endif /*LODEPNG_COMPILE_ERROR_TEXT*/
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // C++ Wrapper // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#ifdef LODEPNG_COMPILE_CPP
+namespace lodepng {
+
+#ifdef LODEPNG_COMPILE_DISK
+unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename) {
+ long size = lodepng_filesize(filename.c_str());
+ if(size < 0) return 78;
+ buffer.resize((size_t)size);
+ return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str());
+}
+
+/*write given buffer to the file, overwriting the file, it doesn't append to it.*/
+unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename) {
+ return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str());
+}
+#endif /* LODEPNG_COMPILE_DISK */
+
+#ifdef LODEPNG_COMPILE_ZLIB
+#ifdef LODEPNG_COMPILE_DECODER
+unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings& settings) {
+ unsigned char* buffer = 0;
+ size_t buffersize = 0;
+ unsigned error = zlib_decompress(&buffer, &buffersize, 0, in, insize, &settings);
+ if(buffer) {
+ out.insert(out.end(), &buffer[0], &buffer[buffersize]);
+ lodepng_free(buffer);
+ }
+ return error;
+}
+
+unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
+ const LodePNGDecompressSettings& settings) {
+ return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings);
+}
+#endif /* LODEPNG_COMPILE_DECODER */
+
+#ifdef LODEPNG_COMPILE_ENCODER
+unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
+ const LodePNGCompressSettings& settings) {
+ unsigned char* buffer = 0;
+ size_t buffersize = 0;
+ unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings);
+ if(buffer) {
+ out.insert(out.end(), &buffer[0], &buffer[buffersize]);
+ lodepng_free(buffer);
+ }
+ return error;
+}
+
+unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
+ const LodePNGCompressSettings& settings) {
+ return compress(out, in.empty() ? 0 : &in[0], in.size(), settings);
+}
+#endif /* LODEPNG_COMPILE_ENCODER */
+#endif /* LODEPNG_COMPILE_ZLIB */
+
+
+#ifdef LODEPNG_COMPILE_PNG
+
+State::State() {
+ lodepng_state_init(this);
+}
+
+State::State(const State& other) {
+ lodepng_state_init(this);
+ lodepng_state_copy(this, &other);
+}
+
+State::~State() {
+ lodepng_state_cleanup(this);
+}
+
+State& State::operator=(const State& other) {
+ lodepng_state_copy(this, &other);
+ return *this;
+}
+
+#ifdef LODEPNG_COMPILE_DECODER
+
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const unsigned char* in,
+ size_t insize, LodePNGColorType colortype, unsigned bitdepth) {
+ unsigned char* buffer = 0;
+ unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth);
+ if(buffer && !error) {
+ State state;
+ state.info_raw.colortype = colortype;
+ state.info_raw.bitdepth = bitdepth;
+ size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw);
+ out.insert(out.end(), &buffer[0], &buffer[buffersize]);
+ }
+ lodepng_free(buffer);
+ return error;
+}
+
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ const std::vector<unsigned char>& in, LodePNGColorType colortype, unsigned bitdepth) {
+ return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth);
+}
+
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ State& state,
+ const unsigned char* in, size_t insize) {
+ unsigned char* buffer = NULL;
+ unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize);
+ if(buffer && !error) {
+ size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw);
+ out.insert(out.end(), &buffer[0], &buffer[buffersize]);
+ }
+ lodepng_free(buffer);
+ return error;
+}
+
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ State& state,
+ const std::vector<unsigned char>& in) {
+ return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size());
+}
+
+#ifdef LODEPNG_COMPILE_DISK
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::string& filename,
+ LodePNGColorType colortype, unsigned bitdepth) {
+ std::vector<unsigned char> buffer;
+ /* safe output values in case error happens */
+ w = h = 0;
+ unsigned error = load_file(buffer, filename);
+ if(error) return error;
+ return decode(out, w, h, buffer, colortype, bitdepth);
+}
+#endif /* LODEPNG_COMPILE_DECODER */
+#endif /* LODEPNG_COMPILE_DISK */
+
+#ifdef LODEPNG_COMPILE_ENCODER
+unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth) {
+ unsigned char* buffer;
+ size_t buffersize;
+ unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth);
+ if(buffer) {
+ out.insert(out.end(), &buffer[0], &buffer[buffersize]);
+ lodepng_free(buffer);
+ }
+ return error;
+}
+
+unsigned encode(std::vector<unsigned char>& out,
+ const std::vector<unsigned char>& in, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth) {
+ if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84;
+ return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth);
+}
+
+unsigned encode(std::vector<unsigned char>& out,
+ const unsigned char* in, unsigned w, unsigned h,
+ State& state) {
+ unsigned char* buffer;
+ size_t buffersize;
+ unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state);
+ if(buffer) {
+ out.insert(out.end(), &buffer[0], &buffer[buffersize]);
+ lodepng_free(buffer);
+ }
+ return error;
+}
+
+unsigned encode(std::vector<unsigned char>& out,
+ const std::vector<unsigned char>& in, unsigned w, unsigned h,
+ State& state) {
+ if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84;
+ return encode(out, in.empty() ? 0 : &in[0], w, h, state);
+}
+
+#ifdef LODEPNG_COMPILE_DISK
+unsigned encode(const std::string& filename,
+ const unsigned char* in, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth) {
+ std::vector<unsigned char> buffer;
+ unsigned error = encode(buffer, in, w, h, colortype, bitdepth);
+ if(!error) error = save_file(buffer, filename);
+ return error;
+}
+
+unsigned encode(const std::string& filename,
+ const std::vector<unsigned char>& in, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth) {
+ if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84;
+ return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth);
+}
+#endif /* LODEPNG_COMPILE_DISK */
+#endif /* LODEPNG_COMPILE_ENCODER */
+#endif /* LODEPNG_COMPILE_PNG */
+} /* namespace lodepng */
+#endif /*LODEPNG_COMPILE_CPP*/
diff --git a/src/lodepng/lodepng.h b/src/lodepng/lodepng.h
new file mode 100644
index 0000000..a386459
--- /dev/null
+++ b/src/lodepng/lodepng.h
@@ -0,0 +1,1945 @@
+/*
+LodePNG version 20200306
+
+Copyright (c) 2005-2020 Lode Vandevenne
+
+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 LODEPNG_H
+#define LODEPNG_H
+
+#include <string.h> /*for size_t*/
+
+extern const char* LODEPNG_VERSION_STRING;
+
+/*
+The following #defines are used to create code sections. They can be disabled
+to disable code sections, which can give faster compile time and smaller binary.
+The "NO_COMPILE" defines are designed to be used to pass as defines to the
+compiler command to disable them without modifying this header, e.g.
+-DLODEPNG_NO_COMPILE_ZLIB for gcc.
+In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to
+allow implementing a custom lodepng_crc32.
+*/
+/*deflate & zlib. If disabled, you must specify alternative zlib functions in
+the custom_zlib field of the compress and decompress settings*/
+#ifndef LODEPNG_NO_COMPILE_ZLIB
+#define LODEPNG_COMPILE_ZLIB
+#endif
+
+/*png encoder and png decoder*/
+#ifndef LODEPNG_NO_COMPILE_PNG
+#define LODEPNG_COMPILE_PNG
+#endif
+
+/*deflate&zlib decoder and png decoder*/
+#ifndef LODEPNG_NO_COMPILE_DECODER
+#define LODEPNG_COMPILE_DECODER
+#endif
+
+/*deflate&zlib encoder and png encoder*/
+#ifndef LODEPNG_NO_COMPILE_ENCODER
+#define LODEPNG_COMPILE_ENCODER
+#endif
+
+/*the optional built in harddisk file loading and saving functions*/
+#ifndef LODEPNG_NO_COMPILE_DISK
+#define LODEPNG_COMPILE_DISK
+#endif
+
+/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
+#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
+#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
+#endif
+
+/*ability to convert error numerical codes to English text string*/
+#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
+#define LODEPNG_COMPILE_ERROR_TEXT
+#endif
+
+/*Compile the default allocators (C's free, malloc and realloc). If you disable this,
+you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
+source files with custom allocators.*/
+#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
+#define LODEPNG_COMPILE_ALLOCATORS
+#endif
+
+/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
+#ifdef __cplusplus
+#ifndef LODEPNG_NO_COMPILE_CPP
+#define LODEPNG_COMPILE_CPP
+#endif
+#endif
+
+#ifdef LODEPNG_COMPILE_CPP
+#include <vector>
+#include <string>
+#endif /*LODEPNG_COMPILE_CPP*/
+
+#ifdef LODEPNG_COMPILE_PNG
+/*The PNG color types (also used for raw image).*/
+typedef enum LodePNGColorType {
+ LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
+ LCT_RGB = 2, /*RGB: 8,16 bit*/
+ LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
+ LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
+ LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
+ /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
+ byte value from 0 to 255 that could be present in an invalid PNG file header. Do
+ not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
+ the valid color type names above, or numeric values like 1 or 7 when checking for
+ particular disallowed color type byte values, or cast to integer to print it.*/
+ LCT_MAX_OCTET_VALUE = 255
+} LodePNGColorType;
+
+#ifdef LODEPNG_COMPILE_DECODER
+/*
+Converts PNG data in memory to raw pixel data.
+out: Output parameter. Pointer to buffer that will contain the raw pixel data.
+ After decoding, its size is w * h * (bytes per pixel) bytes larger than
+ initially. Bytes per pixel depends on colortype and bitdepth.
+ Must be freed after usage with free(*out).
+ Note: for 16-bit per channel colors, uses big endian format like PNG does.
+w: Output parameter. Pointer to width of pixel data.
+h: Output parameter. Pointer to height of pixel data.
+in: Memory buffer with the PNG file.
+insize: size of the in buffer.
+colortype: the desired color type for the raw output image. See explanation on PNG color types.
+bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types.
+Return value: LodePNG error code (0 means no error).
+*/
+unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h,
+ const unsigned char* in, size_t insize,
+ LodePNGColorType colortype, unsigned bitdepth);
+
+/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/
+unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h,
+ const unsigned char* in, size_t insize);
+
+/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/
+unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
+ const unsigned char* in, size_t insize);
+
+#ifdef LODEPNG_COMPILE_DISK
+/*
+Load PNG from disk, from file with given name.
+Same as the other decode functions, but instead takes a filename as input.
+*/
+unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
+ const char* filename,
+ LodePNGColorType colortype, unsigned bitdepth);
+
+/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/
+unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
+ const char* filename);
+
+/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/
+unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
+ const char* filename);
+#endif /*LODEPNG_COMPILE_DISK*/
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/*
+Converts raw pixel data into a PNG image in memory. The colortype and bitdepth
+ of the output PNG image cannot be chosen, they are automatically determined
+ by the colortype, bitdepth and content of the input pixel data.
+ Note: for 16-bit per channel colors, needs big endian format like PNG does.
+out: Output parameter. Pointer to buffer that will contain the PNG image data.
+ Must be freed after usage with free(*out).
+outsize: Output parameter. Pointer to the size in bytes of the out buffer.
+image: The raw pixel data to encode. The size of this buffer should be
+ w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth.
+w: width of the raw pixel data in pixels.
+h: height of the raw pixel data in pixels.
+colortype: the color type of the raw input image. See explanation on PNG color types.
+bitdepth: the bit depth of the raw input image. See explanation on PNG color types.
+Return value: LodePNG error code (0 means no error).
+*/
+unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize,
+ const unsigned char* image, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth);
+
+/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/
+unsigned lodepng_encode32(unsigned char** out, size_t* outsize,
+ const unsigned char* image, unsigned w, unsigned h);
+
+/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/
+unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
+ const unsigned char* image, unsigned w, unsigned h);
+
+#ifdef LODEPNG_COMPILE_DISK
+/*
+Converts raw pixel data into a PNG file on disk.
+Same as the other encode functions, but instead takes a filename as output.
+NOTE: This overwrites existing files without warning!
+*/
+unsigned lodepng_encode_file(const char* filename,
+ const unsigned char* image, unsigned w, unsigned h,
+ LodePNGColorType colortype, unsigned bitdepth);
+
+/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/
+unsigned lodepng_encode32_file(const char* filename,
+ const unsigned char* image, unsigned w, unsigned h);
+
+/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/
+unsigned lodepng_encode24_file(const char* filename,
+ const unsigned char* image, unsigned w, unsigned h);
+#endif /*LODEPNG_COMPILE_DISK*/
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+
+#ifdef LODEPNG_COMPILE_CPP
+namespace lodepng {
+#ifdef LODEPNG_COMPILE_DECODER
+/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype
+is the format to output the pixels to. Default is RGBA 8-bit per channel.*/
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ const unsigned char* in, size_t insize,
+ LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ const std::vector<unsigned char>& in,
+ LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+#ifdef LODEPNG_COMPILE_DISK
+/*
+Converts PNG file from disk to raw pixel data in memory.
+Same as the other decode functions, but instead takes a filename as input.
+*/
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ const std::string& filename,
+ LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+#endif /* LODEPNG_COMPILE_DISK */
+#endif /* LODEPNG_COMPILE_DECODER */
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype
+is that of the raw input data. The output PNG color type will be auto chosen.*/
+unsigned encode(std::vector<unsigned char>& out,
+ const unsigned char* in, unsigned w, unsigned h,
+ LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+unsigned encode(std::vector<unsigned char>& out,
+ const std::vector<unsigned char>& in, unsigned w, unsigned h,
+ LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+#ifdef LODEPNG_COMPILE_DISK
+/*
+Converts 32-bit RGBA raw pixel data into a PNG file on disk.
+Same as the other encode functions, but instead takes a filename as output.
+NOTE: This overwrites existing files without warning!
+*/
+unsigned encode(const std::string& filename,
+ const unsigned char* in, unsigned w, unsigned h,
+ LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+unsigned encode(const std::string& filename,
+ const std::vector<unsigned char>& in, unsigned w, unsigned h,
+ LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
+#endif /* LODEPNG_COMPILE_DISK */
+#endif /* LODEPNG_COMPILE_ENCODER */
+} /* namespace lodepng */
+#endif /*LODEPNG_COMPILE_CPP*/
+#endif /*LODEPNG_COMPILE_PNG*/
+
+#ifdef LODEPNG_COMPILE_ERROR_TEXT
+/*Returns an English description of the numerical error code.*/
+const char* lodepng_error_text(unsigned code);
+#endif /*LODEPNG_COMPILE_ERROR_TEXT*/
+
+#ifdef LODEPNG_COMPILE_DECODER
+/*Settings for zlib decompression*/
+typedef struct LodePNGDecompressSettings LodePNGDecompressSettings;
+struct LodePNGDecompressSettings {
+ /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
+ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
+ unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
+
+ /*use custom zlib decoder instead of built in one (default: null)*/
+ unsigned (*custom_zlib)(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGDecompressSettings*);
+ /*use custom deflate decoder instead of built in one (default: null)
+ if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
+ unsigned (*custom_inflate)(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGDecompressSettings*);
+
+ const void* custom_context; /*optional custom settings for custom functions*/
+};
+
+extern const LodePNGDecompressSettings lodepng_default_decompress_settings;
+void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings);
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/*
+Settings for zlib compression. Tweaking these settings tweaks the balance
+between speed and compression ratio.
+*/
+typedef struct LodePNGCompressSettings LodePNGCompressSettings;
+struct LodePNGCompressSettings /*deflate = compress*/ {
+ /*LZ77 related settings*/
+ unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/
+ unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/
+ unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/
+ unsigned minmatch; /*minimum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/
+ unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/
+ unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/
+
+ /*use custom zlib encoder instead of built in one (default: null)*/
+ unsigned (*custom_zlib)(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGCompressSettings*);
+ /*use custom deflate encoder instead of built in one (default: null)
+ if custom_zlib is used, custom_deflate is ignored since only the built in
+ zlib function will call custom_deflate*/
+ unsigned (*custom_deflate)(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGCompressSettings*);
+
+ const void* custom_context; /*optional custom settings for custom functions*/
+};
+
+extern const LodePNGCompressSettings lodepng_default_compress_settings;
+void lodepng_compress_settings_init(LodePNGCompressSettings* settings);
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+#ifdef LODEPNG_COMPILE_PNG
+/*
+Color mode of an image. Contains all information required to decode the pixel
+bits to RGBA colors. This information is the same as used in the PNG file
+format, and is used both for PNG and raw image data in LodePNG.
+*/
+typedef struct LodePNGColorMode {
+ /*header (IHDR)*/
+ LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
+ unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
+
+ /*
+ palette (PLTE and tRNS)
+
+ Dynamically allocated with the colors of the palette, including alpha.
+ This field may not be allocated directly, use lodepng_color_mode_init first,
+ then lodepng_palette_add per color to correctly initialize it (to ensure size
+ of exactly 1024 bytes).
+
+ The alpha channels must be set as well, set them to 255 for opaque images.
+
+ When decoding, by default you can ignore this palette, since LodePNG already
+ fills the palette colors in the pixels of the raw RGBA output.
+
+ The palette is only supported for color type 3.
+ */
+ unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
+ size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
+
+ /*
+ transparent color key (tRNS)
+
+ This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
+ For grayscale PNGs, r, g and b will all 3 be set to the same.
+
+ When decoding, by default you can ignore this information, since LodePNG sets
+ pixels with this key to transparent already in the raw RGBA output.
+
+ The color key is only supported for color types 0 and 2.
+ */
+ unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
+ unsigned key_r; /*red/grayscale component of color key*/
+ unsigned key_g; /*green component of color key*/
+ unsigned key_b; /*blue component of color key*/
+} LodePNGColorMode;
+
+/*init, cleanup and copy functions to use with this struct*/
+void lodepng_color_mode_init(LodePNGColorMode* info);
+void lodepng_color_mode_cleanup(LodePNGColorMode* info);
+/*return value is error code (0 means no error)*/
+unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source);
+/* Makes a temporary LodePNGColorMode that does not need cleanup (no palette) */
+LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth);
+
+void lodepng_palette_clear(LodePNGColorMode* info);
+/*add 1 color to the palette*/
+unsigned lodepng_palette_add(LodePNGColorMode* info,
+ unsigned char r, unsigned char g, unsigned char b, unsigned char a);
+
+/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/
+unsigned lodepng_get_bpp(const LodePNGColorMode* info);
+/*get the amount of color channels used, based on colortype in the struct.
+If a palette is used, it counts as 1 channel.*/
+unsigned lodepng_get_channels(const LodePNGColorMode* info);
+/*is it a grayscale type? (only colortype 0 or 4)*/
+unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info);
+/*has it got an alpha channel? (only colortype 2 or 6)*/
+unsigned lodepng_is_alpha_type(const LodePNGColorMode* info);
+/*has it got a palette? (only colortype 3)*/
+unsigned lodepng_is_palette_type(const LodePNGColorMode* info);
+/*only returns true if there is a palette and there is a value in the palette with alpha < 255.
+Loops through the palette to check this.*/
+unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info);
+/*
+Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image.
+Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels).
+Returns false if the image can only have opaque pixels.
+In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values,
+or if "key_defined" is true.
+*/
+unsigned lodepng_can_have_alpha(const LodePNGColorMode* info);
+/*Returns the byte size of a raw image buffer with given width, height and color mode*/
+size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color);
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+/*The information of a Time chunk in PNG.*/
+typedef struct LodePNGTime {
+ unsigned year; /*2 bytes used (0-65535)*/
+ unsigned month; /*1-12*/
+ unsigned day; /*1-31*/
+ unsigned hour; /*0-23*/
+ unsigned minute; /*0-59*/
+ unsigned second; /*0-60 (to allow for leap seconds)*/
+} LodePNGTime;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+/*Information about the PNG image, except pixels, width and height.*/
+typedef struct LodePNGInfo {
+ /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
+ unsigned compression_method;/*compression method of the original file. Always 0.*/
+ unsigned filter_method; /*filter method of the original file*/
+ unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
+ LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ /*
+ Suggested background color chunk (bKGD)
+
+ This uses the same color mode and bit depth as the PNG (except no alpha channel),
+ with values truncated to the bit depth in the unsigned integer.
+
+ For grayscale and palette PNGs, the value is stored in background_r. The values
+ in background_g and background_b are then unused.
+
+ So when decoding, you may get these in a different color mode than the one you requested
+ for the raw pixels.
+
+ When encoding with auto_convert, you must use the color model defined in info_png.color for
+ these values. The encoder normally ignores info_png.color when auto_convert is on, but will
+ use it to interpret these values (and convert copies of them to its chosen color model).
+
+ When encoding, avoid setting this to an expensive color, such as a non-gray value
+ when the image is gray, or the compression will be worse since it will be forced to
+ write the PNG with a more expensive color mode (when auto_convert is on).
+
+ The decoder does not use this background color to edit the color of pixels. This is a
+ completely optional metadata feature.
+ */
+ unsigned background_defined; /*is a suggested background color given?*/
+ unsigned background_r; /*red/gray/palette component of suggested background color*/
+ unsigned background_g; /*green component of suggested background color*/
+ unsigned background_b; /*blue component of suggested background color*/
+
+ /*
+ non-international text chunks (tEXt and zTXt)
+
+ The char** arrays each contain num strings. The actual messages are in
+ text_strings, while text_keys are keywords that give a short description what
+ the actual text represents, e.g. Title, Author, Description, or anything else.
+
+ All the string fields below including keys, names and language tags are null terminated.
+ The PNG specification uses null characters for the keys, names and tags, and forbids null
+ characters to appear in the main text which is why we can use null termination everywhere here.
+
+ A keyword is minimum 1 character and maximum 79 characters long. It's
+ discouraged to use a single line length longer than 79 characters for texts.
+
+ Don't allocate these text buffers yourself. Use the init/cleanup functions
+ correctly and use lodepng_add_text and lodepng_clear_text.
+ */
+ size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
+ char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
+ char** text_strings; /*the actual text*/
+
+ /*
+ international text chunks (iTXt)
+ Similar to the non-international text chunks, but with additional strings
+ "langtags" and "transkeys".
+ */
+ size_t itext_num; /*the amount of international texts in this PNG*/
+ char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
+ char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/
+ char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/
+ char** itext_strings; /*the actual international text - UTF-8 string*/
+
+ /*time chunk (tIME)*/
+ unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/
+ LodePNGTime time;
+
+ /*phys chunk (pHYs)*/
+ unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/
+ unsigned phys_x; /*pixels per unit in x direction*/
+ unsigned phys_y; /*pixels per unit in y direction*/
+ unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
+
+ /*
+ Color profile related chunks: gAMA, cHRM, sRGB, iCPP
+
+ LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
+ profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
+ use these values with a color management library.
+
+ See the PNG, ICC and sRGB specifications for more information about the meaning of these values.
+ */
+
+ /* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */
+ unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */
+ unsigned gama_gamma; /* Gamma exponent times 100000 */
+
+ /* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */
+ unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */
+ unsigned chrm_white_x; /* White Point x times 100000 */
+ unsigned chrm_white_y; /* White Point y times 100000 */
+ unsigned chrm_red_x; /* Red x times 100000 */
+ unsigned chrm_red_y; /* Red y times 100000 */
+ unsigned chrm_green_x; /* Green x times 100000 */
+ unsigned chrm_green_y; /* Green y times 100000 */
+ unsigned chrm_blue_x; /* Blue x times 100000 */
+ unsigned chrm_blue_y; /* Blue y times 100000 */
+
+ /*
+ sRGB chunk: optional. May not appear at the same time as iCCP.
+ If gAMA is also present gAMA must contain value 45455.
+ If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000.
+ */
+ unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */
+ unsigned srgb_intent; /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */
+
+ /*
+ iCCP chunk: optional. May not appear at the same time as sRGB.
+
+ LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a
+ separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color
+ management and conversions.
+
+ For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC
+ profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and
+ enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile.
+
+ For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-gray
+ PNG color types and a "GRAY" profile for gray PNG color types. If you disable auto_convert, you must ensure
+ the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is
+ enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder
+ error if the pixel data has non-gray pixels for a GRAY profile, or a silent less-optimal compression of the pixel
+ data if the pixels could be encoded as grayscale but the ICC profile is RGB.
+
+ To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so
+ make sure you compute it carefully to avoid the above problems.
+ */
+ unsigned iccp_defined; /* Whether an iCCP chunk is present (0 = not present, 1 = present). */
+ char* iccp_name; /* Null terminated string with profile name, 1-79 bytes */
+ /*
+ The ICC profile in iccp_profile_size bytes.
+ Don't allocate this buffer yourself. Use the init/cleanup functions
+ correctly and use lodepng_set_icc and lodepng_clear_icc.
+ */
+ unsigned char* iccp_profile;
+ unsigned iccp_profile_size; /* The size of iccp_profile in bytes */
+
+ /* End of color profile related chunks */
+
+
+ /*
+ unknown chunks: chunks not known by LodePNG, passed on byte for byte.
+
+ There are 3 buffers, one for each position in the PNG where unknown chunks can appear.
+ Each buffer contains all unknown chunks for that position consecutively.
+ The 3 positions are:
+ 0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND.
+
+ For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag
+ above in here, since the encoder will blindly follow this and could then encode an invalid PNG file
+ (such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use
+ this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST),
+ or any non-standard PNG chunk.
+
+ Do not allocate or traverse this data yourself. Use the chunk traversing functions declared
+ later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct.
+ */
+ unsigned char* unknown_chunks_data[3];
+ size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+} LodePNGInfo;
+
+/*init, cleanup and copy functions to use with this struct*/
+void lodepng_info_init(LodePNGInfo* info);
+void lodepng_info_cleanup(LodePNGInfo* info);
+/*return value is error code (0 means no error)*/
+unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source);
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/
+void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
+
+unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag,
+ const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/
+void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/
+
+/*replaces if exists*/
+unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size);
+void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+
+/*
+Converts raw buffer from one color type to another color type, based on
+LodePNGColorMode structs to describe the input and output color type.
+See the reference manual at the end of this header file to see which color conversions are supported.
+return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported)
+The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel
+of the output color type (lodepng_get_bpp).
+For < 8 bpp images, there should not be padding bits at the end of scanlines.
+For 16-bit per channel colors, uses big endian format like PNG does.
+Return value is LodePNG error code
+*/
+unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
+ const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in,
+ unsigned w, unsigned h);
+
+#ifdef LODEPNG_COMPILE_DECODER
+/*
+Settings for the decoder. This contains settings for the PNG and the Zlib
+decoder, but not the Info settings from the Info structs.
+*/
+typedef struct LodePNGDecoderSettings {
+ LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
+
+ /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
+ unsigned ignore_crc; /*ignore CRC checksums*/
+ unsigned ignore_critical; /*ignore unknown critical chunks*/
+ unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
+ /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
+ errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
+ strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
+ in string keys, etc... */
+
+ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
+
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
+ /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
+ unsigned remember_unknown_chunks;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+} LodePNGDecoderSettings;
+
+void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings);
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/
+typedef enum LodePNGFilterStrategy {
+ /*every filter at zero*/
+ LFS_ZERO = 0,
+ /*every filter at 1, 2, 3 or 4 (paeth), unlike LFS_ZERO not a good choice, but for testing*/
+ LFS_ONE = 1,
+ LFS_TWO = 2,
+ LFS_THREE = 3,
+ LFS_FOUR = 4,
+ /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/
+ LFS_MINSUM,
+ /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending
+ on the image, this is better or worse than minsum.*/
+ LFS_ENTROPY,
+ /*
+ Brute-force-search PNG filters by compressing each filter for each scanline.
+ Experimental, very slow, and only rarely gives better compression than MINSUM.
+ */
+ LFS_BRUTE_FORCE,
+ /*use predefined_filters buffer: you specify the filter type for each scanline*/
+ LFS_PREDEFINED
+} LodePNGFilterStrategy;
+
+/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...),
+which helps decide which color model to use for encoding.
+Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/
+typedef struct LodePNGColorStats {
+ unsigned colored; /*not grayscale*/
+ unsigned key; /*image is not opaque and color key is possible instead of full alpha*/
+ unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/
+ unsigned short key_g;
+ unsigned short key_b;
+ unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/
+ unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16 or allow_palette is disabled.*/
+ unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order, only valid when numcolors is valid*/
+ unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for grayscale only. 16 if 16-bit per channel required.*/
+ size_t numpixels;
+
+ /*user settings for computing/using the stats*/
+ unsigned allow_palette; /*default 1. if 0, disallow choosing palette colortype in auto_choose_color, and don't count numcolors*/
+ unsigned allow_greyscale; /*default 1. if 0, choose RGB or RGBA even if the image only has gray colors*/
+} LodePNGColorStats;
+
+void lodepng_color_stats_init(LodePNGColorStats* stats);
+
+/*Get a LodePNGColorStats of the image. The stats must already have been inited.
+Returns error code (e.g. alloc fail) or 0 if ok.*/
+unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
+ const unsigned char* image, unsigned w, unsigned h,
+ const LodePNGColorMode* mode_in);
+
+/*Settings for the encoder.*/
+typedef struct LodePNGEncoderSettings {
+ LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/
+
+ unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/
+
+ /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than
+ 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to
+ completely follow the official PNG heuristic, filter_palette_zero must be true and
+ filter_strategy must be LFS_MINSUM*/
+ unsigned filter_palette_zero;
+ /*Which filter strategy to use when not using zeroes due to filter_palette_zero.
+ Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/
+ LodePNGFilterStrategy filter_strategy;
+ /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with
+ the same length as the amount of scanlines in the image, and each value must <= 5. You
+ have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero
+ must be set to 0 to ensure this is also used on palette or low bitdepth images.*/
+ const unsigned char* predefined_filters;
+
+ /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette).
+ If colortype is 3, PLTE is _always_ created.*/
+ unsigned force_palette;
+#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
+ /*add LodePNG identifier and version as a text chunk, for debugging*/
+ unsigned add_id;
+ /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/
+ unsigned text_compression;
+#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
+} LodePNGEncoderSettings;
+
+void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings);
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+
+#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
+/*The settings, state and information for extended encoding and decoding.*/
+typedef struct LodePNGState {
+#ifdef LODEPNG_COMPILE_DECODER
+ LodePNGDecoderSettings decoder; /*the decoding settings*/
+#endif /*LODEPNG_COMPILE_DECODER*/
+#ifdef LODEPNG_COMPILE_ENCODER
+ LodePNGEncoderSettings encoder; /*the encoding settings*/
+#endif /*LODEPNG_COMPILE_ENCODER*/
+ LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
+ LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
+ unsigned error;
+} LodePNGState;
+
+/*init, cleanup and copy functions to use with this struct*/
+void lodepng_state_init(LodePNGState* state);
+void lodepng_state_cleanup(LodePNGState* state);
+void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source);
+#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */
+
+#ifdef LODEPNG_COMPILE_DECODER
+/*
+Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and
+getting much more information about the PNG image and color mode.
+*/
+unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h,
+ LodePNGState* state,
+ const unsigned char* in, size_t insize);
+
+/*
+Read the PNG header, but not the actual data. This returns only the information
+that is in the IHDR chunk of the PNG, such as width, height and color type. The
+information is placed in the info_png field of the LodePNGState.
+*/
+unsigned lodepng_inspect(unsigned* w, unsigned* h,
+ LodePNGState* state,
+ const unsigned char* in, size_t insize);
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+/*
+Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it
+read in the state. Returns error code on failure.
+Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const
+to find the desired chunk type, and if non null use lodepng_inspect_chunk (with
+chunk_pointer - start_of_file as pos).
+Supports most metadata chunks from the PNG standard (gAMA, bKGD, tEXt, ...).
+Ignores unsupported, unknown, non-metadata or IHDR chunks (without error).
+Requirements: &in[pos] must point to start of a chunk, must use regular
+lodepng_inspect first since format of most other chunks depends on IHDR, and if
+there is a PLTE chunk, that one must be inspected before tRNS or bKGD.
+*/
+unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
+ const unsigned char* in, size_t insize);
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/
+unsigned lodepng_encode(unsigned char** out, size_t* outsize,
+ const unsigned char* image, unsigned w, unsigned h,
+ LodePNGState* state);
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+/*
+The lodepng_chunk functions are normally not needed, except to traverse the
+unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
+It also allows traversing the chunks of an encoded PNG file yourself.
+
+The chunk pointer always points to the beginning of the chunk itself, that is
+the first byte of the 4 length bytes.
+
+In the PNG file format, chunks have the following format:
+-4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer)
+-4 bytes chunk type (ASCII a-z,A-Z only, see below)
+-length bytes of data (may be 0 bytes if length was 0)
+-4 bytes of CRC, computed on chunk name + data
+
+The first chunk starts at the 8th byte of the PNG file, the entire rest of the file
+exists out of concatenated chunks with the above format.
+
+PNG standard chunk ASCII naming conventions:
+-First byte: uppercase = critical, lowercase = ancillary
+-Second byte: uppercase = public, lowercase = private
+-Third byte: must be uppercase
+-Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
+*/
+
+/*
+Gets the length of the data of the chunk. Total chunk length has 12 bytes more.
+There must be at least 4 bytes to read from. If the result value is too large,
+it may be corrupt data.
+*/
+unsigned lodepng_chunk_length(const unsigned char* chunk);
+
+/*puts the 4-byte type in null terminated string*/
+void lodepng_chunk_type(char type[5], const unsigned char* chunk);
+
+/*check if the type is the given type*/
+unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type);
+
+/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/
+unsigned char lodepng_chunk_ancillary(const unsigned char* chunk);
+
+/*0: public, 1: private (see PNG standard)*/
+unsigned char lodepng_chunk_private(const unsigned char* chunk);
+
+/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/
+unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk);
+
+/*get pointer to the data of the chunk, where the input points to the header of the chunk*/
+unsigned char* lodepng_chunk_data(unsigned char* chunk);
+const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk);
+
+/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/
+unsigned lodepng_chunk_check_crc(const unsigned char* chunk);
+
+/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/
+void lodepng_chunk_generate_crc(unsigned char* chunk);
+
+/*
+Iterate to next chunks, allows iterating through all chunks of the PNG file.
+Input must be at the beginning of a chunk (result of a previous lodepng_chunk_next call,
+or the 8th byte of a PNG file which always has the first chunk), or alternatively may
+point to the first byte of the PNG file (which is not a chunk but the magic header, the
+function will then skip over it and return the first real chunk).
+Will output pointer to the start of the next chunk, or at or beyond end of the file if there
+is no more chunk after this or possibly if the chunk is corrupt.
+Start this process at the 8th byte of the PNG file.
+In a non-corrupt PNG file, the last chunk should have name "IEND".
+*/
+unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end);
+const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end);
+
+/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/
+unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]);
+const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]);
+
+/*
+Appends chunk to the data in out. The given chunk should already have its chunk header.
+The out variable and outsize are updated to reflect the new reallocated buffer.
+Returns error code (0 if it went ok)
+*/
+unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk);
+
+/*
+Appends new chunk to out. The chunk to append is given by giving its length, type
+and data separately. The type is a 4-letter string.
+The out variable and outsize are updated to reflect the new reallocated buffer.
+Returne error code (0 if it went ok)
+*/
+unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
+ const char* type, const unsigned char* data);
+
+
+/*Calculate CRC32 of buffer*/
+unsigned lodepng_crc32(const unsigned char* buf, size_t len);
+#endif /*LODEPNG_COMPILE_PNG*/
+
+
+#ifdef LODEPNG_COMPILE_ZLIB
+/*
+This zlib part can be used independently to zlib compress and decompress a
+buffer. It cannot be used to create gzip files however, and it only supports the
+part of zlib that is required for PNG, it does not support dictionaries.
+*/
+
+#ifdef LODEPNG_COMPILE_DECODER
+/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/
+unsigned lodepng_inflate(unsigned char** out, size_t* outsize,
+ const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings* settings);
+
+/*
+Decompresses Zlib data. Reallocates the out buffer and appends the data. The
+data must be according to the zlib specification.
+Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
+buffer and *outsize its size in bytes. out must be freed by user after usage.
+*/
+unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize,
+ const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings* settings);
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/*
+Compresses data with Zlib. Reallocates the out buffer and appends the data.
+Zlib adds a small header and trailer around the deflate data.
+The data is output in the format of the zlib specification.
+Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
+buffer and *outsize its size in bytes. out must be freed by user after usage.
+*/
+unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize,
+ const unsigned char* in, size_t insize,
+ const LodePNGCompressSettings* settings);
+
+/*
+Find length-limited Huffman code for given frequencies. This function is in the
+public interface only for tests, it's used internally by lodepng_deflate.
+*/
+unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
+ size_t numcodes, unsigned maxbitlen);
+
+/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/
+unsigned lodepng_deflate(unsigned char** out, size_t* outsize,
+ const unsigned char* in, size_t insize,
+ const LodePNGCompressSettings* settings);
+
+#endif /*LODEPNG_COMPILE_ENCODER*/
+#endif /*LODEPNG_COMPILE_ZLIB*/
+
+#ifdef LODEPNG_COMPILE_DISK
+/*
+Load a file from disk into buffer. The function allocates the out buffer, and
+after usage you should free it.
+out: output parameter, contains pointer to loaded buffer.
+outsize: output parameter, size of the allocated out buffer
+filename: the path to the file to load
+return value: error code (0 means ok)
+*/
+unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename);
+
+/*
+Save a file from buffer to disk. Warning, if it exists, this function overwrites
+the file without warning!
+buffer: the buffer to write
+buffersize: size of the buffer to write
+filename: the path to the file to save to
+return value: error code (0 means ok)
+*/
+unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
+#endif /*LODEPNG_COMPILE_DISK*/
+
+#ifdef LODEPNG_COMPILE_CPP
+/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */
+namespace lodepng {
+#ifdef LODEPNG_COMPILE_PNG
+class State : public LodePNGState {
+ public:
+ State();
+ State(const State& other);
+ ~State();
+ State& operator=(const State& other);
+};
+
+#ifdef LODEPNG_COMPILE_DECODER
+/* Same as other lodepng::decode, but using a State for more settings and information. */
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ State& state,
+ const unsigned char* in, size_t insize);
+unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
+ State& state,
+ const std::vector<unsigned char>& in);
+#endif /*LODEPNG_COMPILE_DECODER*/
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/* Same as other lodepng::encode, but using a State for more settings and information. */
+unsigned encode(std::vector<unsigned char>& out,
+ const unsigned char* in, unsigned w, unsigned h,
+ State& state);
+unsigned encode(std::vector<unsigned char>& out,
+ const std::vector<unsigned char>& in, unsigned w, unsigned h,
+ State& state);
+#endif /*LODEPNG_COMPILE_ENCODER*/
+
+#ifdef LODEPNG_COMPILE_DISK
+/*
+Load a file from disk into an std::vector.
+return value: error code (0 means ok)
+*/
+unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename);
+
+/*
+Save the binary data in an std::vector to a file on disk. The file is overwritten
+without warning.
+*/
+unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
+#endif /* LODEPNG_COMPILE_DISK */
+#endif /* LODEPNG_COMPILE_PNG */
+
+#ifdef LODEPNG_COMPILE_ZLIB
+#ifdef LODEPNG_COMPILE_DECODER
+/* Zlib-decompress an unsigned char buffer */
+unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
+ const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
+
+/* Zlib-decompress an std::vector */
+unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
+ const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
+#endif /* LODEPNG_COMPILE_DECODER */
+
+#ifdef LODEPNG_COMPILE_ENCODER
+/* Zlib-compress an unsigned char buffer */
+unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
+ const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
+
+/* Zlib-compress an std::vector */
+unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
+ const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
+#endif /* LODEPNG_COMPILE_ENCODER */
+#endif /* LODEPNG_COMPILE_ZLIB */
+} /* namespace lodepng */
+#endif /*LODEPNG_COMPILE_CPP*/
+
+/*
+TODO:
+[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often
+[.] check compatibility with various compilers - done but needs to be redone for every newer version
+[X] converting color to 16-bit per channel types
+[X] support color profile chunk types (but never let them touch RGB values by default)
+[ ] support all public PNG chunk types (almost done except sBIT, sPLT and hIST)
+[ ] make sure encoder generates no chunks with size > (2^31)-1
+[ ] partial decoding (stream processing)
+[X] let the "isFullyOpaque" function check color keys and transparent palettes too
+[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl"
+[ ] allow treating some errors like warnings, when image is recoverable (e.g. 69, 57, 58)
+[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ...
+[ ] error messages with line numbers (and version)
+[ ] errors in state instead of as return code?
+[ ] new errors/warnings like suspiciously big decompressed ztxt or iccp chunk
+[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes
+[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ...
+[ ] allow user to give data (void*) to custom allocator
+[X] provide alternatives for C library functions not present on some platforms (memcpy, ...)
+*/
+
+#endif /*LODEPNG_H inclusion guard*/
+
+/*
+LodePNG Documentation
+---------------------
+
+0. table of contents
+--------------------
+
+ 1. about
+ 1.1. supported features
+ 1.2. features not supported
+ 2. C and C++ version
+ 3. security
+ 4. decoding
+ 5. encoding
+ 6. color conversions
+ 6.1. PNG color types
+ 6.2. color conversions
+ 6.3. padding bits
+ 6.4. A note about 16-bits per channel and endianness
+ 7. error values
+ 8. chunks and PNG editing
+ 9. compiler support
+ 10. examples
+ 10.1. decoder C++ example
+ 10.2. decoder C example
+ 11. state settings reference
+ 12. changes
+ 13. contact information
+
+
+1. about
+--------
+
+PNG is a file format to store raster images losslessly with good compression,
+supporting different color types and alpha channel.
+
+LodePNG is a PNG codec according to the Portable Network Graphics (PNG)
+Specification (Second Edition) - W3C Recommendation 10 November 2003.
+
+The specifications used are:
+
+*) Portable Network Graphics (PNG) Specification (Second Edition):
+ http://www.w3.org/TR/2003/REC-PNG-20031110
+*) RFC 1950 ZLIB Compressed Data Format version 3.3:
+ http://www.gzip.org/zlib/rfc-zlib.html
+*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3:
+ http://www.gzip.org/zlib/rfc-deflate.html
+
+The most recent version of LodePNG can currently be found at
+http://lodev.org/lodepng/
+
+LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds
+extra functionality.
+
+LodePNG exists out of two files:
+-lodepng.h: the header file for both C and C++
+-lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage
+
+If you want to start using LodePNG right away without reading this doc, get the
+examples from the LodePNG website to see how to use it in code, or check the
+smaller examples in chapter 13 here.
+
+LodePNG is simple but only supports the basic requirements. To achieve
+simplicity, the following design choices were made: There are no dependencies
+on any external library. There are functions to decode and encode a PNG with
+a single function call, and extended versions of these functions taking a
+LodePNGState struct allowing to specify or get more information. By default
+the colors of the raw image are always RGB or RGBA, no matter what color type
+the PNG file uses. To read and write files, there are simple functions to
+convert the files to/from buffers in memory.
+
+This all makes LodePNG suitable for loading textures in games, demos and small
+programs, ... It's less suitable for full fledged image editors, loading PNGs
+over network (it requires all the image data to be available before decoding can
+begin), life-critical systems, ...
+
+1.1. supported features
+-----------------------
+
+The following features are supported by the decoder:
+
+*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image,
+ or the same color type as the PNG
+*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image
+*) Adam7 interlace and deinterlace for any color type
+*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk
+*) support for alpha channels, including RGBA color model, translucent palettes and color keying
+*) zlib decompression (inflate)
+*) zlib compression (deflate)
+*) CRC32 and ADLER32 checksums
+*) colorimetric color profile conversions: currently experimentally available in lodepng_util.cpp only,
+ plus alternatively ability to pass on chroma/gamma/ICC profile information to other color management system.
+*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks.
+*) the following chunks are supported by both encoder and decoder:
+ IHDR: header information
+ PLTE: color palette
+ IDAT: pixel data
+ IEND: the final chunk
+ tRNS: transparency for palettized images
+ tEXt: textual information
+ zTXt: compressed textual information
+ iTXt: international textual information
+ bKGD: suggested background color
+ pHYs: physical dimensions
+ tIME: modification time
+ cHRM: RGB chromaticities
+ gAMA: RGB gamma correction
+ iCCP: ICC color profile
+ sRGB: rendering intent
+
+1.2. features not supported
+---------------------------
+
+The following features are _not_ supported:
+
+*) some features needed to make a conformant PNG-Editor might be still missing.
+*) partial loading/stream processing. All data must be available and is processed in one call.
+*) The following public chunks are not (yet) supported but treated as unknown chunks by LodePNG:
+ sBIT
+ hIST
+ sPLT
+
+
+2. C and C++ version
+--------------------
+
+The C version uses buffers allocated with alloc that you need to free()
+yourself. You need to use init and cleanup functions for each struct whenever
+using a struct from the C version to avoid exploits and memory leaks.
+
+The C++ version has extra functions with std::vectors in the interface and the
+lodepng::State class which is a LodePNGState with constructor and destructor.
+
+These files work without modification for both C and C++ compilers because all
+the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers
+ignore it, and the C code is made to compile both with strict ISO C90 and C++.
+
+To use the C++ version, you need to rename the source file to lodepng.cpp
+(instead of lodepng.c), and compile it with a C++ compiler.
+
+To use the C version, you need to rename the source file to lodepng.c (instead
+of lodepng.cpp), and compile it with a C compiler.
+
+
+3. Security
+-----------
+
+Even if carefully designed, it's always possible that LodePNG contains possible
+exploits. If you discover one, please let me know, and it will be fixed.
+
+When using LodePNG, care has to be taken with the C version of LodePNG, as well
+as the C-style structs when working with C++. The following conventions are used
+for all C-style structs:
+
+-if a struct has a corresponding init function, always call the init function when making a new one
+-if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks
+-if a struct has a corresponding copy function, use the copy function instead of "=".
+ The destination must also be inited already.
+
+
+4. Decoding
+-----------
+
+Decoding converts a PNG compressed image to a raw pixel buffer.
+
+Most documentation on using the decoder is at its declarations in the header
+above. For C, simple decoding can be done with functions such as
+lodepng_decode32, and more advanced decoding can be done with the struct
+LodePNGState and lodepng_decode. For C++, all decoding can be done with the
+various lodepng::decode functions, and lodepng::State can be used for advanced
+features.
+
+When using the LodePNGState, it uses the following fields for decoding:
+*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here
+*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get
+*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use
+
+LodePNGInfo info_png
+--------------------
+
+After decoding, this contains extra information of the PNG image, except the actual
+pixels, width and height because these are already gotten directly from the decoder
+functions.
+
+It contains for example the original color type of the PNG image, text comments,
+suggested background color, etc... More details about the LodePNGInfo struct are
+at its declaration documentation.
+
+LodePNGColorMode info_raw
+-------------------------
+
+When decoding, here you can specify which color type you want
+the resulting raw image to be. If this is different from the colortype of the
+PNG, then the decoder will automatically convert the result. This conversion
+always works, except if you want it to convert a color PNG to grayscale or to
+a palette with missing colors.
+
+By default, 32-bit color is used for the result.
+
+LodePNGDecoderSettings decoder
+------------------------------
+
+The settings can be used to ignore the errors created by invalid CRC and Adler32
+chunks, and to disable the decoding of tEXt chunks.
+
+There's also a setting color_convert, true by default. If false, no conversion
+is done, the resulting data will be as it was in the PNG (after decompression)
+and you'll have to puzzle the colors of the pixels together yourself using the
+color type information in the LodePNGInfo.
+
+
+5. Encoding
+-----------
+
+Encoding converts a raw pixel buffer to a PNG compressed image.
+
+Most documentation on using the encoder is at its declarations in the header
+above. For C, simple encoding can be done with functions such as
+lodepng_encode32, and more advanced decoding can be done with the struct
+LodePNGState and lodepng_encode. For C++, all encoding can be done with the
+various lodepng::encode functions, and lodepng::State can be used for advanced
+features.
+
+Like the decoder, the encoder can also give errors. However it gives less errors
+since the encoder input is trusted, the decoder input (a PNG image that could
+be forged by anyone) is not trusted.
+
+When using the LodePNGState, it uses the following fields for encoding:
+*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be.
+*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has
+*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use
+
+LodePNGInfo info_png
+--------------------
+
+When encoding, you use this the opposite way as when decoding: for encoding,
+you fill in the values you want the PNG to have before encoding. By default it's
+not needed to specify a color type for the PNG since it's automatically chosen,
+but it's possible to choose it yourself given the right settings.
+
+The encoder will not always exactly match the LodePNGInfo struct you give,
+it tries as close as possible. Some things are ignored by the encoder. The
+encoder uses, for example, the following settings from it when applicable:
+colortype and bitdepth, text chunks, time chunk, the color key, the palette, the
+background color, the interlace method, unknown chunks, ...
+
+When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk.
+If the palette contains any colors for which the alpha channel is not 255 (so
+there are translucent colors in the palette), it'll add a tRNS chunk.
+
+LodePNGColorMode info_raw
+-------------------------
+
+You specify the color type of the raw image that you give to the input here,
+including a possible transparent color key and palette you happen to be using in
+your raw image data.
+
+By default, 32-bit color is assumed, meaning your input has to be in RGBA
+format with 4 bytes (unsigned chars) per pixel.
+
+LodePNGEncoderSettings encoder
+------------------------------
+
+The following settings are supported (some are in sub-structs):
+*) auto_convert: when this option is enabled, the encoder will
+automatically choose the smallest possible color mode (including color key) that
+can encode the colors of all pixels without information loss.
+*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree,
+ 2 = dynamic huffman tree (best compression). Should be 2 for proper
+ compression.
+*) use_lz77: whether or not to use LZ77 for compressed block types. Should be
+ true for proper compression.
+*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value
+ 2048 by default, but can be set to 32768 for better, but slow, compression.
+*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE
+ chunk if force_palette is true. This can used as suggested palette to convert
+ to by viewers that don't support more than 256 colors (if those still exist)
+*) add_id: add text chunk "Encoder: LodePNG <version>" to the image.
+*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks.
+ zTXt chunks use zlib compression on the text. This gives a smaller result on
+ large texts but a larger result on small texts (such as a single program name).
+ It's all tEXt or all zTXt though, there's no separate setting per text yet.
+
+
+6. color conversions
+--------------------
+
+An important thing to note about LodePNG, is that the color type of the PNG, and
+the color type of the raw image, are completely independent. By default, when
+you decode a PNG, you get the result as a raw image in the color type you want,
+no matter whether the PNG was encoded with a palette, grayscale or RGBA color.
+And if you encode an image, by default LodePNG will automatically choose the PNG
+color type that gives good compression based on the values of colors and amount
+of colors in the image. It can be configured to let you control it instead as
+well, though.
+
+To be able to do this, LodePNG does conversions from one color mode to another.
+It can convert from almost any color type to any other color type, except the
+following conversions: RGB to grayscale is not supported, and converting to a
+palette when the palette doesn't have a required color is not supported. This is
+not supported on purpose: this is information loss which requires a color
+reduction algorithm that is beyond the scope of a PNG encoder (yes, RGB to gray
+is easy, but there are multiple ways if you want to give some channels more
+weight).
+
+By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB
+color, no matter what color type the PNG has. And by default when encoding,
+LodePNG automatically picks the best color model for the output PNG, and expects
+the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control
+the color format of the images yourself, you can skip this chapter.
+
+6.1. PNG color types
+--------------------
+
+A PNG image can have many color types, ranging from 1-bit color to 64-bit color,
+as well as palettized color modes. After the zlib decompression and unfiltering
+in the PNG image is done, the raw pixel data will have that color type and thus
+a certain amount of bits per pixel. If you want the output raw image after
+decoding to have another color type, a conversion is done by LodePNG.
+
+The PNG specification gives the following color types:
+
+0: grayscale, bit depths 1, 2, 4, 8, 16
+2: RGB, bit depths 8 and 16
+3: palette, bit depths 1, 2, 4 and 8
+4: grayscale with alpha, bit depths 8 and 16
+6: RGBA, bit depths 8 and 16
+
+Bit depth is the amount of bits per pixel per color channel. So the total amount
+of bits per pixel is: amount of channels * bitdepth.
+
+6.2. color conversions
+----------------------
+
+As explained in the sections about the encoder and decoder, you can specify
+color types and bit depths in info_png and info_raw to change the default
+behaviour.
+
+If, when decoding, you want the raw image to be something else than the default,
+you need to set the color type and bit depth you want in the LodePNGColorMode,
+or the parameters colortype and bitdepth of the simple decoding function.
+
+If, when encoding, you use another color type than the default in the raw input
+image, you need to specify its color type and bit depth in the LodePNGColorMode
+of the raw image, or use the parameters colortype and bitdepth of the simple
+encoding function.
+
+If, when encoding, you don't want LodePNG to choose the output PNG color type
+but control it yourself, you need to set auto_convert in the encoder settings
+to false, and specify the color type you want in the LodePNGInfo of the
+encoder (including palette: it can generate a palette if auto_convert is true,
+otherwise not).
+
+If the input and output color type differ (whether user chosen or auto chosen),
+LodePNG will do a color conversion, which follows the rules below, and may
+sometimes result in an error.
+
+To avoid some confusion:
+-the decoder converts from PNG to raw image
+-the encoder converts from raw image to PNG
+-the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image
+-the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG
+-when encoding, the color type in LodePNGInfo is ignored if auto_convert
+ is enabled, it is automatically generated instead
+-when decoding, the color type in LodePNGInfo is set by the decoder to that of the original
+ PNG image, but it can be ignored since the raw image has the color type you requested instead
+-if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion
+ between the color types is done if the color types are supported. If it is not
+ supported, an error is returned. If the types are the same, no conversion is done.
+-even though some conversions aren't supported, LodePNG supports loading PNGs from any
+ colortype and saving PNGs to any colortype, sometimes it just requires preparing
+ the raw image correctly before encoding.
+-both encoder and decoder use the same color converter.
+
+The function lodepng_convert does the color conversion. It is available in the
+interface but normally isn't needed since the encoder and decoder already call
+it.
+
+Non supported color conversions:
+-color to grayscale when non-gray pixels are present: no error is thrown, but
+the result will look ugly because only the red channel is taken (it assumes all
+three channels are the same in this case so ignores green and blue). The reason
+no error is given is to allow converting from three-channel grayscale images to
+one-channel even if there are numerical imprecisions.
+-anything to palette when the palette does not have an exact match for a from-color
+in it: in this case an error is thrown
+
+Supported color conversions:
+-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA
+-any gray or gray+alpha, to gray or gray+alpha
+-anything to a palette, as long as the palette has the requested colors in it
+-removing alpha channel
+-higher to smaller bitdepth, and vice versa
+
+If you want no color conversion to be done (e.g. for speed or control):
+-In the encoder, you can make it save a PNG with any color type by giving the
+raw color mode and LodePNGInfo the same color mode, and setting auto_convert to
+false.
+-In the decoder, you can make it store the pixel data in the same color type
+as the PNG has, by setting the color_convert setting to false. Settings in
+info_raw are then ignored.
+
+6.3. padding bits
+-----------------
+
+In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines
+have a bit amount that isn't a multiple of 8, then padding bits are used so that each
+scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output.
+The raw input image you give to the encoder, and the raw output image you get from the decoder
+will NOT have these padding bits, e.g. in the case of a 1-bit image with a width
+of 7 pixels, the first pixel of the second scanline will the 8th bit of the first byte,
+not the first bit of a new byte.
+
+6.4. A note about 16-bits per channel and endianness
+----------------------------------------------------
+
+LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like
+for any other color format. The 16-bit values are stored in big endian (most
+significant byte first) in these arrays. This is the opposite order of the
+little endian used by x86 CPU's.
+
+LodePNG always uses big endian because the PNG file format does so internally.
+Conversions to other formats than PNG uses internally are not supported by
+LodePNG on purpose, there are myriads of formats, including endianness of 16-bit
+colors, the order in which you store R, G, B and A, and so on. Supporting and
+converting to/from all that is outside the scope of LodePNG.
+
+This may mean that, depending on your use case, you may want to convert the big
+endian output of LodePNG to little endian with a for loop. This is certainly not
+always needed, many applications and libraries support big endian 16-bit colors
+anyway, but it means you cannot simply cast the unsigned char* buffer to an
+unsigned short* buffer on x86 CPUs.
+
+
+7. error values
+---------------
+
+All functions in LodePNG that return an error code, return 0 if everything went
+OK, or a non-zero code if there was an error.
+
+The meaning of the LodePNG error values can be retrieved with the function
+lodepng_error_text: given the numerical error code, it returns a description
+of the error in English as a string.
+
+Check the implementation of lodepng_error_text to see the meaning of each code.
+
+
+8. chunks and PNG editing
+-------------------------
+
+If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG
+editor that should follow the rules about handling of unknown chunks, or if your
+program is able to read other types of chunks than the ones handled by LodePNG,
+then that's possible with the chunk functions of LodePNG.
+
+A PNG chunk has the following layout:
+
+4 bytes length
+4 bytes type name
+length bytes data
+4 bytes CRC
+
+8.1. iterating through chunks
+-----------------------------
+
+If you have a buffer containing the PNG image data, then the first chunk (the
+IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the
+signature of the PNG and are not part of a chunk. But if you start at byte 8
+then you have a chunk, and can check the following things of it.
+
+NOTE: none of these functions check for memory buffer boundaries. To avoid
+exploits, always make sure the buffer contains all the data of the chunks.
+When using lodepng_chunk_next, make sure the returned value is within the
+allocated memory.
+
+unsigned lodepng_chunk_length(const unsigned char* chunk):
+
+Get the length of the chunk's data. The total chunk length is this length + 12.
+
+void lodepng_chunk_type(char type[5], const unsigned char* chunk):
+unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type):
+
+Get the type of the chunk or compare if it's a certain type
+
+unsigned char lodepng_chunk_critical(const unsigned char* chunk):
+unsigned char lodepng_chunk_private(const unsigned char* chunk):
+unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk):
+
+Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are).
+Check if the chunk is private (public chunks are part of the standard, private ones not).
+Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical
+chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your
+program doesn't handle that type of unknown chunk.
+
+unsigned char* lodepng_chunk_data(unsigned char* chunk):
+const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk):
+
+Get a pointer to the start of the data of the chunk.
+
+unsigned lodepng_chunk_check_crc(const unsigned char* chunk):
+void lodepng_chunk_generate_crc(unsigned char* chunk):
+
+Check if the crc is correct or generate a correct one.
+
+unsigned char* lodepng_chunk_next(unsigned char* chunk):
+const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk):
+
+Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these
+functions do no boundary checking of the allocated data whatsoever, so make sure there is enough
+data available in the buffer to be able to go to the next chunk.
+
+unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk):
+unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
+ const char* type, const unsigned char* data):
+
+These functions are used to create new chunks that are appended to the data in *out that has
+length *outsize. The append function appends an existing chunk to the new data. The create
+function creates a new chunk with the given parameters and appends it. Type is the 4-letter
+name of the chunk.
+
+8.2. chunks in info_png
+-----------------------
+
+The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3
+buffers (each with size) to contain 3 types of unknown chunks:
+the ones that come before the PLTE chunk, the ones that come between the PLTE
+and the IDAT chunks, and the ones that come after the IDAT chunks.
+It's necessary to make the distinction between these 3 cases because the PNG
+standard forces to keep the ordering of unknown chunks compared to the critical
+chunks, but does not force any other ordering rules.
+
+info_png.unknown_chunks_data[0] is the chunks before PLTE
+info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT
+info_png.unknown_chunks_data[2] is the chunks after IDAT
+
+The chunks in these 3 buffers can be iterated through and read by using the same
+way described in the previous subchapter.
+
+When using the decoder to decode a PNG, you can make it store all unknown chunks
+if you set the option settings.remember_unknown_chunks to 1. By default, this
+option is off (0).
+
+The encoder will always encode unknown chunks that are stored in the info_png.
+If you need it to add a particular chunk that isn't known by LodePNG, you can
+use lodepng_chunk_append or lodepng_chunk_create to the chunk data in
+info_png.unknown_chunks_data[x].
+
+Chunks that are known by LodePNG should not be added in that way. E.g. to make
+LodePNG add a bKGD chunk, set background_defined to true and add the correct
+parameters there instead.
+
+
+9. compiler support
+-------------------
+
+No libraries other than the current standard C library are needed to compile
+LodePNG. For the C++ version, only the standard C++ library is needed on top.
+Add the files lodepng.c(pp) and lodepng.h to your project, include
+lodepng.h where needed, and your program can read/write PNG files.
+
+It is compatible with C90 and up, and C++03 and up.
+
+If performance is important, use optimization when compiling! For both the
+encoder and decoder, this makes a large difference.
+
+Make sure that LodePNG is compiled with the same compiler of the same version
+and with the same settings as the rest of the program, or the interfaces with
+std::vectors and std::strings in C++ can be incompatible.
+
+CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets.
+
+*) gcc and g++
+
+LodePNG is developed in gcc so this compiler is natively supported. It gives no
+warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++
+version 4.7.1 on Linux, 32-bit and 64-bit.
+
+*) Clang
+
+Fully supported and warning-free.
+
+*) Mingw
+
+The Mingw compiler (a port of gcc for Windows) should be fully supported by
+LodePNG.
+
+*) Visual Studio and Visual C++ Express Edition
+
+LodePNG should be warning-free with warning level W4. Two warnings were disabled
+with pragmas though: warning 4244 about implicit conversions, and warning 4996
+where it wants to use a non-standard function fopen_s instead of the standard C
+fopen.
+
+Visual Studio may want "stdafx.h" files to be included in each source file and
+give an error "unexpected end of file while looking for precompiled header".
+This is not standard C++ and will not be added to the stock LodePNG. You can
+disable it for lodepng.cpp only by right clicking it, Properties, C/C++,
+Precompiled Headers, and set it to Not Using Precompiled Headers there.
+
+NOTE: Modern versions of VS should be fully supported, but old versions, e.g.
+VS6, are not guaranteed to work.
+
+*) Compilers on Macintosh
+
+LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for
+C and C++.
+
+*) Other Compilers
+
+If you encounter problems on any compilers, feel free to let me know and I may
+try to fix it if the compiler is modern and standards compliant.
+
+
+10. examples
+------------
+
+This decoder example shows the most basic usage of LodePNG. More complex
+examples can be found on the LodePNG website.
+
+10.1. decoder C++ example
+-------------------------
+
+#include "lodepng.h"
+#include <iostream>
+
+int main(int argc, char *argv[]) {
+ const char* filename = argc > 1 ? argv[1] : "test.png";
+
+ //load and decode
+ std::vector<unsigned char> image;
+ unsigned width, height;
+ unsigned error = lodepng::decode(image, width, height, filename);
+
+ //if there's an error, display it
+ if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+
+ //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
+}
+
+10.2. decoder C example
+-----------------------
+
+#include "lodepng.h"
+
+int main(int argc, char *argv[]) {
+ unsigned error;
+ unsigned char* image;
+ size_t width, height;
+ const char* filename = argc > 1 ? argv[1] : "test.png";
+
+ error = lodepng_decode32_file(&image, &width, &height, filename);
+
+ if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error));
+
+ / * use image here * /
+
+ free(image);
+ return 0;
+}
+
+11. state settings reference
+----------------------------
+
+A quick reference of some settings to set on the LodePNGState
+
+For decoding:
+
+state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums
+state.decoder.zlibsettings.custom_...: use custom inflate function
+state.decoder.ignore_crc: ignore CRC checksums
+state.decoder.ignore_critical: ignore unknown critical chunks
+state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors
+state.decoder.color_convert: convert internal PNG color to chosen one
+state.decoder.read_text_chunks: whether to read in text metadata chunks
+state.decoder.remember_unknown_chunks: whether to read in unknown chunks
+state.info_raw.colortype: desired color type for decoded image
+state.info_raw.bitdepth: desired bit depth for decoded image
+state.info_raw....: more color settings, see struct LodePNGColorMode
+state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo
+
+For encoding:
+
+state.encoder.zlibsettings.btype: disable compression by setting it to 0
+state.encoder.zlibsettings.use_lz77: use LZ77 in compression
+state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize
+state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match
+state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching
+state.encoder.zlibsettings.lazymatching: try one more LZ77 matching
+state.encoder.zlibsettings.custom_...: use custom deflate function
+state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png
+state.encoder.filter_palette_zero: PNG filter strategy for palette
+state.encoder.filter_strategy: PNG filter strategy to encode with
+state.encoder.force_palette: add palette even if not encoding to one
+state.encoder.add_id: add LodePNG identifier and version as a text chunk
+state.encoder.text_compression: use compressed text chunks for metadata
+state.info_raw.colortype: color type of raw input image you provide
+state.info_raw.bitdepth: bit depth of raw input image you provide
+state.info_raw: more color settings, see struct LodePNGColorMode
+state.info_png.color.colortype: desired color type if auto_convert is false
+state.info_png.color.bitdepth: desired bit depth if auto_convert is false
+state.info_png.color....: more color settings, see struct LodePNGColorMode
+state.info_png....: more PNG related settings, see struct LodePNGInfo
+
+
+12. changes
+-----------
+
+The version number of LodePNG is the date of the change given in the format
+yyyymmdd.
+
+Some changes aren't backwards compatible. Those are indicated with a (!)
+symbol.
+
+Not all changes are listed here, the commit history in github lists more:
+https://github.com/lvandeve/lodepng
+
+*) 06 mar 2020: simplified some of the dynamic memory allocations.
+*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
+ overflow checks.
+*) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables.
+*) 15 jun 2019: (!) auto_choose_color API changed (for bugfix: don't use palette
+ if gray ICC profile) and non-ICC LodePNGColorProfile renamed to
+ LodePNGColorStats.
+*) 30 dec 2018: code style changes only: removed newlines before opening braces.
+*) 10 sep 2018: added way to inspect metadata chunks without full decoding.
+*) 19 aug 2018: (!) fixed color mode bKGD is encoded with and made it use
+ palette index in case of palette.
+*) 10 aug 2018: (!) added support for gAMA, cHRM, sRGB and iCCP chunks. This
+ change is backwards compatible unless you relied on unknown_chunks for those.
+*) 11 jun 2018: less restrictive check for pixel size integer overflow
+*) 14 jan 2018: allow optionally ignoring a few more recoverable errors
+*) 17 sep 2017: fix memory leak for some encoder input error cases
+*) 27 nov 2016: grey+alpha auto color model detection bugfix
+*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort).
+*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within
+ the limits of pure C90).
+*) 08 dec 2015: Made load_file function return error if file can't be opened.
+*) 24 okt 2015: Bugfix with decoding to palette output.
+*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding.
+*) 24 aug 2014: Moved to github
+*) 23 aug 2014: Reduced needless memory usage of decoder.
+*) 28 jun 2014: Removed fix_png setting, always support palette OOB for
+ simplicity. Made ColorProfile public.
+*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization.
+*) 22 dec 2013: Power of two windowsize required for optimization.
+*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key.
+*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png).
+*) 11 mar 2013: (!) Bugfix with custom free. Changed from "my" to "lodepng_"
+ prefix for the custom allocators and made it possible with a new #define to
+ use custom ones in your project without needing to change lodepng's code.
+*) 28 jan 2013: Bugfix with color key.
+*) 27 okt 2012: Tweaks in text chunk keyword length error handling.
+*) 8 okt 2012: (!) Added new filter strategy (entropy) and new auto color mode.
+ (no palette). Better deflate tree encoding. New compression tweak settings.
+ Faster color conversions while decoding. Some internal cleanups.
+*) 23 sep 2012: Reduced warnings in Visual Studio a little bit.
+*) 1 sep 2012: (!) Removed #define's for giving custom (de)compression functions
+ and made it work with function pointers instead.
+*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc
+ and free functions and toggle #defines from compiler flags. Small fixes.
+*) 6 may 2012: (!) Made plugging in custom zlib/deflate functions more flexible.
+*) 22 apr 2012: (!) Made interface more consistent, renaming a lot. Removed
+ redundant C++ codec classes. Reduced amount of structs. Everything changed,
+ but it is cleaner now imho and functionality remains the same. Also fixed
+ several bugs and shrunk the implementation code. Made new samples.
+*) 6 nov 2011: (!) By default, the encoder now automatically chooses the best
+ PNG color model and bit depth, based on the amount and type of colors of the
+ raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color.
+*) 9 okt 2011: simpler hash chain implementation for the encoder.
+*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching.
+*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking.
+ A bug with the PNG filtertype heuristic was fixed, so that it chooses much
+ better ones (it's quite significant). A setting to do an experimental, slow,
+ brute force search for PNG filter types is added.
+*) 17 aug 2011: (!) changed some C zlib related function names.
+*) 16 aug 2011: made the code less wide (max 120 characters per line).
+*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors.
+*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled.
+*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman
+ to optimize long sequences of zeros.
+*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and
+ LodePNG_InfoColor_canHaveAlpha functions for convenience.
+*) 7 nov 2010: added LodePNG_error_text function to get error code description.
+*) 30 okt 2010: made decoding slightly faster
+*) 26 okt 2010: (!) changed some C function and struct names (more consistent).
+ Reorganized the documentation and the declaration order in the header.
+*) 08 aug 2010: only changed some comments and external samples.
+*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version.
+*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers.
+*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could
+ read by ignoring the problem but windows apps couldn't.
+*) 06 jun 2008: added more error checks for out of memory cases.
+*) 26 apr 2008: added a few more checks here and there to ensure more safety.
+*) 06 mar 2008: crash with encoding of strings fixed
+*) 02 feb 2008: support for international text chunks added (iTXt)
+*) 23 jan 2008: small cleanups, and #defines to divide code in sections
+*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor.
+*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder.
+*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added
+ Also various fixes, such as in the deflate and the padding bits code.
+*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved
+ filtering code of encoder.
+*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A
+ C++ wrapper around this provides an interface almost identical to before.
+ Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code
+ are together in these files but it works both for C and C++ compilers.
+*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks
+*) 30 aug 2007: bug fixed which makes this Borland C++ compatible
+*) 09 aug 2007: some VS2005 warnings removed again
+*) 21 jul 2007: deflate code placed in new namespace separate from zlib code
+*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images
+*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing
+ invalid std::vector element [0] fixed, and level 3 and 4 warnings removed
+*) 02 jun 2007: made the encoder add a tag with version by default
+*) 27 may 2007: zlib and png code separated (but still in the same file),
+ simple encoder/decoder functions added for more simple usage cases
+*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69),
+ moved some examples from here to lodepng_examples.cpp
+*) 12 may 2007: palette decoding bug fixed
+*) 24 apr 2007: changed the license from BSD to the zlib license
+*) 11 mar 2007: very simple addition: ability to encode bKGD chunks.
+*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding
+ palettized PNG images. Plus little interface change with palette and texts.
+*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes.
+ Fixed a bug where the end code of a block had length 0 in the Huffman tree.
+*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented
+ and supported by the encoder, resulting in smaller PNGs at the output.
+*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone.
+*) 24 jan 2007: gave encoder an error interface. Added color conversion from any
+ greyscale type to 8-bit greyscale with or without alpha.
+*) 21 jan 2007: (!) Totally changed the interface. It allows more color types
+ to convert to and is more uniform. See the manual for how it works now.
+*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days:
+ encode/decode custom tEXt chunks, separate classes for zlib & deflate, and
+ at last made the decoder give errors for incorrect Adler32 or Crc.
+*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel.
+*) 29 dec 2006: Added support for encoding images without alpha channel, and
+ cleaned out code as well as making certain parts faster.
+*) 28 dec 2006: Added "Settings" to the encoder.
+*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now.
+ Removed some code duplication in the decoder. Fixed little bug in an example.
+*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter.
+ Fixed a bug of the decoder with 16-bit per color.
+*) 15 okt 2006: Changed documentation structure
+*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the
+ given image buffer, however for now it's not compressed.
+*) 08 sep 2006: (!) Changed to interface with a Decoder class
+*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different
+ way. Renamed decodePNG to decodePNGGeneric.
+*) 29 jul 2006: (!) Changed the interface: image info is now returned as a
+ struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy.
+*) 28 jul 2006: Cleaned the code and added new error checks.
+ Corrected terminology "deflate" into "inflate".
+*) 23 jun 2006: Added SDL example in the documentation in the header, this
+ example allows easy debugging by displaying the PNG and its transparency.
+*) 22 jun 2006: (!) Changed way to obtain error value. Added
+ loadFile function for convenience. Made decodePNG32 faster.
+*) 21 jun 2006: (!) Changed type of info vector to unsigned.
+ Changed position of palette in info vector. Fixed an important bug that
+ happened on PNGs with an uncompressed block.
+*) 16 jun 2006: Internally changed unsigned into unsigned where
+ needed, and performed some optimizations.
+*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them
+ in LodePNG namespace. Changed the order of the parameters. Rewrote the
+ documentation in the header. Renamed files to lodepng.cpp and lodepng.h
+*) 22 apr 2006: Optimized and improved some code
+*) 07 sep 2005: (!) Changed to std::vector interface
+*) 12 aug 2005: Initial release (C++, decoder only)
+
+
+13. contact information
+-----------------------
+
+Feel free to contact me with suggestions, problems, comments, ... concerning
+LodePNG. If you encounter a PNG image that doesn't work properly with this
+decoder, feel free to send it and I'll use it to find and fix the problem.
+
+My email address is (puzzle the account and domain together with an @ symbol):
+Domain: gmail dot com.
+Account: lode dot vandevenne.
+
+
+Copyright (c) 2005-2020 Lode Vandevenne
+*/
diff --git a/src/matfx.cpp b/src/matfx.cpp
new file mode 100644
index 0000000..c143fc1
--- /dev/null
+++ b/src/matfx.cpp
@@ -0,0 +1,641 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwanim.h"
+#include "rwplugins.h"
+#include "ps2/rwps2.h"
+#include "ps2/rwps2plg.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+#include "gl/rwwdgl.h"
+#include "gl/rwgl3.h"
+#include "gl/rwgl3plg.h"
+
+#define PLUGIN_ID ID_MATFX
+
+namespace rw {
+
+bool32 MatFX::envMapFlipU;
+bool32 MatFX::envMapApplyLight;
+bool32 MatFX::envMapUseMatColor;
+RGBA MatFX::envMapColor = { 255, 255, 255, 255 };
+
+
+// Atomic
+
+static void*
+createAtomicMatFX(void *object, int32 offset, int32)
+{
+ *PLUGINOFFSET(int32, object, offset) = 0;
+ return object;
+}
+
+static void*
+copyAtomicMatFX(void *dst, void *src, int32 offset, int32)
+{
+ // don't call seteffects, it will override the pipeline
+ if(*PLUGINOFFSET(int32, src, offset))
+ *PLUGINOFFSET(int32, dst, offset) = 1;
+ return dst;
+}
+
+static Stream*
+readAtomicMatFX(Stream *stream, int32, void *object, int32, int32)
+{
+ if(stream->readI32())
+ MatFX::enableEffects((Atomic*)object);
+ return stream;
+}
+
+static Stream*
+writeAtomicMatFX(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ stream->writeI32(*PLUGINOFFSET(int32, object, offset));
+ return stream;
+}
+
+static int32
+getSizeAtomicMatFX(void *object, int32 offset, int32)
+{
+ int32 flag = *PLUGINOFFSET(int32, object, offset);
+ // TODO: not sure which version
+ return flag || rw::version < 0x34000 ? 4 : 0;
+}
+
+// Material
+
+MatFXGlobals matFXGlobals = { 0, 0, { nil }, nil };
+
+// TODO: Frames and Matrices?
+static void
+clearMatFX(MatFX *matfx)
+{
+ for(int i = 0; i < 2; i++)
+ switch(matfx->fx[i].type){
+ case MatFX::BUMPMAP:
+ if(matfx->fx[i].bump.bumpedTex)
+ matfx->fx[i].bump.bumpedTex->destroy();
+ if(matfx->fx[i].bump.tex)
+ matfx->fx[i].bump.tex->destroy();
+ break;
+
+ case MatFX::ENVMAP:
+ if(matfx->fx[i].env.tex)
+ matfx->fx[i].env.tex->destroy();
+ break;
+
+ case MatFX::DUAL:
+ if(matfx->fx[i].dual.tex)
+ matfx->fx[i].dual.tex->destroy();
+ break;
+ }
+ memset(matfx, 0, sizeof(MatFX));
+}
+
+void
+MatFX::setEffects(Material *mat, uint32 type)
+{
+ MatFX *matfx;
+
+ matfx = MatFX::get(mat);
+ if(matfx == nil){
+ matfx = rwNewT(MatFX, 1, MEMDUR_EVENT | ID_MATFX);
+ memset(matfx, 0, sizeof(MatFX));
+ *PLUGINOFFSET(MatFX*, mat, matFXGlobals.materialOffset) = matfx;
+ }
+
+ if(matfx->type != 0 && matfx->type != type)
+ clearMatFX(matfx);
+ matfx->type = type;
+ switch(type){
+ case BUMPMAP:
+ case ENVMAP:
+ case DUAL:
+ case UVTRANSFORM:
+ matfx->fx[0].type = type;
+ matfx->fx[1].type = NOTHING;
+ break;
+
+ case BUMPENVMAP:
+ matfx->fx[0].type = BUMPMAP;
+ matfx->fx[1].type = ENVMAP;
+ break;
+
+ case DUALUVTRANSFORM:
+ matfx->fx[0].type = UVTRANSFORM;
+ matfx->fx[1].type = DUAL;
+ break;
+ }
+}
+
+uint32
+MatFX::getEffects(const Material *m)
+{
+ MatFX *fx = *PLUGINOFFSET(MatFX*, m, matFXGlobals.materialOffset);
+ if(fx)
+ return fx->type;
+ return 0;
+}
+
+MatFX*
+MatFX::get(const Material *m)
+{
+ return *PLUGINOFFSET(MatFX*, m, matFXGlobals.materialOffset);
+}
+
+int32
+MatFX::getEffectIndex(uint32 type)
+{
+ for(int i = 0; i < 2; i++)
+ if(this->fx[i].type == type)
+ return i;
+ return -1;
+}
+
+void
+MatFX::setBumpTexture(Texture *t)
+{
+ int32 i = this->getEffectIndex(BUMPMAP);
+ if(i >= 0){
+ if(this->fx[i].bump.tex)
+ this->fx[i].bump.tex->destroy();
+ this->fx[i].bump.tex = t;
+ if(t)
+ t->addRef();
+ }
+}
+
+void
+MatFX::setBumpCoefficient(float32 coef)
+{
+ int32 i = this->getEffectIndex(BUMPMAP);
+ if(i >= 0)
+ this->fx[i].bump.coefficient = coef;
+}
+
+Texture*
+MatFX::getBumpTexture(void)
+{
+ int32 i = this->getEffectIndex(BUMPMAP);
+ if(i >= 0)
+ return this->fx[i].bump.tex;
+ return nil;
+}
+
+float32
+MatFX::getBumpCoefficient(void)
+{
+ int32 i = this->getEffectIndex(BUMPMAP);
+ if(i >= 0)
+ return this->fx[i].bump.coefficient;
+ return 0.0f;
+}
+
+void
+MatFX::setEnvTexture(Texture *t)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0){
+ if(this->fx[i].env.tex)
+ this->fx[i].env.tex->destroy();
+ this->fx[i].env.tex = t;
+ if(t)
+ t->addRef();
+ }
+}
+
+void
+MatFX::setEnvFrame(Frame *f)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0)
+ this->fx[i].env.frame = f;
+}
+
+void
+MatFX::setEnvCoefficient(float32 coef)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0)
+ this->fx[i].env.coefficient = coef;
+}
+
+void
+MatFX::setEnvFBAlpha(bool32 useFBAlpha)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0)
+ this->fx[i].env.fbAlpha = useFBAlpha;
+}
+
+Texture*
+MatFX::getEnvTexture(void)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0)
+ return this->fx[i].env.tex;
+ return nil;
+}
+
+Frame*
+MatFX::getEnvFrame(void)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0)
+ return this->fx[i].env.frame;
+ return nil;
+}
+
+float32
+MatFX::getEnvCoefficient(void)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0)
+ return this->fx[i].env.coefficient;
+ return 0.0f;
+}
+
+bool32
+MatFX::getEnvFBAlpha(void)
+{
+ int32 i = this->getEffectIndex(ENVMAP);
+ if(i >= 0)
+ return this->fx[i].env.fbAlpha;
+ return 0;
+}
+
+
+void
+MatFX::setDualTexture(Texture *t)
+{
+ int32 i = this->getEffectIndex(DUAL);
+ if(i >= 0){
+ if(this->fx[i].dual.tex)
+ this->fx[i].dual.tex->destroy();
+ this->fx[i].dual.tex = t;
+ if(t)
+ t->addRef();
+ }
+}
+
+void
+MatFX::setDualSrcBlend(int32 blend)
+{
+ int32 i = this->getEffectIndex(DUAL);
+ if(i >= 0)
+ this->fx[i].dual.srcBlend = blend;
+}
+
+void
+MatFX::setDualDestBlend(int32 blend)
+{
+ int32 i = this->getEffectIndex(DUAL);
+ if(i >= 0)
+ this->fx[i].dual.dstBlend = blend;
+}
+
+Texture*
+MatFX::getDualTexture(void)
+{
+ int32 i = this->getEffectIndex(DUAL);
+ if(i >= 0)
+ return this->fx[i].dual.tex;
+ return nil;
+}
+
+int32
+MatFX::getDualSrcBlend(void)
+{
+ int32 i = this->getEffectIndex(DUAL);
+ if(i >= 0)
+ return this->fx[i].dual.srcBlend;
+ return 0;
+}
+
+int32
+MatFX::getDualDestBlend(void)
+{
+ int32 i = this->getEffectIndex(DUAL);
+ if(i >= 0)
+ return this->fx[i].dual.dstBlend;
+ return 0;
+}
+
+void
+MatFX::setUVTransformMatrices(Matrix *base, Matrix *dual)
+{
+ int32 i = this->getEffectIndex(UVTRANSFORM);
+ if(i >= 0){
+ this->fx[i].uvtransform.baseTransform = base;
+ this->fx[i].uvtransform.dualTransform = dual;
+ }
+}
+
+void
+MatFX::getUVTransformMatrices(Matrix **base, Matrix **dual)
+{
+ int32 i = this->getEffectIndex(UVTRANSFORM);
+ if(i >= 0){
+ if(base) *base = this->fx[i].uvtransform.baseTransform;
+ if(dual) *dual = this->fx[i].uvtransform.dualTransform;
+ return;
+ }
+ if(base) *base = nil;
+ if(dual) *dual = nil;
+}
+
+static void*
+createMaterialMatFX(void *object, int32 offset, int32)
+{
+ *PLUGINOFFSET(MatFX*, object, offset) = nil;
+ return object;
+}
+
+static void*
+destroyMaterialMatFX(void *object, int32 offset, int32)
+{
+ MatFX *matfx = *PLUGINOFFSET(MatFX*, object, offset);
+ if(matfx){
+ clearMatFX(matfx);
+ rwFree(matfx);
+ }
+ return object;
+}
+
+static void*
+copyMaterialMatFX(void *dst, void *src, int32 offset, int32)
+{
+ MatFX *srcfx = *PLUGINOFFSET(MatFX*, src, offset);
+ if(srcfx == nil)
+ return dst;
+ MatFX *dstfx = rwNewT(MatFX, 1, MEMDUR_EVENT | ID_MATFX);
+ *PLUGINOFFSET(MatFX*, dst, offset) = dstfx;
+ memcpy(dstfx, srcfx, sizeof(MatFX));
+ for(int i = 0; i < 2; i++)
+ switch(dstfx->fx[i].type){
+ case MatFX::BUMPMAP:
+ if(dstfx->fx[i].bump.bumpedTex)
+ dstfx->fx[i].bump.bumpedTex->addRef();
+ if(dstfx->fx[i].bump.tex)
+ dstfx->fx[i].bump.tex->addRef();
+ break;
+
+ case MatFX::ENVMAP:
+ if(dstfx->fx[i].env.tex)
+ dstfx->fx[i].env.tex->addRef();
+ break;
+
+ case MatFX::DUAL:
+ if(dstfx->fx[i].dual.tex)
+ dstfx->fx[i].dual.tex->addRef();
+ break;
+ }
+ return dst;
+}
+
+static Stream*
+readMaterialMatFX(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ Material *mat;
+ MatFX *matfx;
+ Texture *tex, *bumpedTex;
+ float coefficient;
+ int32 fbAlpha;
+ int32 srcBlend, dstBlend;
+ int32 idx;
+
+ mat = (Material*)object;
+ MatFX::setEffects(mat, stream->readU32());
+ matfx = MatFX::get(mat);
+
+ for(int i = 0; i < 2; i++){
+ uint32 type = stream->readU32();
+ switch(type){
+ case MatFX::BUMPMAP:
+ coefficient = stream->readF32();
+ bumpedTex = tex = nil;
+ if(stream->readI32()){
+ if(!findChunk(stream, ID_TEXTURE,
+ nil, nil)){
+ RWERROR((ERR_CHUNK, "TEXTURE"));
+ return nil;
+ }
+ bumpedTex = Texture::streamRead(stream);
+ }
+ if(stream->readI32()){
+ if(!findChunk(stream, ID_TEXTURE,
+ nil, nil)){
+ RWERROR((ERR_CHUNK, "TEXTURE"));
+ return nil;
+ }
+ tex = Texture::streamRead(stream);
+ }
+ idx = matfx->getEffectIndex(type);
+ assert(idx >= 0);
+ matfx->fx[idx].bump.bumpedTex = bumpedTex;
+ matfx->fx[idx].bump.tex = tex;
+ matfx->fx[idx].bump.coefficient = coefficient;
+ break;
+
+ case MatFX::ENVMAP:
+ coefficient = stream->readF32();
+ fbAlpha = stream->readI32();
+ tex = nil;
+ if(stream->readI32()){
+ if(!findChunk(stream, ID_TEXTURE,
+ nil, nil)){
+ RWERROR((ERR_CHUNK, "TEXTURE"));
+ return nil;
+ }
+ tex = Texture::streamRead(stream);
+ }
+ idx = matfx->getEffectIndex(type);
+ assert(idx >= 0);
+ matfx->fx[idx].env.tex = tex;
+ matfx->fx[idx].env.fbAlpha = fbAlpha;
+ matfx->fx[idx].env.coefficient = coefficient;
+ break;
+
+ case MatFX::DUAL:
+ srcBlend = stream->readI32();
+ dstBlend = stream->readI32();
+ tex = nil;
+ if(stream->readI32()){
+ if(!findChunk(stream, ID_TEXTURE,
+ nil, nil)){
+ RWERROR((ERR_CHUNK, "TEXTURE"));
+ return nil;
+ }
+ tex = Texture::streamRead(stream);
+ }
+ idx = matfx->getEffectIndex(type);
+ assert(idx >= 0);
+ matfx->fx[idx].dual.tex = tex;
+ matfx->fx[idx].dual.srcBlend = srcBlend;
+ matfx->fx[idx].dual.dstBlend = dstBlend;
+ break;
+ }
+ }
+ return stream;
+}
+
+static Stream*
+writeMaterialMatFX(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ MatFX *matfx = *PLUGINOFFSET(MatFX*, object, offset);
+
+ stream->writeU32(matfx->type);
+ for(int i = 0; i < 2; i++){
+ stream->writeU32(matfx->fx[i].type);
+ switch(matfx->fx[i].type){
+ case MatFX::BUMPMAP:
+ stream->writeF32(matfx->fx[i].bump.coefficient);
+ stream->writeI32(matfx->fx[i].bump.bumpedTex != nil);
+ if(matfx->fx[i].bump.bumpedTex)
+ matfx->fx[i].bump.bumpedTex->streamWrite(stream);
+ stream->writeI32(matfx->fx[i].bump.tex != nil);
+ if(matfx->fx[i].bump.tex)
+ matfx->fx[i].bump.tex->streamWrite(stream);
+ break;
+
+ case MatFX::ENVMAP:
+ stream->writeF32(matfx->fx[i].env.coefficient);
+ stream->writeI32(matfx->fx[i].env.fbAlpha);
+ stream->writeI32(matfx->fx[i].env.tex != nil);
+ if(matfx->fx[i].env.tex)
+ matfx->fx[i].env.tex->streamWrite(stream);
+ break;
+
+ case MatFX::DUAL:
+ stream->writeI32(matfx->fx[i].dual.srcBlend);
+ stream->writeI32(matfx->fx[i].dual.dstBlend);
+ stream->writeI32(matfx->fx[i].dual.tex != nil);
+ if(matfx->fx[i].dual.tex)
+ matfx->fx[i].dual.tex->streamWrite(stream);
+ break;
+ }
+ }
+ return stream;
+}
+
+static int32
+getSizeMaterialMatFX(void *object, int32 offset, int32)
+{
+ MatFX *matfx = *PLUGINOFFSET(MatFX*, object, offset);
+ if(matfx == nil)
+ return -1;
+ int32 size = 4 + 4 + 4;
+
+ for(int i = 0; i < 2; i++){
+ switch(matfx->fx[i].type){
+ case MatFX::BUMPMAP:
+ size += 4 + 4 + 4;
+ if(matfx->fx[i].bump.bumpedTex)
+ size += 12 +
+ matfx->fx[i].bump.bumpedTex->streamGetSize();
+ if(matfx->fx[i].bump.tex)
+ size += 12 +
+ matfx->fx[i].bump.tex->streamGetSize();
+ break;
+
+ case MatFX::ENVMAP:
+ size += 4 + 4 + 4;
+ if(matfx->fx[i].env.tex)
+ size += 12 +
+ matfx->fx[i].env.tex->streamGetSize();
+ break;
+
+ case MatFX::DUAL:
+ size += 4 + 4 + 4;
+ if(matfx->fx[i].dual.tex)
+ size += 12 +
+ matfx->fx[i].dual.tex->streamGetSize();
+ break;
+ }
+ }
+ return size;
+}
+
+void
+MatFX::enableEffects(Atomic *atomic)
+{
+ *PLUGINOFFSET(int32, atomic, matFXGlobals.atomicOffset) = 1;
+ atomic->pipeline = matFXGlobals.pipelines[rw::platform];
+}
+
+// This prevents setting the pipeline on clone
+void
+MatFX::disableEffects(Atomic *atomic)
+{
+ *PLUGINOFFSET(int32, atomic, matFXGlobals.atomicOffset) = 0;
+}
+
+bool32
+MatFX::getEffects(Atomic *atomic)
+{
+ return *PLUGINOFFSET(int32, atomic, matFXGlobals.atomicOffset);
+}
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+ // init dummy pipelines
+ matFXGlobals.dummypipe = ObjPipeline::create();
+ matFXGlobals.dummypipe->pluginID = 0; //ID_MATFX;
+ matFXGlobals.dummypipe->pluginData = 0;
+ for(uint i = 0; i < nelem(matFXGlobals.pipelines); i++)
+ matFXGlobals.pipelines[i] = matFXGlobals.dummypipe;
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+ for(uint i = 0; i < nelem(matFXGlobals.pipelines); i++)
+ if(matFXGlobals.pipelines[i] == matFXGlobals.dummypipe)
+ matFXGlobals.pipelines[i] = nil;
+ matFXGlobals.dummypipe->destroy();
+ matFXGlobals.dummypipe = nil;
+ return o;
+}
+
+void
+registerMatFXPlugin(void)
+{
+ Driver::registerPlugin(PLATFORM_NULL, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+ ps2::initMatFX();
+ xbox::initMatFX();
+ d3d8::initMatFX();
+ d3d9::initMatFX();
+ wdgl::initMatFX();
+ gl3::initMatFX();
+
+ matFXGlobals.atomicOffset =
+ Atomic::registerPlugin(sizeof(int32), ID_MATFX,
+ createAtomicMatFX, nil, copyAtomicMatFX);
+ Atomic::registerPluginStream(ID_MATFX,
+ readAtomicMatFX,
+ writeAtomicMatFX,
+ getSizeAtomicMatFX);
+
+ matFXGlobals.materialOffset =
+ Material::registerPlugin(sizeof(MatFX*), ID_MATFX,
+ createMaterialMatFX, destroyMaterialMatFX,
+ copyMaterialMatFX);
+ Material::registerPluginStream(ID_MATFX,
+ readMaterialMatFX,
+ writeMaterialMatFX,
+ getSizeMaterialMatFX);
+}
+
+}
diff --git a/src/pipeline.cpp b/src/pipeline.cpp
new file mode 100644
index 0000000..6284f68
--- /dev/null
+++ b/src/pipeline.cpp
@@ -0,0 +1,198 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define COLOR_ARGB(a,r,g,b) \
+ ((uint32)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
+
+namespace rw {
+
+static void nothing(ObjPipeline *, Atomic*) {}
+
+void
+ObjPipeline::init(uint32 platform)
+{
+ Pipeline::init(platform);
+ this->impl.instance = nothing;
+ this->impl.uninstance = nothing;
+ this->impl.render = nothing;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init(PLATFORM_NULL);
+ return pipe;
+}
+
+void
+ObjPipeline::destroy(void)
+{
+ rwFree(this);
+}
+
+// helper functions
+
+void
+findMinVertAndNumVertices(uint16 *indices, uint32 numIndices, uint32 *minVert, int32 *numVertices)
+{
+ uint32 min = 0xFFFFFFFF;
+ uint32 max = 0;
+ while(numIndices--){
+ if(*indices < min)
+ min = *indices;
+ if(*indices > max)
+ max = *indices;
+ indices++;
+ }
+ uint32 num = max - min + 1;
+ // if mesh is empty, this can happen
+ if(min > max){
+ min = 0;
+ num = 0;
+ }
+ if(minVert)
+ *minVert = min;
+ if(numVertices)
+ *numVertices = num;
+}
+
+void
+instV4d(int type, uint8 *dst, V4d *src, uint32 numVertices, uint32 stride)
+{
+ if(type == VERT_FLOAT4)
+ for(uint32 i = 0; i < numVertices; i++){
+ memcpy(dst, src, 16);
+ dst += stride;
+ src++;
+ }
+ else
+ assert(0 && "unsupported instV4d type");
+}
+
+void
+instV3d(int type, uint8 *dst, V3d *src, uint32 numVertices, uint32 stride)
+{
+ if(type == VERT_FLOAT3)
+ for(uint32 i = 0; i < numVertices; i++){
+ memcpy(dst, src, 12);
+ dst += stride;
+ src++;
+ }
+ else if(type == VERT_COMPNORM)
+ for(uint32 i = 0; i < numVertices; i++){
+ uint32 n = ((((uint32)(src->z * 511.0f)) & 0x3ff) << 22) |
+ ((((uint32)(src->y * 1023.0f)) & 0x7ff) << 11) |
+ ((((uint32)(src->x * 1023.0f)) & 0x7ff) << 0);
+ *(uint32*)dst = n;
+ dst += stride;
+ src++;
+ }
+ else
+ assert(0 && "unsupported instV3d type");
+}
+
+void
+uninstV3d(int type, V3d *dst, uint8 *src, uint32 numVertices, uint32 stride)
+{
+ if(type == VERT_FLOAT3)
+ for(uint32 i = 0; i < numVertices; i++){
+ memcpy(dst, src, 12);
+ src += stride;
+ dst++;
+ }
+ else if(type == VERT_COMPNORM)
+ for(uint32 i = 0; i < numVertices; i++){
+ uint32 n = *(uint32*)src;
+ int32 normal[3];
+ normal[0] = n & 0x7FF;
+ normal[1] = (n >> 11) & 0x7FF;
+ normal[2] = (n >> 22) & 0x3FF;
+ // sign extend
+ if(normal[0] & 0x400) normal[0] |= ~0x7FF;
+ if(normal[1] & 0x400) normal[1] |= ~0x7FF;
+ if(normal[2] & 0x200) normal[2] |= ~0x3FF;
+ dst->x = normal[0] / 1023.0f;
+ dst->y = normal[1] / 1023.0f;
+ dst->z = normal[2] / 511.0f;
+ src += stride;
+ dst++;
+ }
+ else
+ assert(0 && "unsupported uninstV3d type");
+}
+
+void
+instTexCoords(int type, uint8 *dst, TexCoords *src, uint32 numVertices, uint32 stride)
+{
+ assert(type == VERT_FLOAT2);
+ for(uint32 i = 0; i < numVertices; i++){
+ memcpy(dst, src, 8);
+ dst += stride;
+ src++;
+ }
+}
+
+void
+uninstTexCoords(int type, TexCoords *dst, uint8 *src, uint32 numVertices, uint32 stride)
+{
+ assert(type == VERT_FLOAT2);
+ for(uint32 i = 0; i < numVertices; i++){
+ memcpy(dst, src, 8);
+ src += stride;
+ dst++;
+ }
+}
+
+bool32
+instColor(int type, uint8 *dst, RGBA *src, uint32 numVertices, uint32 stride)
+{
+ uint8 alpha = 0xFF;
+ if(type == VERT_ARGB){
+ for(uint32 i = 0; i < numVertices; i++){
+ dst[0] = src->blue;
+ dst[1] = src->green;
+ dst[2] = src->red;
+ dst[3] = src->alpha;
+ alpha &= src->alpha;
+ dst += stride;
+ src++;
+ }
+ }else if(type == VERT_RGBA){
+ for(uint32 i = 0; i < numVertices; i++){
+ dst[0] = src->red;
+ dst[1] = src->green;
+ dst[2] = src->blue;
+ dst[3] = src->alpha;
+ alpha &= src->alpha;
+ dst += stride;
+ src++;
+ }
+ }else
+ assert(0 && "unsupported color type");
+ return alpha != 0xFF;
+}
+
+void
+uninstColor(int type, RGBA *dst, uint8 *src, uint32 numVertices, uint32 stride)
+{
+ assert(type == VERT_ARGB);
+ for(uint32 i = 0; i < numVertices; i++){
+ dst->red = src[2];
+ dst->green = src[1];
+ dst->blue = src[0];
+ dst->alpha = src[3];
+ src += stride;
+ dst++;
+ }
+}
+
+}
diff --git a/src/plg.cpp b/src/plg.cpp
new file mode 100644
index 0000000..358a67a
--- /dev/null
+++ b/src/plg.cpp
@@ -0,0 +1,246 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+namespace rw {
+
+static void *defCtor(void *object, int32, int32) { return object; }
+static void *defDtor(void *object, int32, int32) { return object; }
+static void *defCopy(void *dst, void*, int32, int32) { return dst; }
+
+static LinkList allPlugins;
+
+#define PLG(lnk) LLLinkGetData(lnk, Plugin, inParentList)
+
+void
+PluginList::open(void)
+{
+ allPlugins.init();
+}
+
+void
+PluginList::close(void)
+{
+ PluginList *l;
+ Plugin *p;
+ FORLIST(lnk, allPlugins){
+ p = LLLinkGetData(lnk, Plugin, inGlobalList);
+ l = p->parentList;
+ p->inParentList.remove();
+ p->inGlobalList.remove();
+ rwFree(p);
+ if(l->plugins.isEmpty())
+ l->size = l->defaultSize;
+ }
+ assert(allPlugins.isEmpty());
+}
+
+void
+PluginList::construct(void *object)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ p->constructor(object, p->offset, p->size);
+ }
+}
+
+void
+PluginList::destruct(void *object)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ p->destructor(object, p->offset, p->size);
+ }
+}
+
+void
+PluginList::copy(void *dst, void *src)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ p->copy(dst, src, p->offset, p->size);
+ }
+}
+
+bool
+PluginList::streamRead(Stream *stream, void *object)
+{
+ int32 length;
+ ChunkHeaderInfo header;
+ if(!findChunk(stream, ID_EXTENSION, (uint32*)&length, nil))
+ return false;
+ while(length > 0){
+ if(!readChunkHeaderInfo(stream, &header))
+ return false;
+ length -= 12;
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->id == header.type && p->read){
+ p->read(stream, header.length,
+ object, p->offset, p->size);
+ goto cont;
+ }
+ }
+ stream->seek(header.length);
+cont:
+ length -= header.length;
+ }
+
+ // now the always callbacks
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->alwaysCallback)
+ p->alwaysCallback(object, p->offset, p->size);
+ }
+ return true;
+}
+
+void
+PluginList::streamWrite(Stream *stream, void *object)
+{
+ int size = this->streamGetSize(object);
+ writeChunkHeader(stream, ID_EXTENSION, size);
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->getSize == nil ||
+ (size = p->getSize(object, p->offset, p->size)) <= 0)
+ continue;
+ writeChunkHeader(stream, p->id, size);
+ p->write(stream, size, object, p->offset, p->size);
+ }
+}
+
+int
+PluginList::streamGetSize(void *object)
+{
+ int32 size = 0;
+ int32 plgsize;
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->getSize &&
+ (plgsize = p->getSize(object, p->offset, p->size)) > 0)
+ size += 12 + plgsize;
+ }
+ return size;
+}
+
+void
+PluginList::streamSkip(Stream *stream)
+{
+ int32 length;
+ ChunkHeaderInfo header;
+ if(!findChunk(stream, ID_EXTENSION, (uint32*)&length, nil))
+ return;
+ while(length > 0){
+ if(!readChunkHeaderInfo(stream, &header))
+ return;
+ stream->seek(header.length);
+ length -= 12 + header.length;
+ }
+}
+
+void
+PluginList::assertRights(void *object, uint32 pluginID, uint32 data)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->id == pluginID){
+ if(p->rightsCallback)
+ p->rightsCallback(object,
+ p->offset, p->size, data);
+ return;
+ }
+ }
+}
+
+
+int32
+PluginList::registerPlugin(int32 size, uint32 id,
+ Constructor ctor, Destructor dtor, CopyConstructor copy)
+{
+ Plugin *p = (Plugin*)rwMalloc(sizeof(Plugin), MEMDUR_GLOBAL);
+ p->offset = this->size;
+ this->size += size;
+ int32 round = sizeof(void*)-1;
+ this->size = (this->size + round)&~round;
+
+ p->size = size;
+ p->id = id;
+ p->constructor = ctor ? ctor : defCtor;
+ p->destructor = dtor ? dtor : defDtor;
+ p->copy = copy ? copy : defCopy;
+ p->read = nil;
+ p->write = nil;
+ p->getSize = nil;
+ p->rightsCallback = nil;
+ p->alwaysCallback = nil;
+ p->parentList = this;
+ this->plugins.append(&p->inParentList);
+ allPlugins.append(&p->inGlobalList);
+ return p->offset;
+}
+
+int32
+PluginList::registerStream(uint32 id,
+ StreamRead read, StreamWrite write, StreamGetSize getSize)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->id == id){
+ p->read = read;
+ p->write = write;
+ p->getSize = getSize;
+ return p->offset;
+ }
+ }
+ return -1;
+}
+
+int32
+PluginList::setStreamRightsCallback(uint32 id, RightsCallback cb)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->id == id){
+ p->rightsCallback = cb;
+ return p->offset;
+ }
+ }
+ return -1;
+}
+
+int32
+PluginList::setStreamAlwaysCallback(uint32 id, AlwaysCallback cb)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->id == id){
+ p->alwaysCallback = cb;
+ return p->offset;
+ }
+ }
+ return -1;
+}
+
+int32
+PluginList::getPluginOffset(uint32 id)
+{
+ FORLIST(lnk, this->plugins){
+ Plugin *p = PLG(lnk);
+ if(p->id == id)
+ return p->offset;
+ }
+ return -1;
+}
+
+}
diff --git a/src/png.cpp b/src/png.cpp
new file mode 100644
index 0000000..45aa102
--- /dev/null
+++ b/src/png.cpp
@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#include "lodepng/lodepng.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+
+
+Image*
+readPNG(const char *filename)
+{
+ Image *image = nil;
+ uint32 length;
+ uint8 *data = getFileContents(filename, &length);
+ assert(data != nil);
+
+ LodePNGState state;
+ lodepng_state_init(&state);
+ uint8 *raw = nil;
+ uint32 w, h;
+
+ // First try: decode without conversion to see if we understand the format
+ state.decoder.color_convert = 0;
+ uint32 error = lodepng_decode(&raw, &w, &h, &state, data, length);
+ if(error){
+ RWERROR((ERR_GENERAL, lodepng_error_text(error)));
+ return nil;
+ }
+
+ if(state.info_raw.bitdepth == 4 && state.info_raw.colortype == LCT_PALETTE){
+ image = Image::create(w, h, 4);
+ image->allocate();
+ memcpy(image->palette, state.info_raw.palette, state.info_raw.palettesize*4);
+ expandPal4_BE(image->pixels, image->stride, raw, w/2, w, h);
+ }else if(state.info_raw.bitdepth == 8){
+ switch(state.info_raw.colortype){
+ case LCT_PALETTE:
+ image = Image::create(w, h, state.info_raw.palettesize <= 16 ? 4 : 8);
+ image->allocate();
+ memcpy(image->palette, state.info_raw.palette, state.info_raw.palettesize*4);
+ memcpy(image->pixels, raw, w*h);
+ break;
+ case LCT_RGB:
+ image = Image::create(w, h, 24);
+ image->allocate();
+ memcpy(image->pixels, raw, w*h*3);
+ break;
+ default:
+ // Second try: just load as 32 bit
+ free(raw);
+ lodepng_state_init(&state);
+ error = lodepng_decode(&raw, &w, &h, &state, data, length);
+ if(error){
+ RWERROR((ERR_GENERAL, lodepng_error_text(error)));
+ return nil;
+ }
+ // fall through
+ case LCT_RGBA:
+ image = Image::create(w, h, 32);
+ image->allocate();
+ memcpy(image->pixels, raw, w*h*4);
+ break;
+ }
+ }
+
+ free(raw); // TODO: maybe override lodepng allocator
+
+ return image;
+}
+
+void
+writePNG(Image *image, const char *filename)
+{
+ int32 i;
+ StreamFile file;
+ if(!file.open(filename, "wb")){
+ RWERROR((ERR_FILE, filename));
+ return;
+ }
+
+ size_t rawsize;
+ uint8 *raw = nil;
+ uint8 *pixels;
+ LodePNGState state;
+ lodepng_state_init(&state);
+
+ pixels = image->pixels;
+ switch(image->depth){
+ case 4:
+ state.info_raw.bitdepth = 4;
+ state.info_raw.colortype = LCT_PALETTE;
+ state.info_png.color.bitdepth = 4;
+ state.info_png.color.colortype = LCT_PALETTE;
+ state.encoder.auto_convert = 0;
+ for(i = 0; i < (1<<image->depth); i++){
+ uint8 *col = &image->palette[i*4];
+ lodepng_palette_add(&state.info_png.color, col[0], col[1], col[2], col[3]);
+ lodepng_palette_add(&state.info_raw, col[0], col[1], col[2], col[3]);
+ }
+ pixels = rwNewT(uint8, image->width/2*image->height, ID_IMAGE | MEMDUR_FUNCTION);
+ compressPal4_BE(pixels, image->width/2, image->pixels, image->width, image->width, image->height);
+ break;
+ case 8:
+ state.info_raw.colortype = LCT_PALETTE;
+ state.info_png.color.colortype = LCT_PALETTE;
+ state.encoder.auto_convert = 0;
+ for(i = 0; i < (1<<image->depth); i++){
+ uint8 *col = &image->palette[i*4];
+ lodepng_palette_add(&state.info_png.color, col[0], col[1], col[2], col[3]);
+ lodepng_palette_add(&state.info_raw, col[0], col[1], col[2], col[3]);
+ }
+ break;
+ case 16:
+ // Don't think we can have 16 bits with PNG
+ // TODO: don't change original image
+ image->convertTo32();
+ break;
+ case 24:
+ state.info_raw.colortype = LCT_RGB;
+ state.info_png.color.colortype = LCT_RGB;
+ state.encoder.auto_convert = 0;
+ break;
+ case 32:
+ // already done
+ break;
+ }
+
+ uint32 error = lodepng_encode(&raw, &rawsize, pixels, image->width, image->height, &state);
+ if(error){
+ RWERROR((ERR_GENERAL, lodepng_error_text(error)));
+ return;
+ }
+ if(pixels != image->pixels)
+ rwFree(pixels);
+ file.write8(raw, rawsize);
+ file.close();
+ free(raw);
+}
+
+}
diff --git a/src/prim.cpp b/src/prim.cpp
new file mode 100644
index 0000000..eb353d9
--- /dev/null
+++ b/src/prim.cpp
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+
+
+void
+BBox::initialize(V3d *point)
+{
+ this->inf = *point;
+ this->sup = *point;
+}
+
+void
+BBox::addPoint(V3d *point)
+{
+ if(point->x < this->inf.x)
+ this->inf.x = point->x;
+ if(point->y < this->inf.y)
+ this->inf.y = point->y;
+ if(point->z < this->inf.z)
+ this->inf.z = point->z;
+ if(point->x > this->sup.x)
+ this->sup.x = point->x;
+ if(point->y > this->sup.y)
+ this->sup.y = point->y;
+ if(point->z > this->sup.z)
+ this->sup.z = point->z;
+}
+
+void
+BBox::calculate(V3d *points, int32 n)
+{
+ this->inf = points[0];
+ this->sup = points[0];
+ while(--n){
+ points++;
+ if(points->x < this->inf.x)
+ this->inf.x = points->x;
+ if(points->y < this->inf.y)
+ this->inf.y = points->y;
+ if(points->z < this->inf.z)
+ this->inf.z = points->z;
+ if(points->x > this->sup.x)
+ this->sup.x = points->x;
+ if(points->y > this->sup.y)
+ this->sup.y = points->y;
+ if(points->z > this->sup.z)
+ this->sup.z = points->z;
+ }
+}
+
+bool
+BBox::containsPoint(V3d *point)
+{
+ return point->x >= this->inf.x && point->x <= this->sup.x &&
+ point->y >= this->inf.y && point->y <= this->sup.y &&
+ point->z >= this->inf.z && point->z <= this->sup.z;
+}
+
+}
diff --git a/src/ps2/pds.cpp b/src/ps2/pds.cpp
new file mode 100644
index 0000000..afdc6e8
--- /dev/null
+++ b/src/ps2/pds.cpp
@@ -0,0 +1,203 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+namespace rw {
+namespace ps2 {
+
+struct PdsGlobals
+{
+ Pipeline **pipes;
+ int32 maxPipes;
+ int32 numPipes;
+};
+static PdsGlobals pdsGlobals;
+
+Pipeline*
+getPDSPipe(uint32 data)
+{
+ for(int32 i = 0; i < pdsGlobals.numPipes; i++)
+ if(pdsGlobals.pipes[i]->pluginData == data)
+ return pdsGlobals.pipes[i];
+ return nil;
+}
+
+void
+registerPDSPipe(Pipeline *pipe)
+{
+ if(pdsGlobals.pipes == nil)
+ pdsGlobals.pipes = rwNewT(Pipeline*, pdsGlobals.maxPipes, MEMDUR_GLOBAL | ID_PDS);
+ assert(pdsGlobals.numPipes < pdsGlobals.maxPipes);
+ pdsGlobals.pipes[pdsGlobals.numPipes++] = pipe;
+}
+
+static void
+atomicPDSRights(void *object, int32, int32, uint32 data)
+{
+ Atomic *a = (Atomic*)object;
+ a->pipeline = (ObjPipeline*)getPDSPipe(data);
+// printf("atm pds: %x %x %x\n", data, a->pipeline->pluginID, a->pipeline->pluginData);
+}
+
+static void
+materialPDSRights(void *object, int32, int32, uint32 data)
+{
+ Material *m = (Material*)object;
+ m->pipeline = (ObjPipeline*)getPDSPipe(data);
+// printf("mat pds: %x %x %x\n", data, m->pipeline->pluginID, m->pipeline->pluginData);
+}
+
+static void *pdsOpen(void *object, int32 offset, int32 size) { return object; }
+static void*
+pdsClose(void *object, int32 offset, int32 size)
+{
+ // TODO MEMORY: free registered pipelines
+ rwFree(pdsGlobals.pipes);
+ return object;
+}
+
+void
+registerPDSPlugin(int32 n)
+{
+ pdsGlobals.maxPipes = n;
+ pdsGlobals.numPipes = 0;
+ pdsGlobals.pipes = nil;
+ Engine::registerPlugin(0, ID_PDS, pdsOpen, pdsClose);
+ Atomic::registerPlugin(0, ID_PDS, nil, nil, nil);
+ Atomic::setStreamRightsCallback(ID_PDS, atomicPDSRights);
+
+ Material::registerPlugin(0, ID_PDS, nil, nil, nil);
+ Material::setStreamRightsCallback(ID_PDS, materialPDSRights);
+}
+
+void
+registerPluginPDSPipes(void)
+{
+ // TODO: how do we destroy them?
+
+ // rwPDS_G3_Skin_GrpMatPipeID
+ MatPipeline *pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x11001;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ pipe->attribs[AT_NORMAL+1] = &attribWeights;
+ uint32 vertCount = MatPipeline::getVertCount(VU_Lights-0x100, 5, 3, 2);
+ pipe->setTriBufferSizes(5, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = skinPreCB;
+ pipe->postUninstCB = skinPostCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3_Skin_GrpAtmPipeID
+ ObjPipeline *opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x11002;
+ opipe->groupPipeline = pipe;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3_MatfxUV1_GrpMatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x1100b;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3_MatfxUV1_GrpAtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x1100d;
+ opipe->groupPipeline = pipe;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3_MatfxUV2_GrpMatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x1100c;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV2;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3_MatfxUV2_GrpAtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x1100e;
+ opipe->groupPipeline = pipe;
+ registerPDSPipe(opipe);
+
+ // RW World plugin
+
+ // rwPDS_G3x_Generic_AtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x50001;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3x_Skin_AtmPipeID
+ opipe = ObjPipeline::create();
+ opipe->pluginID = ID_PDS;
+ opipe->pluginData = 0x5000b;
+ registerPDSPipe(opipe);
+
+ // rwPDS_G3xd_A4D_MatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x5002f;
+ pipe->attribs[0] = &attribXYZW;
+ pipe->attribs[1] = &attribUV;
+ pipe->attribs[2] = &attribNormal;
+ vertCount = 0x50;
+ pipe->setTriBufferSizes(3, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount; // 0xF0
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = genericPreCB;
+ registerPDSPipe(pipe);
+
+ // rwPDS_G3xd_A4DSkin_MatPipeID
+ pipe = MatPipeline::create();
+ pipe->pluginID = ID_PDS;
+ pipe->pluginData = 0x5003e;
+ pipe->attribs[0] = &attribXYZW;
+ pipe->attribs[1] = &attribUV;
+ pipe->attribs[2] = &attribNormal;
+ pipe->attribs[3] = &attribWeights;
+ vertCount = 0x30;
+ pipe->setTriBufferSizes(4, vertCount); // 0xC0
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = genericPreCB;
+ pipe->postUninstCB = skinPostCB;
+ registerPDSPipe(pipe);
+}
+
+}
+}
diff --git a/src/ps2/ps2.cpp b/src/ps2/ps2.cpp
new file mode 100644
index 0000000..0f504d6
--- /dev/null
+++ b/src/ps2/ps2.cpp
@@ -0,0 +1,1583 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#include "rwps2impl.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace ps2 {
+
+bool adcHack = false;
+
+#define ALIGNPTR(p,a) ((uint8*)(((uintptr)(p)+a-1) & ~(uintptr)(a-1)))
+
+static void*
+driverOpen(void *o, int32, int32)
+{
+ engine->driver[PLATFORM_PS2]->defaultPipeline = makeDefaultPipeline();
+
+ engine->driver[PLATFORM_PS2]->rasterNativeOffset = nativeRasterOffset;
+ engine->driver[PLATFORM_PS2]->rasterCreate = rasterCreate;
+ engine->driver[PLATFORM_PS2]->rasterLock = rasterLock;
+ engine->driver[PLATFORM_PS2]->rasterUnlock = rasterUnlock;
+ engine->driver[PLATFORM_PS2]->rasterLockPalette = rasterLockPalette;
+ engine->driver[PLATFORM_PS2]->rasterUnlockPalette = rasterUnlockPalette;
+ engine->driver[PLATFORM_PS2]->rasterNumLevels = rasterNumLevels;
+ engine->driver[PLATFORM_PS2]->imageFindRasterFormat = imageFindRasterFormat;
+ engine->driver[PLATFORM_PS2]->rasterFromImage = rasterFromImage;
+ engine->driver[PLATFORM_PS2]->rasterToImage = rasterToImage;
+
+ return o;
+}
+
+static void*
+driverClose(void *o, int32, int32)
+{
+ return o;
+}
+
+void
+registerPlatformPlugins(void)
+{
+ Driver::registerPlugin(PLATFORM_PS2, 0, PLATFORM_PS2,
+ driverOpen, driverClose);
+
+ registerNativeRaster();
+}
+
+ObjPipeline *defaultObjPipe;
+MatPipeline *defaultMatPipe;
+
+void*
+destroyNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_PS2)
+ return object;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ for(uint32 i = 0; i < header->numMeshes; i++)
+ rwFree(header->instanceMeshes[i].dataRaw);
+ rwFree(header->instanceMeshes);
+ rwFree(header);
+ geometry->instData = nil;
+ return object;
+}
+
+Stream*
+readNativeData(Stream *stream, int32, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_PS2){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geometry->instData = header;
+ header->platform = PLATFORM_PS2;
+ assert(geometry->meshHeader != nil);
+ header->numMeshes = geometry->meshHeader->numMeshes;
+ header->instanceMeshes = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+ Mesh *m = geometry->meshHeader->getMeshes();
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ InstanceData *instance = &header->instanceMeshes[i];
+ uint32 buf[2];
+ stream->read32(buf, 8);
+ instance->dataSize = buf[0];
+ instance->dataRaw = rwNewT(uint8, instance->dataSize+0x7F, MEMDUR_EVENT | ID_GEOMETRY);
+ instance->data = ALIGNPTR(instance->dataRaw, 0x80);
+#ifdef RW_PS2
+ uint32 a = (uint32)instance->data;
+ assert(a % 0x10 == 0);
+#endif
+ stream->read8(instance->data, instance->dataSize);
+#ifdef RW_PS2
+ if(!buf[1])
+ fixDmaOffsets(instance);
+#endif
+ instance->material = m->material;
+// sizedebug(instance);
+ m++;
+ }
+ return stream;
+}
+
+Stream*
+writeNativeData(Stream *stream, int32 len, void *object, int32, int32)
+{
+ ASSERTLITTLE;
+ Geometry *geometry = (Geometry*)object;
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_PS2)
+ return stream;
+ stream->writeU32(PLATFORM_PS2);
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ InstanceData *instance = &header->instanceMeshes[i];
+ uint32 buf[2];
+ buf[0] = instance->dataSize;
+ buf[1] = unfixDmaOffsets(instance);
+ stream->write32(buf, 8);
+ stream->write8(instance->data, instance->dataSize);
+#ifdef RW_PS2
+ if(!buf[1])
+ fixDmaOffsets(instance);
+#endif
+ }
+ return stream;
+}
+
+int32
+getSizeNativeData(void *object, int32, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ int32 size = 16;
+ if(geometry->instData == nil ||
+ geometry->instData->platform != PLATFORM_PS2)
+ return 0;
+ InstanceDataHeader *header = (InstanceDataHeader*)geometry->instData;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ InstanceData *instance = &header->instanceMeshes[i];
+ size += 8;
+ size += instance->dataSize;
+ }
+ return size;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+// Patch DMA ref ADDR fields to point to the actual data.
+#ifdef RW_PS2
+void
+fixDmaOffsets(InstanceData *inst)
+{
+ uint32 base = (uint32)inst->data;
+ uint32 *tag = (uint32*)inst->data;
+ for(;;){
+ switch(tag[0]&0x70000000){
+ // DMAcnt
+ case 0x10000000:
+ // no need to fix
+ tag += (1+(tag[0]&0xFFFF))*4;
+ break;
+
+ // DMAref
+ case 0x30000000:
+ // fix address and jump to next
+ tag[1] = base + tag[1]<<4;
+ tag += 4;
+ break;
+
+ // DMAret
+ case 0x60000000:
+ // we're done
+ return;
+
+ default:
+ fprintf(stderr, "error: unknown DMAtag %X\n", tag[0]);
+ return;
+ }
+ }
+}
+#endif
+
+// Patch DMA ref ADDR fields to qword offsets and return whether
+// no ref tags were found.
+// Only under RW_PS2 are the addresses actually patched but we need
+// the return value for streaming out.
+bool32
+unfixDmaOffsets(InstanceData *inst)
+{
+ bool32 norefs = 1;
+#ifdef RW_PS2
+ uint32 base = (uint32)inst->data;
+#endif
+ uint32 *tag = (uint32*)inst->data;
+ for(;;){
+ switch(tag[0]&0x70000000){
+ // DMAcnt
+ case 0x10000000:
+ // no need to unfix
+ tag += (1+(tag[0]&0xFFFF))*4;
+ break;
+
+ // DMAref
+ case 0x30000000:
+ norefs = 0;
+ // unfix address and jump to next
+#ifdef RW_PS2
+ tag[1] = (tag[1] - base)>>4;
+#endif
+ tag += 4;
+ break;
+
+ // DMAret
+ case 0x60000000:
+ return norefs;
+
+ default:
+ fprintf(stderr, "error: unknown DMAtag %X\n", tag[0]);
+ return norefs;
+ }
+ }
+}
+
+// Pipeline
+
+PipeAttribute attribXYZ = {
+ "XYZ",
+ AT_V3_32
+};
+
+PipeAttribute attribXYZW = {
+ "XYZW",
+ AT_V4_32
+};
+
+PipeAttribute attribUV = {
+ "UV",
+ AT_V2_32
+};
+
+PipeAttribute attribUV2 = {
+ "UV2",
+ AT_V4_32
+};
+
+PipeAttribute attribRGBA = {
+ "RGBA",
+ AT_V4_8 | AT_UNSGN
+};
+
+PipeAttribute attribNormal = {
+ "Normal",
+ AT_V3_8 // RW has V4_8 but uses V3_8, wtf?
+};
+
+PipeAttribute attribWeights = {
+ "Weights",
+ AT_V4_32 | AT_RW
+};
+
+static uint32
+attribSize(uint32 unpack)
+{
+ static uint32 size[] = { 32, 16, 8, 16 };
+ return ((unpack>>26 & 3)+1)*size[unpack>>24 & 3]/8;
+}
+
+#define QWC(x) (((x)+0xF)>>4)
+
+static uint32
+getBatchSize(MatPipeline *pipe, uint32 vertCount)
+{
+ PipeAttribute *a;
+ if(vertCount == 0)
+ return 0;
+ uint32 size = 1; // ITOP &c. at the end
+ for(uint i = 0; i < nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i]) && (a->attrib & AT_RW) == 0){
+ size++; // UNPACK &c.
+ size += QWC(vertCount*attribSize(a->attrib));
+ }
+ return size;
+}
+
+uint32*
+instanceXYZ(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d = (uint32*)g->morphTargets[0].vertices;
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ *p++ = d[j*3+0];
+ *p++ = d[j*3+1];
+ *p++ = d[j*3+2];
+ }
+ while((uintptr)p % 0x10)
+ *p++ = 0;
+ return p;
+}
+
+uint32*
+instanceXYZW(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d = (uint32*)g->morphTargets[0].vertices;
+ int8 *adcbits = getADCbitsForMesh(g, m);
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ *p++ = d[j*3+0];
+ *p++ = d[j*3+1];
+ *p++ = d[j*3+2];
+ *p++ = adcbits && adcbits[i] ? 0x8000 : 0;
+ }
+ // don't need to pad
+ return p;
+}
+
+uint32*
+instanceUV(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d = (uint32*)g->texCoords[0];
+ if((g->flags & Geometry::TEXTURED) ||
+ (g->flags & Geometry::TEXTURED2))
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ *p++ = d[j*2+0];
+ *p++ = d[j*2+1];
+ }
+ else
+ for(uint32 i = idx; i < idx+n; i++){
+ *p++ = 0;
+ *p++ = 0;
+ }
+ while((uintptr)p % 0x10)
+ *p++ = 0;
+ return p;
+}
+
+uint32*
+instanceUV2(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d0 = (uint32*)g->texCoords[0];
+ uint32 *d1 = (uint32*)g->texCoords[1];
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ if(g->numTexCoordSets > 0){
+ *p++ = d0[j*2+0];
+ *p++ = d0[j*2+1];
+ }else{
+ *p++ = 0;
+ *p++ = 0;
+ }
+ if(g->numTexCoordSets > 1){
+ *p++ = d1[j*2+0];
+ *p++ = d1[j*2+1];
+ }else{
+ *p++ = 0;
+ *p++ = 0;
+ }
+ }
+ while((uintptr)p % 0x10)
+ *p++ = 0;
+ return p;
+}
+
+uint32*
+instanceRGBA(uint32 *p, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ uint32 *d = (uint32*)g->colors;
+ if((g->flags & Geometry::PRELIT))
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ *p++ = d[j];
+ }
+ else
+ for(uint32 i = idx; i < idx+n; i++)
+ *p++ = 0xFF000000;
+ while((uintptr)p % 0x10)
+ *p++ = 0;
+ return p;
+}
+
+uint32*
+instanceNormal(uint32 *wp, Geometry *g, Mesh *m, uint32 idx, uint32 n)
+{
+ uint16 j;
+ V3d *d = g->morphTargets[0].normals;
+ uint8 *p = (uint8*)wp;
+ if((g->flags & Geometry::NORMALS))
+ for(uint32 i = idx; i < idx+n; i++){
+ j = m->indices[i];
+ *p++ = d[j].x*127.0f;
+ *p++ = d[j].y*127.0f;
+ *p++ = d[j].z*127.0f;
+ }
+ else
+ for(uint32 i = idx; i < idx+n; i++){
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
+ while((uintptr)p % 0x10)
+ *p++ = 0;
+ return (uint32*)p;
+}
+
+void
+MatPipeline::init(void)
+{
+ this->rw::Pipeline::init(PLATFORM_PS2);
+ for(int i = 0; i < 10; i++)
+ this->attribs[i] = nil;
+ this->instanceCB = nil;
+ this->uninstanceCB = nil;
+ this->preUninstCB = nil;
+ this->postUninstCB = nil;
+}
+
+MatPipeline*
+MatPipeline::create(void)
+{
+ MatPipeline *pipe = rwNewT(MatPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+void
+MatPipeline::destroy(void)
+{
+ rwFree(this);
+}
+
+void
+MatPipeline::dump(void)
+{
+ if(this->platform != PLATFORM_PS2)
+ return;
+ PipeAttribute *a;
+ printf("%x %x\n", this->pluginID, this->pluginData);
+ for(uint i = 0; i < nelem(this->attribs); i++){
+ a = this->attribs[i];
+ if(a)
+ printf("%d %s: %x\n", i, a->name, a->attrib);
+ }
+ printf("stride: %x\n", this->inputStride);
+ printf("vertcount: %x\n", this->vifOffset/this->inputStride);
+ printf("triSCount: %x\n", this->triStripCount);
+ printf("triLCount: %x\n", this->triListCount);
+ printf("vifOffset: %x\n", this->vifOffset);
+ printf("\n");
+}
+
+void
+MatPipeline::setTriBufferSizes(uint32 inputStride, uint32 bufferSize)
+{
+ PipeAttribute *a;
+
+ this->inputStride = inputStride;
+ uint32 numTLtris = bufferSize/3;
+ this->triListCount = (numTLtris & ~3) * 3;
+ this->triStripCount = bufferSize & ~3;
+ for(uint i = 0; i < nelem(this->attribs); i++){
+ a = this->attribs[i];
+ if(a && a->attrib & AT_RW){
+ // broken out attribs have different requirement
+ // because we have to be able to restart a strip
+ // at an aligned offset
+ this->triStripCount = ((bufferSize-2) & ~3)+2;
+ return;
+ }
+ }
+}
+
+// Instance format:
+// no broken out clusters
+// ======================
+// DMAret [FLUSH; MSKPATH3 || FLUSH; FLUSH] {
+// foreach batch {
+// foreach cluster {
+// MARK/0; STMOD; STCYCL; UNPACK
+// unpack-data
+// }
+// ITOP; MSCALF/MSCNT; // if first/not-first
+// 0/FLUSH; 0/MSKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last
+// }
+// }
+//
+// broken out clusters
+// ===================
+// foreach batch {
+// foreach broken out cluster {
+// DMAref [STCYCL; UNPACK] -> pointer into unpack-data
+// DMAcnt (empty)
+// }
+// DMAcnt/ret {
+// foreach cluster {
+// MARK/0; STMOD; STCYCL; UNPACK
+// unpack-data
+// }
+// ITOP; MSCALF/MSCNT; // if first/not-first
+// 0/FLUSH; 0/MSKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last
+// }
+// }
+// unpack-data for broken out clusters
+
+uint32 markcnt = 0;
+
+enum {
+ DMAcnt = 0x10000000,
+ DMAref = 0x30000000,
+ DMAret = 0x60000000,
+
+ VIF_NOP = 0,
+ VIF_STCYCL = 0x01000000,
+ VIF_STCYCL1 = 0x01000100, // WL = 1
+ VIF_OFFSET = 0x02000000,
+ VIF_BASE = 0x03000000,
+ VIF_ITOP = 0x04000000,
+ VIF_STMOD = 0x05000000,
+ VIF_MSKPATH3 = 0x06000000,
+ VIF_MARK = 0x07000000,
+ VIF_FLUSHE = 0x10000000,
+ VIF_FLUSH = 0x11000000,
+ VIF_FLUSHA = 0x13000000,
+ VIF_MSCAL = 0x14000000,
+ VIF_MSCALF = 0x15000000,
+ VIF_MSCNT = 0x17000000,
+ VIF_STMASK = 0x20000000,
+ VIF_STROW = 0x30000000,
+ VIF_STCOL = 0x31000000,
+ VIF_MPG = 0x4A000000,
+ VIF_DIRECT = 0x50000000,
+ VIF_DIRECTHL = 0x51000000,
+ VIF_UNPACK = 0x60000000 // no mode encoded
+};
+
+struct InstMeshInfo
+{
+ uint32 numAttribs, numBrokenAttribs;
+ uint32 batchVertCount, lastBatchVertCount;
+ uint32 numBatches;
+ uint32 batchSize, lastBatchSize;
+ uint32 size; // size of DMA chain without broken out data
+ uint32 size2; // size of broken out data
+ uint32 vertexSize;
+ uint32 attribPos[10];
+};
+
+InstMeshInfo
+getInstMeshInfo(MatPipeline *pipe, Geometry *g, Mesh *m)
+{
+ PipeAttribute *a;
+ InstMeshInfo im;
+ im.numAttribs = 0;
+ im.numBrokenAttribs = 0;
+ im.vertexSize = 0;
+ for(uint i = 0; i < nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i])) {
+ if(a->attrib & AT_RW)
+ im.numBrokenAttribs++;
+ else{
+ im.vertexSize += attribSize(a->attrib);
+ im.numAttribs++;
+ }
+ }
+ if(g->meshHeader->flags == MeshHeader::TRISTRIP){
+ im.numBatches = (m->numIndices-2) / (pipe->triStripCount-2);
+ im.batchVertCount = pipe->triStripCount;
+ im.lastBatchVertCount = (m->numIndices-2) % (pipe->triStripCount-2);
+ if(im.lastBatchVertCount){
+ im.numBatches++;
+ im.lastBatchVertCount += 2;
+ }
+ }else{ // TRILIST; nothing else supported yet
+ im.numBatches = (m->numIndices+pipe->triListCount-1) /
+ pipe->triListCount;
+ im.batchVertCount = pipe->triListCount;
+ im.lastBatchVertCount = m->numIndices % pipe->triListCount;
+ }
+ if(im.lastBatchVertCount == 0)
+ im.lastBatchVertCount = im.batchVertCount;
+
+ im.batchSize = getBatchSize(pipe, im.batchVertCount);
+ im.lastBatchSize = getBatchSize(pipe, im.lastBatchVertCount);
+ if(im.numBrokenAttribs == 0)
+ im.size = 1 + im.batchSize*(im.numBatches-1) + im.lastBatchSize;
+ else
+ im.size = 2*im.numBrokenAttribs*im.numBatches +
+ (1+im.batchSize)*(im.numBatches-1) + 1+im.lastBatchSize;
+
+ /* figure out size and addresses of broken out sections */
+ im.size2 = 0;
+ for(uint i = 0; i < nelem(im.attribPos); i++)
+ if((a = pipe->attribs[i]) && a->attrib & AT_RW){
+ im.attribPos[i] = im.size2 + im.size;
+ im.size2 += QWC(m->numIndices*attribSize(a->attrib));
+ }
+
+ return im;
+}
+
+void
+MatPipeline::instance(Geometry *g, InstanceData *inst, Mesh *m)
+{
+ PipeAttribute *a;
+ InstMeshInfo im = getInstMeshInfo(this, g, m);
+
+ inst->dataSize = (im.size+im.size2)<<4;
+ // TODO: do this properly, just a test right now
+ inst->dataSize += 0x7F;
+ inst->dataRaw = rwNewT(uint8, inst->dataSize, MEMDUR_EVENT | ID_GEOMETRY);
+ inst->data = ALIGNPTR(inst->dataRaw, 0x80);
+
+ /* make array of addresses of broken out sections */
+ uint8 *datap[nelem(this->attribs)];
+ uint8 **dp = datap;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && a->attrib & AT_RW)
+ dp[i] = inst->data + im.attribPos[i]*0x10;
+
+ // TODO: not sure if this is correct
+ uint32 msk_flush = rw::version >= 0x35000 ? VIF_FLUSH : VIF_MSKPATH3;
+
+ uint32 idx = 0;
+ uint32 *p = (uint32*)inst->data;
+ if(im.numBrokenAttribs == 0){
+ *p++ = DMAret | im.size-1;
+ *p++ = 0;
+ *p++ = VIF_FLUSH;
+ *p++ = msk_flush;
+ }
+ for(uint32 j = 0; j < im.numBatches; j++){
+ uint32 nverts, bsize;
+ if(j < im.numBatches-1){
+ bsize = im.batchSize;
+ nverts = im.batchVertCount;
+ }else{
+ bsize = im.lastBatchSize;
+ nverts = im.lastBatchVertCount;
+ }
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && a->attrib & AT_RW){
+ uint32 atsz = attribSize(a->attrib);
+ *p++ = DMAref | QWC(nverts*atsz);
+ *p++ = im.attribPos[i];
+ *p++ = VIF_STCYCL1 | this->inputStride;
+ // Round up nverts so UNPACK will fit exactly into the DMA packet
+ // (can't pad with zeroes in broken out sections).
+ int num = (QWC(nverts*atsz)<<4)/atsz;
+ *p++ = (a->attrib&0xFF004000)
+ | 0x8000 | num << 16 | i; // UNPACK
+ // This probably shouldn't happen.
+ if(num*this->inputStride > this->vifOffset)
+ fprintf(stderr, "WARNING: PS2 instance data over vifOffset %08X, %X-> %X %X\n",
+ p[-1], num,
+ num*this->inputStride, this->vifOffset);
+
+ *p++ = DMAcnt;
+ *p++ = 0x0;
+ *p++ = VIF_NOP;
+ *p++ = VIF_NOP;
+
+ im.attribPos[i] += g->meshHeader->flags == 1 ?
+ QWC((im.batchVertCount-2)*atsz) :
+ QWC(im.batchVertCount*atsz);
+ }
+ if(im.numBrokenAttribs){
+ *p++ = (j < im.numBatches-1 ? DMAcnt : DMAret) | bsize;
+ *p++ = 0x0;
+ *p++ = VIF_NOP;
+ *p++ = VIF_NOP;
+ }
+
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && (a->attrib & AT_RW) == 0){
+ uint32 mask;
+ if(adcHack && a == &attribXYZ){
+ *p++ = VIF_STMASK;
+ *p++ = 1 << 6; // write w field with VIF1_R3
+ mask = 0x10000000;
+ }else{
+ if(rw::version >= 0x35000)
+ *p++ = VIF_NOP;
+ else
+ *p++ = VIF_MARK | markcnt++;
+ *p++ = VIF_STMOD;
+ mask = 0x00000000;
+ }
+ *p++ = VIF_STCYCL1 | this->inputStride;
+ *p++ = (a->attrib&0xFF004000) | mask
+ | 0x8000 | nverts << 16 | i; // UNPACK
+
+ if(a == &attribXYZ)
+ p = instanceXYZ(p, g, m, idx, nverts);
+ else if(a == &attribXYZW)
+ p = instanceXYZW(p, g, m, idx, nverts);
+ else if(a == &attribUV)
+ p = instanceUV(p, g, m, idx, nverts);
+ else if(a == &attribUV2)
+ p = instanceUV2(p, g, m, idx, nverts);
+ else if(a == &attribRGBA)
+ p = instanceRGBA(p, g, m, idx, nverts);
+ else if(a == &attribNormal)
+ p = instanceNormal(p, g, m, idx, nverts);
+ }
+ idx += g->meshHeader->flags == 1
+ ? im.batchVertCount-2 : im.batchVertCount;
+
+ *p++ = VIF_ITOP | nverts;
+ *p++ = j == 0 ? VIF_MSCALF : VIF_MSCNT;
+ if(j < im.numBatches-1){
+ *p++ = VIF_NOP;
+ *p++ = VIF_NOP;
+ }else{
+ *p++ = VIF_FLUSH;
+ *p++ = msk_flush;
+ }
+ }
+
+ if(this->instanceCB)
+ this->instanceCB(this, g, m, datap);
+#ifdef RW_PS2
+ if(im.numBrokenAttribs)
+ fixDmaOffsets(inst);
+#endif
+}
+
+uint8*
+MatPipeline::collectData(Geometry *g, InstanceData *inst, Mesh *m, uint8 *data[])
+{
+ PipeAttribute *a;
+ InstMeshInfo im = getInstMeshInfo(this, g, m);
+
+ uint8 *raw = rwNewT(uint8, im.vertexSize*m->numIndices, MEMDUR_EVENT | ID_GEOMETRY);
+ uint8 *dp = raw;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i])) {
+ if(a->attrib & AT_RW){
+ data[i] = inst->data + im.attribPos[i]*0x10;
+ }else{
+ data[i] = dp;
+ dp += m->numIndices*attribSize(a->attrib);
+ }
+ }
+
+ uint8 *datap[nelem(this->attribs)];
+ memcpy(datap, data, sizeof(datap));
+
+ uint32 overlap = g->meshHeader->flags == 1 ? 2 : 0;
+ uint32 *p = (uint32*)inst->data;
+ if(im.numBrokenAttribs == 0)
+ p += 4;
+ for(uint32 j = 0; j < im.numBatches; j++){
+ uint32 nverts = j < im.numBatches-1 ? im.batchVertCount :
+ im.lastBatchVertCount;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && a->attrib & AT_RW)
+ p += 8;
+ if(im.numBrokenAttribs)
+ p += 4;
+ for(uint i = 0; i < nelem(this->attribs); i++)
+ if((a = this->attribs[i]) && (a->attrib & AT_RW) == 0){
+ uint32 asz = attribSize(a->attrib);
+ p += 4;
+ if((p[-1] & 0xff004000) != a->attrib){
+ fprintf(stderr, "unexpected unpack: %08x %08x\n", p[-1], a->attrib);
+ assert(0 && "unexpected unpack\n");
+ }
+ memcpy(datap[i], p, asz*nverts);
+ datap[i] += asz*(nverts-overlap);
+ p += QWC(asz*nverts)*4;
+ }
+ p += 4;
+ }
+ return raw;
+}
+
+static void
+objInstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ // TODO: allow for REINSTANCE
+ if(geo->instData)
+ return;
+ InstanceDataHeader *header = rwNewT(InstanceDataHeader, 1, MEMDUR_EVENT | ID_GEOMETRY);
+ geo->instData = header;
+ header->platform = PLATFORM_PS2;
+ assert(geo->meshHeader != nil);
+ header->numMeshes = geo->meshHeader->numMeshes;
+ header->instanceMeshes = rwNewT(InstanceData, header->numMeshes, MEMDUR_EVENT | ID_GEOMETRY);
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ InstanceData *instance = &header->instanceMeshes[i];
+
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil)
+ m = defaultMatPipe;
+ m->instance(geo, instance, mesh);
+ instance->material = mesh->material;
+ }
+}
+
+/*
+static void
+printVertCounts(InstanceData *inst, int flag)
+{
+ uint32 *d = (uint32*)inst->data;
+ uint32 id = 0;
+ if(inst->material->pipeline)
+ id = inst->material->pipeline->pluginData;
+ int stride;
+ if(inst->arePointersFixed){
+ d += 4;
+ while(d[3]&0x60000000){ // skip UNPACKs
+ stride = d[2]&0xFF;
+ d += 4 + 4*QWC(attribSize(d[3])*((d[3]>>16)&0xFF));
+ }
+ if(d[2] == 0)
+ printf("ITOP %x %d (%d) %x\n", *d, stride, flag, id);
+ }else{
+ while((*d&0x70000000) == 0x30000000){
+ stride = d[2]&0xFF;
+ printf("UNPACK %x %d (%d) %x\n", d[3], stride, flag, id);
+ d += 8;
+ }
+ if((*d&0x70000000) == 0x10000000){
+ d += (*d&0xFFFF)*4;
+ printf("ITOP %x %d (%d) %x\n", *d, stride, flag, id);
+ }
+ }
+}
+*/
+
+static void
+objUninstance(rw::ObjPipeline *rwpipe, Atomic *atomic)
+{
+ ObjPipeline *pipe = (ObjPipeline*)rwpipe;
+ Geometry *geo = atomic->geometry;
+ if((geo->flags & Geometry::NATIVE) == 0)
+ return;
+ assert(geo->instData != nil);
+ assert(geo->instData->platform == PLATFORM_PS2);
+ InstanceDataHeader *header = (InstanceDataHeader*)geo->instData;
+ // highest possible number of vertices
+ geo->numVertices = geo->meshHeader->totalIndices;
+ geo->numTriangles = geo->meshHeader->guessNumTriangles();
+ geo->allocateData();
+ geo->allocateMeshes(geo->meshHeader->numMeshes, geo->meshHeader->totalIndices, 0);
+ uint32 *flags = rwNewT(uint32, geo->numVertices,
+ MEMDUR_FUNCTION | ID_GEOMETRY);
+ memset(flags, 0, 4*geo->numVertices);
+ memset(geo->meshHeader->getMeshes()->indices, 0, 2*geo->meshHeader->totalIndices);
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil) m = defaultMatPipe;
+ if(m->preUninstCB) m->preUninstCB(m, geo);
+ }
+ geo->numVertices = 0;
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ InstanceData *instance = &header->instanceMeshes[i];
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil) m = defaultMatPipe;
+
+ //printDMAVIF(instance);
+ uint8 *data[nelem(m->attribs)] = { nil };
+ uint8 *raw = m->collectData(geo, instance, mesh, data);
+ assert(m->uninstanceCB);
+ m->uninstanceCB(m, geo, flags, mesh, data);
+ rwFree(raw);
+ }
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->getMeshes()[i];
+ MatPipeline *m;
+ m = pipe->groupPipeline ?
+ pipe->groupPipeline :
+ (MatPipeline*)mesh->material->pipeline;
+ if(m == nil) m = defaultMatPipe;
+ if(m->postUninstCB) m->postUninstCB(m, geo);
+ }
+
+ int8 *bits = getADCbits(geo);
+ geo->generateTriangles(bits);
+ rwFree(flags);
+ geo->flags &= ~Geometry::NATIVE;
+ destroyNativeData(geo, 0, 0);
+/*
+ for(uint32 i = 0; i < header->numMeshes; i++){
+ Mesh *mesh = &geo->meshHeader->mesh[i];
+ InstanceData *instance = &header->instanceMeshes[i];
+// printf("numIndices: %d\n", mesh->numIndices);
+// printDMA(instance);
+ printVertCounts(instance, geo->meshHeader->flags);
+ }
+*/
+}
+
+void
+ObjPipeline::init(void)
+{
+ this->rw::ObjPipeline::init(PLATFORM_PS2);
+ this->groupPipeline = nil;
+ this->impl.instance = objInstance;
+ this->impl.uninstance = objUninstance;
+}
+
+ObjPipeline*
+ObjPipeline::create(void)
+{
+ ObjPipeline *pipe = rwNewT(ObjPipeline, 1, MEMDUR_GLOBAL);
+ pipe->init();
+ return pipe;
+}
+
+void
+insertVertex(Geometry *geo, int32 i, uint32 mask, Vertex *v)
+{
+ if(mask & 0x1)
+ geo->morphTargets[0].vertices[i] = v->p;
+ if(mask & 0x10)
+ geo->morphTargets[0].normals[i] = v->n;
+ if(mask & 0x100)
+ geo->colors[i] = v->c;
+ if(mask & 0x1000)
+ geo->texCoords[0][i] = v->t;
+ if(mask & 0x2000)
+ geo->texCoords[1][i] = v->t1;
+}
+
+void
+genericPreCB(MatPipeline *pipe, Geometry *geo)
+{
+ PipeAttribute *a;
+ for(int32 i = 0; i < (int)nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i]))
+ if(a == &attribXYZW){
+ allocateADC(geo);
+ break;
+ }
+ skinPreCB(pipe, geo);
+}
+
+void
+genericUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[])
+{
+ float32 *xyz = nil, *xyzw = nil;
+ float32 *uv = nil, *uv2 = nil;
+ uint8 *rgba = nil;
+ int8 *normals = nil;
+ uint32 *weights = nil;
+ int8 *adc = nil;
+ Skin *skin = nil;
+ if(skinGlobals.geoOffset)
+ skin = Skin::get(geo);
+
+ PipeAttribute *a;
+ for(int32 i = 0; i < (int)nelem(pipe->attribs); i++)
+ if((a = pipe->attribs[i])){
+ if(a == &attribXYZ) xyz = (float32*)data[i];
+ else if(a == &attribXYZW) xyzw = (float32*)data[i];
+ else if(a == &attribUV) uv = (float32*)data[i];
+ else if(a == &attribUV2) uv2 = (float32*)data[i];
+ else if(a == &attribRGBA) rgba = data[i];
+ else if(a == &attribNormal) normals = (int8*)data[i];
+ else if(a == &attribWeights) weights = (uint32*)data[i];
+ }
+
+ uint32 mask = 0x1; // vertices
+ if(normals && geo->flags & Geometry::NORMALS)
+ mask |= 0x10;
+ if(rgba && geo->flags & Geometry::PRELIT)
+ mask |= 0x100;
+ if((uv || uv2) && geo->numTexCoordSets > 0)
+ mask |= 0x1000;
+ if(uv2 && geo->numTexCoordSets > 1)
+ mask |= 0x2000;
+ if(weights && skin)
+ mask |= 0x10000;
+ if(xyzw)
+ adc = getADCbitsForMesh(geo, mesh);
+
+ Vertex v;
+ for(uint32 i = 0; i < mesh->numIndices; i++){
+ if(mask & 0x1)
+ memcpy(&v.p, xyz ? xyz : xyzw, 12);
+ if(mask & 0x10){
+ // TODO: figure out scaling :/
+ v.n.x = normals[0]/128.0f;
+ v.n.y = normals[1]/128.0f;
+ v.n.z = normals[2]/128.0f;
+ }
+ if(mask & 0x100)
+ memcpy(&v.c, rgba, 4);
+ if(mask & 0x1000)
+ memcpy(&v.t, uv ? uv : uv2, 8);
+ if(mask & 0x2000)
+ memcpy(&v.t1, uv2 + 2, 8);
+ if(mask & 0x10000)
+ for(int j = 0; j < 4; j++){
+ ((uint32*)v.w)[j] = weights[j] & ~0x3FF;
+ v.i[j] = (weights[j] & 0x3FF) >> 2;
+ if(v.i[j]) v.i[j]--;
+ if(v.w[j] == 0.0f) v.i[j] = 0;
+ }
+ int32 idx = findVertexSkin(geo, flags, mask, &v);
+ if(idx < 0)
+ idx = geo->numVertices++;
+ mesh->indices[i] = idx;
+ if(adc)
+ adc[i] = xyzw[3] != 0.0f;
+ flags[idx] = mask;
+ insertVertexSkin(geo, idx, mask, &v);
+ if(xyz) xyz += 3;
+ if(xyzw) xyzw += 4;
+ if(uv) uv += 2;
+ if(uv2) uv2 += 4;
+ rgba += 4;
+ normals += 3;
+ weights += 4;
+ }
+}
+
+/*
+void
+defaultUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[])
+{
+ float32 *verts = (float32*)data[AT_XYZ];
+ float32 *texcoords = (float32*)data[AT_UV];
+ uint8 *colors = (uint8*)data[AT_RGBA];
+ int8 *norms = (int8*)data[AT_NORMAL];
+ uint32 mask = 0x1; // vertices
+ if(geo->flags & Geometry::NORMALS)
+ mask |= 0x10;
+ if(geo->flags & Geometry::PRELIT)
+ mask |= 0x100;
+ for(int32 i = 0; i < geo->numTexCoordSets; i++)
+ mask |= 0x1000 << i;
+ int numUV = pipe->attribs[AT_UV] == &attribUV2 ? 2 : 1;
+
+ Vertex v;
+ for(uint32 i = 0; i < mesh->numIndices; i++){
+ if(mask & 0x1)
+ memcpy(&v.p, verts, 12);
+ if(mask & 0x10){
+ v.n[0] = norms[0]/127.0f;
+ v.n[1] = norms[1]/127.0f;
+ v.n[2] = norms[2]/127.0f;
+ }
+ if(mask & 0x100){
+ memcpy(&v.c, colors, 4);
+ //v.c[3] = 0xFF;
+ }
+ if(mask & 0x1000)
+ memcpy(&v.t, texcoords, 8);
+ if(mask & 0x2000)
+ memcpy(&v.t1, texcoords+2, 8);
+
+ int32 idx = findVertex(geo, flags, mask, &v);
+ if(idx < 0)
+ idx = geo->numVertices++;
+ mesh->indices[i] = idx;
+ flags[idx] = mask;
+ insertVertex(geo, idx, mask, &v);
+ verts += 3;
+ texcoords += 2*numUV;
+ colors += 4;
+ norms += 3;
+ }
+}
+*/
+
+#undef QWC
+
+ObjPipeline*
+makeDefaultPipeline(void)
+{
+ if(defaultMatPipe == nil){
+ MatPipeline *pipe = MatPipeline::create();
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ uint32 vertCount = MatPipeline::getVertCount(VU_Lights,4,3,2);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+ defaultMatPipe = pipe;
+ }
+
+ if(defaultObjPipe == nil){
+ ObjPipeline *opipe = ObjPipeline::create();
+ defaultObjPipe = opipe;
+ }
+ return defaultObjPipe;
+}
+
+// ADC
+
+int32 adcOffset;
+
+int8*
+getADCbits(Geometry *geo)
+{
+ int8 *bits = nil;
+ if(adcOffset){
+ ADCData *adc = PLUGINOFFSET(ADCData, geo, adcOffset);
+ if(adc->adcFormatted)
+ bits = adc->adcBits;
+ }
+ return bits;
+}
+
+int8*
+getADCbitsForMesh(Geometry *geo, Mesh *mesh)
+{
+ int8 *bits = getADCbits(geo);
+ if(bits == nil)
+ return nil;
+ int32 n = mesh - geo->meshHeader->getMeshes();
+ for(int32 i = 0; i < n; i++)
+ bits += geo->meshHeader->getMeshes()[i].numIndices;
+ return bits;
+}
+
+// TODO
+void
+convertADC(Geometry*)
+{
+}
+
+// Not optimal but works
+void
+unconvertADC(Geometry *g)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, g, adcOffset);
+ if(!adc->adcFormatted)
+ return;
+ int8 *b = adc->adcBits;
+
+ MeshHeader *oldmh = g->meshHeader;
+ g->meshHeader = nil;
+ // Don't allocate indices for now
+ MeshHeader *newmh = g->allocateMeshes(oldmh->numMeshes, 0, 1);
+ newmh->flags = oldmh->flags; // should be tristrip
+ Mesh *oldm = oldmh->getMeshes();
+ Mesh *newm = newmh->getMeshes();
+ for(int32 i = 0; i < newmh->numMeshes; i++){
+ newm->material = oldm->material;
+ newm->numIndices = oldm->numIndices;
+ for(uint32 j = 0; j < oldm->numIndices; j++)
+ if(*b++)
+ newm->numIndices += 2;
+ newmh->totalIndices += newm->numIndices;
+ newm++;
+ oldm++;
+ }
+ // Now re-allocate with indices
+ newmh = g->allocateMeshes(newmh->numMeshes, newmh->totalIndices, 0);
+ b = adc->adcBits;
+ oldm = oldmh->getMeshes();
+ newm = newmh->getMeshes();
+ for(int32 i = 0; i < newmh->numMeshes; i++){
+ int32 n = 0;
+ for(uint32 j = 0; j < oldm->numIndices; j++){
+ if(*b++){
+ newm->indices[n++] = oldm->indices[j-1];
+ newm->indices[n++] = oldm->indices[j-1];
+ }
+ newm->indices[n++] = oldm->indices[j];
+ }
+ newm++;
+ oldm++;
+ }
+ rwFree(oldmh);
+ adc->adcFormatted = 0;
+ rwFree(adc->adcBits);
+ adc->adcBits = nil;
+ adc->numBits = 0;
+}
+
+void
+allocateADC(Geometry *geo)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, geo, adcOffset);
+ adc->adcFormatted = 1;
+ adc->numBits = geo->meshHeader->totalIndices;
+ int32 size = adc->numBits+3 & ~3;
+ adc->adcBits = rwNewT(int8, size, MEMDUR_EVENT | ID_ADC);
+ memset(adc->adcBits, 0, size);
+}
+
+static void*
+createADC(void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ adc->adcFormatted = 0;
+ return object;
+}
+
+static void*
+copyADC(void *dst, void *src, int32 offset, int32)
+{
+ ADCData *dstadc = PLUGINOFFSET(ADCData, dst, offset);
+ ADCData *srcadc = PLUGINOFFSET(ADCData, src, offset);
+ dstadc->adcFormatted = srcadc->adcFormatted;
+ if(!dstadc->adcFormatted)
+ return dst;
+ dstadc->numBits = srcadc->numBits;
+ int32 size = dstadc->numBits+3 & ~3;
+ dstadc->adcBits = rwNewT(int8, size, MEMDUR_EVENT | ID_ADC);
+ memcpy(dstadc->adcBits, srcadc->adcBits, size);
+ return dst;
+}
+
+static void*
+destroyADC(void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ if(adc->adcFormatted)
+ rwFree(adc->adcBits);
+ return object;
+}
+
+static Stream*
+readADC(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ if(!findChunk(stream, ID_ADC, nil, nil)){
+ RWERROR((ERR_CHUNK, "ADC"));
+ return nil;
+ }
+ adc->numBits = stream->readI32();
+ adc->adcFormatted = 1;
+ if(adc->numBits == 0){
+ adc->adcBits = nil;
+ adc->numBits = 0;
+ return stream;
+ }
+ int32 size = adc->numBits+3 & ~3;
+ adc->adcBits = rwNewT(int8, size, MEMDUR_EVENT | ID_ADC);
+ stream->read8(adc->adcBits, size);
+ return stream;
+}
+
+static Stream*
+writeADC(Stream *stream, int32 len, void *object, int32 offset, int32)
+{
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ Geometry *geometry = (Geometry*)object;
+ writeChunkHeader(stream, ID_ADC, len-12);
+ if(geometry->flags & Geometry::NATIVE){
+ stream->writeI32(0);
+ return stream;
+ }
+ stream->writeI32(adc->numBits);
+ int32 size = adc->numBits+3 & ~3;
+ stream->write8(adc->adcBits, size);
+ return stream;
+}
+
+static int32
+getSizeADC(void *object, int32 offset, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+ ADCData *adc = PLUGINOFFSET(ADCData, object, offset);
+ if(!adc->adcFormatted)
+ return 0;
+ if(geometry->flags & Geometry::NATIVE)
+ return 16;
+ return 16 + (adc->numBits+3 & ~3);
+}
+
+void
+registerADCPlugin(void)
+{
+ adcOffset = Geometry::registerPlugin(sizeof(ADCData), ID_ADC,
+ createADC, destroyADC, copyADC);
+ Geometry::registerPluginStream(ID_ADC,
+ readADC,
+ writeADC,
+ getSizeADC);
+}
+
+// misc stuff
+
+static uint32
+unpackSize(uint32 unpack)
+{
+ static uint32 size[] = { 32, 16, 8, 4 };
+ return ((unpack>>26 & 3)+1)*size[unpack>>24 & 3]/8;
+}
+
+/* A little dumb VIF interpreter */
+static void
+sendVIF(uint32 w)
+{
+ enum VIFstate {
+ VST_cmd,
+ VST_stmask,
+ VST_strow,
+ VST_stcol,
+ VST_mpg,
+ VST_direct,
+ VST_unpack
+ };
+// static uint32 buf[256 * 16]; // maximum unpack size
+ static VIFstate state = VST_cmd;
+ static uint32 n;
+ static uint32 code;
+ uint32 imm, num;
+
+ imm = w & 0xFFFF;
+ num = (w>>16) & 0xFF;
+ switch(state){
+ case VST_cmd:
+ code = w;
+ if((code & 0x60000000) == VIF_UNPACK){
+ printf("\t%08X VIF_UNPACK\n", code);
+ printf("\t...skipping...\n");
+ state = VST_unpack;
+ n = (unpackSize(code)*num + 3) >> 2;
+ }else switch(code & 0x7F000000){
+ case VIF_NOP:
+ printf("\t%08X VIF_NOP\n", code);
+ break;
+ case VIF_STCYCL:
+ printf("\t%08X VIF_STCYCL\n", code);
+ break;
+ case VIF_OFFSET:
+ printf("\t%08X VIF_OFFSET\n", code);
+ break;
+ case VIF_BASE:
+ printf("\t%08X VIF_BASE\n", code);
+ break;
+ case VIF_ITOP:
+ printf("\t%08X VIF_ITOP\n", code);
+ break;
+ case VIF_STMOD:
+ printf("\t%08X VIF_STMOD\n", code);
+ break;
+ case VIF_MSKPATH3:
+ printf("\t%08X VIF_MSKPATH3\n", code);
+ break;
+ case VIF_MARK:
+ printf("\t%08X VIF_MARK\n", code);
+ break;
+ case VIF_FLUSHE:
+ printf("\t%08X VIF_FLUSHE\n", code);
+ break;
+ case VIF_FLUSH:
+ printf("\t%08X VIF_FLUSH\n", code);
+ break;
+ case VIF_FLUSHA:
+ printf("\t%08X VIF_FLUSHA\n", code);
+ break;
+ case VIF_MSCAL:
+ printf("\t%08X VIF_MSCAL\n", code);
+ break;
+ case VIF_MSCALF:
+ printf("\t%08X VIF_MSCALF\n", code);
+ break;
+ case VIF_MSCNT:
+ printf("\t%08X VIF_MSCNT\n", code);
+ break;
+ case VIF_STMASK:
+ printf("\t%08X VIF_STMASK\n", code);
+ printf("\t...skipping...\n");
+ state = VST_stmask;
+ n = 1;
+ break;
+ case VIF_STROW:
+ printf("\t%08X VIF_STROW\n", code);
+ printf("\t...skipping...\n");
+ state = VST_strow;
+ n = 4;
+ break;
+ case VIF_STCOL:
+ printf("\t%08X VIF_STCOL\n", code);
+ printf("\t...skipping...\n");
+ state = VST_stcol;
+ n = 4;
+ break;
+ case VIF_MPG:
+ printf("\t%08X VIF_MPG\n", code);
+ state = VST_mpg;
+ n = num*2;
+ break;
+ case VIF_DIRECT:
+ printf("\t%08X VIF_DIRECT\n", code);
+ printf("\t...skipping...\n");
+ state = VST_direct;
+ n = imm*4;
+ break;
+ case VIF_DIRECTHL:
+ printf("\t%08X VIF_DIRECTHL\n", code);
+ printf("\t...skipping...\n");
+ state = VST_direct;
+ n = imm*4;
+ break;
+ default:
+ printf("\tUnknown VIFcode %08X\n", code);
+ }
+ break;
+ /* TODO: actually do something here */
+ case VST_stmask:
+ n--;
+ break;
+ case VST_strow:
+ n--;
+ break;
+ case VST_stcol:
+ n--;
+ break;
+ case VST_mpg:
+ n--;
+ break;
+ case VST_direct:
+ n--;
+ break;
+ case VST_unpack:
+ n--;
+ break;
+ }
+ if(n == 0)
+ state = VST_cmd;
+}
+
+static void
+dmaVIF(int32 qwc, uint32 *data)
+{
+ qwc *= 4;
+ while(qwc--)
+ sendVIF(*data++);
+}
+
+void
+printDMAVIF(InstanceData *inst)
+{
+ uint32 *tag = (uint32*)inst->data;
+ uint32 *base = (uint32*)inst->data;
+ uint32 qwc;
+
+ for(;;){
+ qwc = tag[0]&0xFFFF;
+ switch(tag[0]&0x70000000){
+ case DMAcnt:
+ printf("DMAcnt %04x %08x\n", qwc, tag[1]);
+ sendVIF(tag[2]);
+ sendVIF(tag[3]);
+ dmaVIF(qwc, tag+4);
+ tag += (1+qwc)*4;
+ break;
+
+ case DMAref:
+ printf("DMAref %04x %08x\n", qwc, tag[1]);
+ sendVIF(tag[2]);
+ sendVIF(tag[3]);
+ dmaVIF(qwc, base + tag[1]*4);
+ tag += 4;
+ break;
+
+ case DMAret:
+ printf("DMAret %04x %08x\n", qwc, tag[1]);
+ sendVIF(tag[2]);
+ sendVIF(tag[3]);
+ dmaVIF(qwc, tag+4);
+ printf("\n");
+ return;
+ }
+ }
+}
+
+void
+printDMA(InstanceData *inst)
+{
+ uint32 *tag = (uint32*)inst->data;
+ uint32 qwc;
+ for(;;){
+ qwc = tag[0]&0xFFFF;
+ switch(tag[0]&0x70000000){
+ case DMAcnt:
+ printf("CNT %04x %08x\n", qwc, tag[1]);
+ tag += (1+qwc)*4;
+ break;
+
+ case DMAref:
+ printf("REF %04x %08x\n", qwc, tag[1]);
+ tag += 4;
+ break;
+
+ case DMAret:
+ printf("RET %04x %08x\n\n", qwc, tag[1]);
+ return;
+ }
+ }
+}
+
+/*
+void
+sizedebug(InstanceData *inst)
+{
+ if(inst->arePointersFixed == 2)
+ return;
+ uint32 *base = (uint32*)inst->data;
+ uint32 *tag = (uint32*)inst->data;
+ uint32 *last = nil;
+ for(;;){
+ switch(tag[0]&0x70000000){
+ case DMAcnt:
+ tag += (1+(tag[0]&0xFFFF))*4;
+ break;
+
+ case DMAref:
+ last = base + tag[1]*4 + (tag[0]&0xFFFF)*4;
+ tag += 4;
+ break;
+
+ case DMAret:
+ tag += (1+(tag[0]&0xFFFF))*4;
+ uint32 diff;
+ if(!last)
+ diff = (uint8*)tag - (uint8*)base;
+ else
+ diff = (uint8*)last - (uint8*)base;
+ printf("%x %x %x\n", inst->dataSize-diff, diff, inst->dataSize);
+ return;
+
+ default:
+ printf("unkown DMAtag: %X %X\n", tag[0], tag[1]);
+ break;
+ }
+ }
+}
+*/
+
+}
+}
diff --git a/src/ps2/ps2device.cpp b/src/ps2/ps2device.cpp
new file mode 100644
index 0000000..98f9914
--- /dev/null
+++ b/src/ps2/ps2device.cpp
@@ -0,0 +1,49 @@
+#ifdef RW_PS2
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "../rwanim.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#include "rwps2impl.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+namespace ps2 {
+
+Device renderdevice = {
+ 16777215.0f, 0.0f,
+ null::beginUpdate,
+ null::endUpdate,
+ null::clearCamera,
+ null::showRaster,
+ null::rasterRenderFast,
+ null::setRenderState,
+ null::getRenderState,
+ null::im2DRenderLine,
+ null::im2DRenderTriangle,
+ null::im2DRenderPrimitive,
+ null::im2DRenderIndexedPrimitive,
+ null::im3DTransform,
+ null::im3DRenderPrimitive,
+ null::im3DRenderIndexedPrimitive,
+ null::im3DEnd,
+ null::deviceSystem
+};
+
+}
+}
+
+#endif
diff --git a/src/ps2/ps2matfx.cpp b/src/ps2/ps2matfx.cpp
new file mode 100644
index 0000000..6972a05
--- /dev/null
+++ b/src/ps2/ps2matfx.cpp
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#define PLUGIN_ID ID_MATFX
+
+namespace rw {
+namespace ps2 {
+
+static void*
+matfxOpen(void *o, int32, int32)
+{
+ matFXGlobals.pipelines[PLATFORM_PS2] = makeMatFXPipeline();
+ return o;
+}
+
+static void*
+matfxClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_PS2])->groupPipeline->destroy();
+ ((ObjPipeline*)matFXGlobals.pipelines[PLATFORM_PS2])->destroy();
+ matFXGlobals.pipelines[PLATFORM_PS2] = nil;
+ return o;
+}
+
+void
+initMatFX(void)
+{
+ Driver::registerPlugin(PLATFORM_PS2, 0, ID_MATFX,
+ matfxOpen, matfxClose);
+}
+
+ObjPipeline*
+makeMatFXPipeline(void)
+{
+ MatPipeline *pipe = MatPipeline::create();
+ pipe->pluginID = ID_MATFX;
+ pipe->pluginData = 0;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ uint32 vertCount = MatPipeline::getVertCount(0x3C5, 4, 3, 3);
+ pipe->setTriBufferSizes(4, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->uninstanceCB = genericUninstanceCB;
+
+ ObjPipeline *opipe = ObjPipeline::create();
+ opipe->pluginID = ID_MATFX;
+ opipe->pluginData = 0;
+ opipe->groupPipeline = pipe;
+ return opipe;
+}
+
+}
+}
diff --git a/src/ps2/ps2raster.cpp b/src/ps2/ps2raster.cpp
new file mode 100644
index 0000000..db266de
--- /dev/null
+++ b/src/ps2/ps2raster.cpp
@@ -0,0 +1,2238 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwengine.h"
+#include "rwps2.h"
+
+#define PLUGIN_ID ID_DRIVER
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+namespace rw {
+namespace ps2 {
+
+int32 nativeRasterOffset;
+
+#define MAXLEVEL(r) ((r)->tex1low >> 2)
+static bool32 noNewStyleRasters;
+
+enum Psm {
+ PSMCT32 = 0x0,
+ PSMCT24 = 0x1,
+ PSMCT16 = 0x2,
+ PSMCT16S = 0xA,
+ PSMT8 = 0x13,
+ PSMT4 = 0x14,
+ PSMT8H = 0x1B,
+ PSMT4HL = 0x24,
+ PSMT4HH = 0x2C,
+ PSMZ32 = 0x30,
+ PSMZ24 = 0x31,
+ PSMZ16 = 0x32,
+ PSMZ16S = 0x3A
+};
+
+// i don't really understand this, stolen from RW
+static void
+transferMinSize(int32 psm, int32 flags, int32 *minw, int32 *minh)
+{
+ *minh = 1;
+ switch(psm){
+ case PSMCT32:
+ case PSMZ32:
+ *minw = 2; // 32 bit
+ break;
+ case PSMCT16:
+ case PSMCT16S:
+ case PSMZ16:
+ case PSMZ16S:
+ *minw = 4; // 16 bit
+ break;
+ case PSMCT24:
+ case PSMT8:
+ case PSMT4:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ case PSMZ24:
+ *minw = 8; // everything else
+ break;
+ }
+ if(flags & 0x2 && psm == PSMT8){
+ *minw = 16;
+ *minh = 4;
+ }
+ if(flags & 0x4 && psm == PSMT4){
+ *minw = 32;
+ *minh = 4;
+ }
+}
+
+#define ALIGN(x,a) ((x) + (a)-1 & ~((a)-1))
+#define ALIGN16(x) ((x) + 0xF & ~0xF)
+#define ALIGN64(x) ((x) + 0x3F & ~0x3F)
+#define NSIZE(dim,pagedim) (((dim) + (pagedim)-1)/(pagedim))
+
+void*
+mallocalign(size_t size, int32 alignment)
+{
+ void *p;
+ void **pp;
+ p = rwMalloc(size + alignment + sizeof(void*), MEMDUR_EVENT | ID_RASTERPS2);
+ if(p == nil) return nil;
+ pp = (void**)(((uintptr)p + sizeof(void*) + alignment)&~(alignment-1));
+ pp[-1] = p;
+ return (void*)pp;
+}
+
+void
+freealign(void *p)
+{
+ void *pp;
+ if(p == nil) return;
+ pp = ((void**)p)[-1];
+ rwFree(pp);
+}
+
+// TODO: these depend on video mode, set in deviceSystem!
+int32 cameraFormat = Raster::C8888;
+int32 cameraDepth = 32;
+int32 cameraZDepth = 16;
+
+int32 defaultMipMapKL = 0xFC0;
+int32 maxMipLevels = 7;
+
+int32
+getRasterFormat(Raster *raster)
+{
+ int32 palformat, pixelformat, mipmapflags;
+ pixelformat = raster->format & 0xF00;
+ palformat = raster->format & 0x6000;
+ mipmapflags = raster->format & 0x9000;
+ switch(raster->type){
+ case Raster::ZBUFFER:
+ if(palformat || mipmapflags){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ if(raster->depth && raster->depth != cameraZDepth){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = cameraZDepth;
+ if(pixelformat){
+ if((raster->depth == 16 && pixelformat != Raster::D16) ||
+ (raster->depth == 32 && pixelformat != Raster::D32)){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }
+ pixelformat = raster->depth == 16 ? Raster::D16 : Raster::D32;
+ raster->format = pixelformat;
+ break;
+ case Raster::CAMERA:
+ if(palformat || mipmapflags){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ if(raster->depth && raster->depth != cameraDepth){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = cameraDepth;
+ if(pixelformat && pixelformat != cameraFormat){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ pixelformat = cameraFormat;
+ raster->format = pixelformat;
+ break;
+ case Raster::NORMAL:
+ case Raster::CAMERATEXTURE:
+ if(palformat || mipmapflags){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ /* fallthrough */
+ case Raster::TEXTURE:
+ // Find raster format by depth if none was given
+ if(pixelformat == 0)
+ switch(raster->depth){
+ case 4:
+ pixelformat = Raster::C1555;
+ palformat = Raster::PAL4;
+ break;
+ case 8:
+ pixelformat = Raster::C1555;
+ palformat = Raster::PAL8;
+ break;
+ case 24:
+ // unsafe
+ // pixelformat = Raster::C888;
+ // palformat = 0;
+ // break;
+ case 32:
+ pixelformat = Raster::C8888;
+ palformat = 0;
+ break;
+ default:
+ pixelformat = Raster::C1555;
+ palformat = 0;
+ break;
+ }
+ raster->format = pixelformat | palformat | mipmapflags;
+ // Sanity check raster format and depth; set depth if none given
+ if(palformat){
+ if(palformat == Raster::PAL8){
+ if(raster->depth && raster->depth != 8){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 8;
+ if(pixelformat != Raster::C1555 && pixelformat != Raster::C8888){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }else if(palformat == Raster::PAL4){
+ if(raster->depth && raster->depth != 4){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 4;
+ if(pixelformat != Raster::C1555 && pixelformat != Raster::C8888){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }else{
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ }else if(pixelformat == Raster::C1555){
+ if(raster->depth && raster->depth != 16){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 16;
+ }else if(pixelformat == Raster::C8888){
+ if(raster->depth && raster->depth != 32){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 32;
+ }else if(pixelformat == Raster::C888){
+ assert(0 && "24 bit rasters not supported");
+ if(raster->depth && raster->depth != 24){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ raster->depth = 24;
+ }else{
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Memory units:
+ * Column: 64 bytes (single cycle access)
+ * Block: 256 bytes, 64 words, 4 columns. texture base pointers
+ * Page: 8 kbytes, 2 kwords, 128 columns, 32 blocks. frame buffer base pointers
+ * entire memory: 4 mbytes, 64k columns, 16k blocks, 512 pages
+ *
+ * PSMT4: 128x128 pixels, 4x8 blocks per page, 32x16 pixels per block
+ * PSMT8: 128x64 pixels, 8x4 blocks per page, 16x16 pixels per block
+ * PSMCT16(S): 64x64 pixels, 4x8 blocks per page, 16x8 pixels per block
+ * PSMCT24: 64x32 pixels, 8x4 blocks per page, 8x8 pixels per block
+ * PSMCT32: 64x32 pixels, 8x4 blocks per page, 8x8 pixels per block
+ *
+ * Layout of blocks in page:
+ *
+ * PSMCT24, PSMCT32, PSMT8
+ * +----+----+----+----+----+----+----+----+
+ * | 0 | 1 | 4 | 5 | 16 | 17 | 20 | 21 |
+ * +----+----+----+----+----+----+----+----+
+ * | 2 | 3 | 6 | 7 | 18 | 19 | 22 | 23 |
+ * +----+----+----+----+----+----+----+----+
+ * | 8 | 9 | 12 | 13 | 24 | 25 | 28 | 29 |
+ * +----+----+----+----+----+----+----+----+
+ * | 10 | 11 | 14 | 15 | 26 | 27 | 30 | 31 |
+ * +----+----+----+----+----+----+----+----+
+ *
+ * PSMCT16, PSMT4
+ * +----+----+----+----+
+ * | 0 | 2 | 8 | 10 |
+ * +----+----+----+----+
+ * | 1 | 3 | 9 | 11 |
+ * +----+----+----+----+
+ * | 4 | 6 | 12 | 14 |
+ * +----+----+----+----+
+ * | 5 | 7 | 13 | 15 |
+ * +----+----+----+----+
+ * | 16 | 18 | 24 | 26 |
+ * +----+----+----+----+
+ * | 17 | 19 | 25 | 27 |
+ * +----+----+----+----+
+ * | 20 | 22 | 28 | 30 |
+ * +----+----+----+----+
+ * | 21 | 23 | 29 | 31 |
+ * +----+----+----+----+
+ *
+ * PSMCT16S
+ * +----+----+----+----+
+ * | 0 | 2 | 16 | 18 |
+ * +----+----+----+----+
+ * | 1 | 3 | 17 | 19 |
+ * +----+----+----+----+
+ * | 8 | 10 | 24 | 26 |
+ * +----+----+----+----+
+ * | 9 | 11 | 25 | 27 |
+ * +----+----+----+----+
+ * | 4 | 6 | 20 | 22 |
+ * +----+----+----+----+
+ * | 5 | 7 | 21 | 23 |
+ * +----+----+----+----+
+ * | 12 | 14 | 28 | 30 |
+ * +----+----+----+----+
+ * | 13 | 15 | 29 | 31 |
+ * +----+----+----+----+
+ *
+ */
+
+static uint8 blockmap_PSMCT32[32] = {
+ 0, 1, 4, 5, 16, 17, 20, 21,
+ 2, 3, 6, 7, 18, 19, 22, 23,
+ 8, 9, 12, 13, 24, 25, 28, 29,
+ 10, 11, 14, 15, 26, 27, 30, 31,
+};
+static uint8 blockmap_PSMCT16[32] = {
+ 0, 2, 8, 10,
+ 1, 3, 9, 11,
+ 4, 6, 12, 14,
+ 5, 7, 13, 15,
+ 16, 18, 24, 26,
+ 17, 19, 25, 27,
+ 20, 22, 28, 30,
+ 21, 23, 29, 31,
+};
+static uint8 blockmap_PSMCT16S[32] = {
+ 0, 2, 16, 18,
+ 1, 3, 17, 19,
+ 8, 10, 24, 26,
+ 9, 11, 25, 27,
+ 4, 6, 20, 22,
+ 5, 7, 21, 23,
+ 12, 14, 28, 30,
+ 13, 15, 29, 31,
+};
+static uint8 blockmap_PSMZ32[32] = {
+ 24, 25, 28, 29, 8, 9, 12, 13,
+ 26, 27, 30, 31, 10, 11, 14, 15,
+ 16, 17, 20, 21, 0, 1, 4, 5,
+ 18, 19, 22, 23, 2, 3, 6, 7,
+};
+static uint8 blockmap_PSMZ16[32] = {
+ 24, 26, 16, 18,
+ 25, 27, 17, 19,
+ 28, 30, 20, 22,
+ 29, 31, 21, 23,
+ 8, 10, 0, 2,
+ 9, 11, 1, 3,
+ 12, 14, 4, 6,
+ 13, 15, 5, 7,
+};
+static uint8 blockmap_PSMZ16S[32] = {
+ 24, 26, 8, 10,
+ 25, 27, 9, 11,
+ 16, 18, 0, 2,
+ 17, 19, 1, 3,
+ 28, 30, 12, 14,
+ 29, 31, 13, 15,
+ 20, 22, 4, 6,
+ 21, 23, 5, 7,
+};
+
+static uint8 blockmaprev_PSMCT32[32] = {
+ 0, 1, 8, 9, 2, 3, 10, 11,
+ 16, 17, 24, 25, 18, 19, 26, 27,
+ 4, 5, 12, 13, 6, 7, 14, 15,
+ 20, 21, 28, 29, 22, 23, 30, 31,
+};
+static uint8 blockmaprev_PSMCT16[32] = {
+ 0, 4, 1, 5,
+ 8, 12, 9, 13,
+ 2, 6, 3, 7,
+ 10, 14, 11, 15,
+ 16, 20, 17, 21,
+ 24, 28, 25, 29,
+ 18, 22, 19, 23,
+ 26, 30, 27, 31,
+};
+
+/* Suffixes used:
+ * _Px - pixels
+ * _W - width units (pixels/64)
+ * _B - blocks
+ * _P - pages
+ */
+
+/* Layout mipmaps and palette in GS memory */
+static void
+calcOffsets(int32 width_Px, int32 height_Px, int32 psm, uint64 *bufferBase_B, uint64 *bufferWidth_W, uint32 *trxpos, uint32 *totalSize, uint32 *paletteBase)
+{
+ uint32 pageWidth_Px, pageHeight_Px;
+ uint32 blockWidth_Px, blockHeight_Px;
+ uint32 mindim_Px;
+ int32 nlevels;
+ int32 n;
+ uint32 mipw_Px, miph_Px;
+ uint32 lastmipw_Px, lastmiph_Px;
+ uint32 bufferHeight_P[8];
+ uint32 bufferPage_B[8]; // address of page in which the level is allocated
+ uint32 xoff_Px, yoff_Px; // x/y offset the last level starts at
+ // Whenever we allocate horizontally inside a page,
+ // keep track of the region below it on this stack.
+ uint32 sp;
+ uint32 xoffstack_Px[8]; // actually unused...
+ uint32 widthstack_Px[8];
+ uint32 heightstack_Px[8];
+ uint32 basestack_B[8];
+ uint32 flag;
+
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ case PSMZ32:
+ case PSMZ24:
+ pageWidth_Px = 64;
+ pageHeight_Px = 32;
+ blockWidth_Px = 8;
+ blockHeight_Px = 8;
+ break;
+ case PSMT8:
+ pageWidth_Px = 128;
+ pageHeight_Px = 64;
+ blockWidth_Px = 16;
+ blockHeight_Px = 16;
+ break;
+ case PSMT4:
+ pageWidth_Px = 128;
+ pageHeight_Px = 128;
+ blockWidth_Px = 32;
+ blockHeight_Px = 16;
+ break;
+ case PSMCT16:
+ case PSMCT16S:
+ case PSMZ16:
+ case PSMZ16S:
+ default:
+ pageWidth_Px = 64;
+ pageHeight_Px = 64;
+ blockWidth_Px = 16;
+ blockHeight_Px = 8;
+ break;
+ }
+
+ mindim_Px = min(width_Px, height_Px);
+ for(nlevels = 1; mindim_Px > 8; nlevels++){
+ if(nlevels >= maxMipLevels)
+ break;
+ mindim_Px /= 2;
+ }
+
+#define PAGEWIDTH_B (pageWidth_Px/blockWidth_Px) // number of horizontal blocks per page
+#define NBLKX(dim) (NSIZE((dim), blockWidth_Px))
+#define NBLKY(dim) (NSIZE((dim), blockHeight_Px))
+#define NPGX(dim) (NSIZE((dim), pageWidth_Px))
+#define NPGY(dim) (NSIZE((dim), pageHeight_Px))
+#define REALWIDTH(w) (max((w), blockWidth_Px))
+#define REALHEIGHT(w) (max((w), blockHeight_Px))
+
+ bufferBase_B[0] = 0;
+ bufferWidth_W[0] = NPGX(width_Px)*pageWidth_Px/64;
+ bufferHeight_P[0] = NPGY(height_Px);
+ bufferPage_B[0] = 0;
+ lastmipw_Px = width_Px;
+ lastmiph_Px = height_Px;
+ sp = 0;
+ xoff_Px = 0;
+ yoff_Px = 0;
+ flag = 0;
+ // Calculate info for all mipmap levels.
+ // mipwidth/height are actually the dimensions of level n-1!
+ // This code was reversed from RW and is rather complicated...
+ // partially because it's not clear what the assumptions are,
+ // can width/height be non-powers of 2?
+ for(n = 1; n < nlevels; n++){
+ mipw_Px = lastmipw_Px/2;
+ miph_Px = lastmiph_Px/2;
+ if(lastmipw_Px >= pageWidth_Px){
+ if(lastmiph_Px >= pageHeight_Px){
+ // CASE 0
+ // We allocate full pages
+ // This is the only place bufferWidth can change. Similarly bufferBase_2, which is related
+ bufferBase_B[n] = bufferBase_B[n-1] + (lastmipw_Px/blockWidth_Px)*(lastmiph_Px/blockHeight_Px);
+ bufferPage_B[n] = bufferBase_B[n];
+ bufferWidth_W[n] = NPGX(mipw_Px)*pageWidth_Px/64;
+ bufferHeight_P[n] = NPGY(miph_Px);
+ xoff_Px = 0;
+ yoff_Px = 0;
+ }else{
+ // CASE 1
+ // Allocate vertically in the current page
+ bufferPage_B[n] = bufferPage_B[n-1];
+ bufferHeight_P[n] = bufferHeight_P[n-1];
+ bufferWidth_W[n] = bufferWidth_W[n-1];
+ // How do we know pageHeight - yoff - REALHEIGHT(lastmiph) >= miph?
+ // And how is this condition ever false?
+ // Assuming lastmipw >= pageWidth for any number of levels, lastmiph must be pageHeight/2
+ // or lower to reach this code. No dimension is lower than 8. Then consequent mipmaps
+ // will have heights halved but even with PSMT4 we will only (vertically) fill the
+ // page with the last mipmap and not go beyond...
+ if(REALHEIGHT(lastmiph_Px) + yoff_Px < pageHeight_Px){
+ // CASE 2
+ yoff_Px += REALHEIGHT(lastmiph_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] +
+ PAGEWIDTH_B * NBLKY(lastmiph_Px) *
+ bufferWidth_W[n]*64/pageWidth_Px; // number of horizontal pages for level
+ }else{
+ // CASE 3
+ // Can this happen?
+ xoff_Px += REALWIDTH(lastmipw_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + NBLKX(lastmipw_Px);
+ }
+ }
+ }else if(lastmiph_Px >= pageHeight_Px){
+ // CASE 4
+ // Allocate horizontally
+ bufferPage_B[n] = bufferPage_B[n-1];
+ bufferHeight_P[n] = bufferHeight_P[n-1];
+ bufferWidth_W[n] = bufferWidth_W[n-1];
+ if(REALWIDTH(lastmipw_Px) + xoff_Px < pageWidth_Px){
+ // CASE 5
+ xoffstack_Px[sp] = xoff_Px; // unused...
+ heightstack_Px[sp] = REALHEIGHT(lastmiph_Px);
+ widthstack_Px[sp] = REALWIDTH(lastmipw_Px);
+ basestack_B[sp] = bufferBase_B[n-1] + NBLKY(lastmiph_Px) * PAGEWIDTH_B;
+ sp++;
+ xoff_Px += REALWIDTH(lastmipw_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + NBLKX(lastmipw_Px);
+ }else if(sp){
+ // CASE 7
+ bufferBase_B[n] = basestack_B[sp-1];
+ if(REALWIDTH(mipw_Px) < widthstack_Px[sp-1]){
+ // CASE 9
+ basestack_B[sp-1] += NBLKX(mipw_Px);
+ widthstack_Px[sp-1] -= REALWIDTH(mipw_Px);
+ }else if(REALHEIGHT(miph_Px) < heightstack_Px[sp-1]){
+ // CASE 8
+ basestack_B[sp-1] += NBLKY(miph_Px) * PAGEWIDTH_B;
+ heightstack_Px[sp-1] -= REALHEIGHT(miph_Px);
+ }else{
+ // CASE 10
+ sp--;
+ }
+ flag = 1;
+ }else{
+ // CASE 6
+ yoff_Px += REALHEIGHT(lastmiph_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + PAGEWIDTH_B*NBLKY(lastmiph_Px);
+ }
+ }else{
+ // CASE 11
+ bufferHeight_P[n] = bufferHeight_P[n-1];
+ bufferPage_B[n] = bufferPage_B[n-1];
+ bufferWidth_W[n] = bufferWidth_W[n-1];
+ if(REALWIDTH(lastmipw_Px) + xoff_Px < bufferWidth_W[n-1]*64){
+ // CASE 12
+ xoffstack_Px[sp] = xoff_Px; // unused...
+ widthstack_Px[sp] = REALWIDTH(lastmipw_Px);
+ heightstack_Px[sp] = REALHEIGHT(lastmiph_Px);
+ basestack_B[sp] = bufferBase_B[n-1] + PAGEWIDTH_B * NBLKY(lastmiph_Px);
+ sp++;
+ xoff_Px += REALWIDTH(lastmipw_Px);
+ bufferBase_B[n] = bufferBase_B[n-1] + NBLKX(lastmipw_Px);
+ }else if(REALHEIGHT(lastmiph_Px) + yoff_Px < pageHeight_Px*bufferHeight_P[n] && flag == 0){
+ // CASE 13
+ bufferBase_B[n] = bufferBase_B[n-1] + PAGEWIDTH_B * NBLKY(lastmiph_Px);
+ yoff_Px += blockHeight_Px ? lastmiph_Px : 0; // how exactly can blockHeight be 0?? This looks wrong...
+ flag = n;
+ }else{
+ // CASE 14
+ bufferBase_B[n] = basestack_B[sp-1];
+ if(REALWIDTH(mipw_Px) < widthstack_Px[sp-1]){
+ // CASE 15
+ basestack_B[sp-1] += NBLKX(mipw_Px);
+ widthstack_Px[sp-1] -= REALWIDTH(mipw_Px);
+ }else if(REALHEIGHT(miph_Px) < heightstack_Px[sp-1]){
+ // CASE 16
+ basestack_B[sp-1] += PAGEWIDTH_B * NBLKY(miph_Px);
+ heightstack_Px[sp-1] -= REALHEIGHT(miph_Px);
+ }else{
+ // CASE 17
+ sp--;
+ }
+ }
+ }
+ lastmipw_Px = mipw_Px;
+ lastmiph_Px = miph_Px;
+ }
+
+ // Calculate position of palette.
+ uint32 paletteBase_B = 0;
+ uint64 bufwidth_Px = bufferWidth_W[nlevels-1]*64;
+ uint64 bufheight_Px = bufferHeight_P[nlevels-1]*pageHeight_Px;
+ // != means > really
+ if(bufwidth_Px != lastmipw_Px || bufheight_Px != lastmiph_Px){
+ if(psm == PSMT8){
+ // 2x2 blocks at the end of the page (even for PSMCT16S)
+ paletteBase_B = bufferPage_B[nlevels-1] +
+ ((bufwidth_Px/pageWidth_Px)*bufferHeight_P[nlevels-1] << 5) // total number of blocks
+ - (bufheight_Px/pageWidth_Px) * PAGEWIDTH_B // one block up
+ - 2; // two blocks left
+ }else if(psm == PSMT4){
+ // One block at the end of the page
+ paletteBase_B = bufferPage_B[nlevels-1] +
+ ((bufwidth_Px/pageWidth_Px) * bufferHeight_P[nlevels-1] << 5)
+ - 1;
+ }
+ }else{
+ if(psm == PSMT8 || psm == PSMT4){
+ paletteBase_B = bufferPage_B[nlevels-1] +
+ (bufwidth_Px/blockWidth_Px) * (bufheight_Px/blockHeight_Px);
+ }
+ }
+
+ uint32 bufwidth_W = bufferWidth_W[0];
+ uint32 bufpage_B = bufferPage_B[0];
+ uint32 pixeloff;
+ for(n = 0; n < nlevels; n++){
+ // Calculate TRXPOS register (DSAX and DSAY, shifted up later)
+ // Start of buffer on current page (x in pixels, y in blocks)
+ pixeloff = (bufferBase_B[n] - bufpage_B) * blockWidth_Px;
+ // y coordinate of first pixel
+ yoff_Px = (pixeloff / (bufwidth_W*64)) * blockHeight_Px;
+ // x coordinate of first pixel
+ xoff_Px = pixeloff % (bufwidth_W*64);
+ if(bufferWidth_W[n] == bufwidth_W &&
+ // Not quite sure what's the meaning of this.
+ // DSAY is 11 bits, but so is DSAX and it is not checked?
+ yoff_Px < 0x800){
+ trxpos[n] = yoff_Px<<16 | xoff_Px;
+ }else{
+ bufwidth_W = bufferWidth_W[n];
+ bufpage_B = bufferPage_B[n];
+ trxpos[n] = 0;
+ }
+
+ // If using more than one page we have to swizzle rows inside page rows
+ if(bufwidth_W*64 / pageWidth_Px > 1){
+ uint32 bufpagestride_B = bufwidth_W*64 * 32 / pageWidth_Px; // one row of pages
+ uint32 bufwidth_B = bufwidth_W*64 / blockWidth_Px; // one row of blocks
+ // To illustrate assume:
+ // - 8x4 block pages
+ // - texture is 4 pages wide
+ // Then the lower bits of an input block address look like: RRRPPCC
+ // where the C bits are the block's column inside a page
+ // the P bits are the block's page horizontally
+ // the R bits are the block's row in a row of pages
+ // We want to swap P and R: PPRRRCC
+ bufferBase_B[n] =
+ (bufferBase_B[n] & ~((uint64)bufpagestride_B - PAGEWIDTH_B)) // mask out R and P
+ | ((bufferBase_B[n] & (bufwidth_B - PAGEWIDTH_B)) * (bufpagestride_B/bufwidth_B)) // extract P and shift left
+ | ((bufferBase_B[n] & (bufpagestride_B - bufwidth_B)) / (bufwidth_B/PAGEWIDTH_B)); // extract R and shift right
+ }
+
+ // Always have to swizzle blocks inside pages. We use a lookup, RW does bit operations
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ // ABCDE -> CADBE
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMCT32[bufferBase_B[n]&0x1F];
+ break;
+ case PSMT4:
+ case PSMCT16:
+ // ABCDE -> ADBEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMCT16[bufferBase_B[n]&0x1F];
+ break;
+ case PSMCT16S:
+ // ABCDE -> DBAEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMCT16S[bufferBase_B[n]&0x1F];
+ break;
+ case PSMZ32:
+ case PSMZ24:
+ // ABCDE -> ~C~ADBE
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMZ32[bufferBase_B[n]&0x1F];
+ break;
+ case PSMZ16:
+ // ABCDE -> ~A~DBEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMZ16[bufferBase_B[n]&0x1F];
+ break;
+ case PSMZ16S:
+ // ABCDE -> ~D~BAEC
+ bufferBase_B[n] = (bufferBase_B[n]&~0x1F) | (uint64)blockmap_PSMZ16S[bufferBase_B[n]&0x1F];
+ break;
+ default: break;
+ }
+ }
+
+ // Same dance as above, with the palette
+ if(bufwidth_W*64 / pageWidth_Px > 1){
+ uint32 bufpagestride_B = bufwidth_W*64 * 32 / pageWidth_Px; // one row of pages
+ uint32 bufwidth_B = bufwidth_W*64 / blockWidth_Px; // one row of blocks
+ paletteBase_B =
+ (paletteBase_B & ~(bufpagestride_B - PAGEWIDTH_B)) // mask out R and P
+ | ((paletteBase_B & (bufwidth_B - PAGEWIDTH_B)) * (bufpagestride_B/bufwidth_B)) // extract P and shift left
+ | ((paletteBase_B & (bufpagestride_B - bufwidth_B)) / (bufwidth_B/PAGEWIDTH_B)); // extract R and shift right
+ }
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8:
+ case PSMT8H:
+ case PSMT4HL:
+ case PSMT4HH:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMCT32[paletteBase_B&0x1F];
+ break;
+ case PSMT4:
+ case PSMCT16:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMCT16[paletteBase_B&0x1F];
+ break;
+ case PSMCT16S:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMCT16S[paletteBase_B&0x1F];
+ break;
+ case PSMZ32:
+ case PSMZ24:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMZ32[paletteBase_B&0x1F];
+ break;
+ case PSMZ16:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMZ16[paletteBase_B&0x1F];
+ break;
+ case PSMZ16S:
+ paletteBase_B = (paletteBase_B&~0x1F) | (uint64)blockmap_PSMZ16S[paletteBase_B&0x1F];
+ break;
+ default: break;
+ }
+ *paletteBase = paletteBase_B;
+ *totalSize = bufferPage_B[nlevels-1] + // start of last buffer`
+ bufferWidth_W[nlevels-1]*64/blockWidth_Px * // number of horizontal blocks in last level
+ pageHeight_Px*bufferHeight_P[nlevels-1]/blockHeight_Px; // number of vertical blocks in last level
+ *totalSize *= 64; // to words
+
+#undef BLKSTRIDE
+#undef NBLKX
+#undef NBLKY
+#undef NPGX
+#undef NPGY
+#undef REALWIDTH
+#undef REALHEIGHT
+}
+
+static Raster*
+rasterCreateTexture(Raster *raster)
+{
+ // We use a map for fast lookup, even for impossible depths
+ static int32 pageWidths[32] = {
+ 128, 128, 128, 128,
+ 128, 128, 128, 128,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ };
+ static int32 pageHeights[32] = {
+ 128, 128, 128, 128,
+ 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32,
+ };
+ enum {
+ TCC_RGBA = 1 << 2,
+ CLD_1 = 1 << 29,
+
+ WD2BLK = 64, // words per block
+ WD2PG = 2048, // words per page
+ };
+ int32 pixelformat, palformat;
+ // TEX0 fields (not all)
+ int64 tbw = 0; // texture buffer width, texels/64
+ int64 psm = 0; // pixel storage mode
+ int64 tw = 0; // texture width exponent, width = 2^tw
+ int64 th = 0; // texture height exponent, height = 2^th
+ int64 tcc = 0; // texture color component, 0 = rgb, 1 = rgba
+ int64 cpsm = 0; // CLUT pixel storage mode
+ int64 cld = 0; // CLUT buffer load control
+
+ uint64 bufferWidth[7]; // in number of pixels / 64
+ uint64 bufferBase[7]; // block address
+ uint32 trxpos_hi[8];
+ int32 width, height, depth;
+ int32 pageWidth, pageHeight;
+ int32 paletteWidth, paletteHeight, paletteDepth;
+ int32 palettePagewidth, palettePageheight;
+
+
+ Ps2Raster *ras = GETPS2RASTEREXT(raster);
+ pixelformat = raster->format & 0xF00;
+ palformat = raster->format & 0x6000;
+ width = raster->width;
+ height = raster->height;
+ depth = raster->depth;
+ // RW's code does not seem to quite work with 24 bit rasters
+ // so make sure we're not generating them for safety
+ assert(depth != 24);
+
+ ras->flags = 0;
+ ras->data = nil;
+ ras->dataSize = 0;
+
+ // RW doesn't seem to check this, hm...
+ if(raster->flags & Raster::DONTALLOCATE)
+ return raster;
+
+ //printf("%x %x %x %x\n", raster->format, raster->flags, raster->type, noNewStyleRasters);
+ pageWidth = pageWidths[depth-1];
+ pageHeight = pageHeights[depth-1];
+
+ int32 s;
+ tw = 0;
+ for(s = 1; s < width; s *= 2)
+ tw++;
+ th = 0;
+ for(s = 1; s < height; s *= 2)
+ th++;
+ ras->kl = defaultMipMapKL;
+ // unk2[0] = 1
+ //printf("%d %d %d %d\n", raster->width, logw, raster->height, logh);
+
+ // round up to page width, set TBW, TW, TH
+ tbw = max(width,pageWidth)/64;
+
+ // set PSM, TCC, CLD, CPSM and figure out palette format
+ if(palformat){
+ if(palformat == Raster::PAL8){
+ psm = PSMT8;
+ paletteWidth = 16;
+ paletteHeight = 16;
+ }else if(palformat == Raster::PAL4){
+ psm = PSMT4;
+ paletteWidth = 8;
+ paletteHeight = 2;
+ }else{
+ // can't happen, sanity check in getRasterFormat
+ return nil;;
+ }
+ tcc = 1; // RGBA
+ cld = 1;
+ if(pixelformat == Raster::C1555){
+ paletteDepth = 2;
+ cpsm = PSMCT16S;
+ palettePagewidth = 64;
+ palettePageheight = 64;
+ }else if(pixelformat == Raster::C8888){
+ paletteDepth = 4;
+ cpsm = PSMCT32;
+ palettePagewidth = 64;
+ palettePageheight = 32;
+ }else
+ // can't happen, sanity check in getRasterFormat
+ return nil;;
+ }else{
+ paletteWidth = 0;
+ paletteHeight = 0;
+ paletteDepth = 0;
+ palettePagewidth = 0;
+ palettePageheight = 0;
+ if(pixelformat == Raster::C8888){
+ psm = PSMCT32;
+ tcc = 1; // RGBA
+ }else if(pixelformat == Raster::C888){
+ psm = PSMCT24;
+ tcc = 0; // RGB
+ }else if(pixelformat == Raster::C1555){
+ psm = PSMCT16S;
+ tcc = 1; // RGBA
+ }else
+ // can't happen, sanity check in getRasterFormat
+ return nil;;
+ }
+
+ for(int i = 0; i < 7; i++){
+ bufferWidth[i] = 1;
+ bufferBase[i] = 0;
+ }
+
+ int32 mipw, miph;
+ int32 w, h;
+ int32 n;
+ int32 nPagW, nPagH;
+ raster->stride = width*depth/8;
+
+ if(raster->format & Raster::MIPMAP){
+ // NOTE: much of this code seems to be totally useless.
+ // calcOffsets overwrites what we calculate here. I wonder
+ // why this code even is in RW. Maybe it's older code that used
+ // the GS' automatic base pointer calculation?
+
+ // see the left columns in the maps above
+ static uint32 blockOffset32_24_8[8] = { 0, 2, 2, 8, 8, 10, 10, 32 };
+ static uint32 blockOffset16_4[8] = { 0, 1, 4, 5, 16, 17, 20, 21 };
+ static uint32 blockOffset16S[8] = { 0, 1, 8, 9, 4, 5, 12, 13 };
+ uint64 lastBufferWidth;
+ mipw = width;
+ miph = height;
+ lastBufferWidth = max(pageWidth, width)/64;
+ ras->pixelSize = 0;
+ int32 lastaddress = 0; // word address
+ int32 nextaddress = 0; // word address
+ int32 stride; // in bytes
+ for(n = 0; mipw != 0 && miph != 0 && n < maxMipLevels; n++){
+ if(width >= 8 && height >= 8 && (mipw < 8 || miph < 8))
+ break;
+ ras->pixelSize += ALIGN64(mipw*miph*depth/8);
+ bufferWidth[n] = max(pageWidth, mipw)/64;
+ stride = bufferWidth[n]*64*depth/8;
+
+ // If buffer width changes, align next address to page
+ if(bufferWidth[n] != lastBufferWidth){
+ nPagW = ((width >> (n-1)) + pageWidth-1)/pageWidth;
+ nPagH = ((height >> (n-1)) + pageHeight-1)/pageHeight;
+ nextaddress = (lastaddress + nPagW*nPagH*WD2PG) & ~(WD2PG-1);
+ }
+ lastBufferWidth = bufferWidth[n];
+ nextaddress = ALIGN64(nextaddress); // this should already be the case...
+ uint32 b = nextaddress>>(11-3) & 7; // upper three bits of block-in-page address
+ switch(psm){
+ case PSMCT32:
+ case PSMCT24:
+ case PSMT8:
+ b = blockOffset32_24_8[b];
+ break;
+ case PSMCT16:
+ case PSMT4:
+ b = blockOffset16_4[b];
+ break;
+ case PSMCT16S:
+ b = blockOffset16S[b];
+ break;
+ default:
+ // can't happen
+ break;
+ }
+ // shift to page address, then to block address and add offset inside page
+ bufferBase[n] = b + (nextaddress>>11 << 5);
+
+ lastaddress = nextaddress;
+ nextaddress = ALIGN64(miph*stride/4 + lastaddress);
+
+ mipw /= 2;
+ miph /= 2;
+ }
+
+ // Do the real work here
+ uint32 paletteBase;
+ uint32 totalSize;
+ calcOffsets(width, height, psm, bufferBase, bufferWidth, trxpos_hi, &totalSize, &paletteBase);
+
+ ras->paletteSize = paletteWidth*paletteHeight*paletteDepth;
+ ras->miptbp1 =
+ bufferWidth[1]<<14 | (bufferBase[1] & 0x3FFF)<<0
+ | bufferWidth[2]<<34 | (bufferBase[2] & 0x3FFF)<<20
+ | bufferWidth[3]<<54 | (bufferBase[3] & 0x3FFF)<<40;
+ ras->miptbp2 =
+ bufferWidth[4]<<14 | (bufferBase[4] & 0x3FFF)<<0
+ | bufferWidth[5]<<34 | (bufferBase[5] & 0x3FFF)<<20
+ | bufferWidth[6]<<54 | (bufferBase[6] & 0x3FFF)<<40;
+ ras->tex1low = (n-1)<<2;
+ ras->totalSize = totalSize;
+ if(ras->paletteSize){
+ ras->paletteBase = paletteBase;
+ if(ras->paletteBase*64 == ras->totalSize)
+ ras->totalSize += WD2PG;
+ }else
+ ras->paletteBase = 0;
+ }else{
+ // No mipmaps
+
+ ras->pixelSize = ALIGN16(raster->stride*raster->height);
+ ras->paletteSize = paletteWidth*paletteHeight*paletteDepth;
+ ras->miptbp1 = 1ULL<<54 | 1ULL<<34 | 1ULL<<14;
+ ras->miptbp2 = 1ULL<<54 | 1ULL<<34 | 1ULL<<14;
+ ras->tex1low = 0; // one mipmap level
+
+ // find out number of pages needed
+ nPagW = (width + pageWidth-1)/pageWidth;
+ nPagH = (height + pageHeight-1)/pageHeight;
+
+ // calculate buffer width in units of pixels/64
+ bufferBase[0] = 0;
+ trxpos_hi[0] = 0;
+ bufferWidth[0] = nPagW*pageWidth / 64;
+
+ // calculate whole buffer size in words
+ ras->totalSize = nPagW*nPagH*WD2PG;
+
+ // calculate palette offset on GS in units of words/64
+ if(ras->paletteSize){
+ // Maximum palette size is 256 words.
+ // If there is still room, use it!
+ // If dimensions don't fill a page, we have at least
+ // half a page left, enough for any palette
+//TODO: this was not always done it seems but even gta3's 3.1 seems to??
+ if(pageWidth*nPagW > width ||
+ pageHeight*nPagH > height){
+ ras->paletteBase = (ras->totalSize - 256) / WD2BLK;
+ }else{
+ // Otherwise allocate more space...
+ ras->paletteBase = ras->totalSize / WD2BLK;
+ // ...using the same calculation as above.
+ // WHY? we never need more than one page!
+ nPagW = (paletteWidth + palettePagewidth-1)/palettePagewidth;
+ nPagH = (paletteHeight + palettePageheight-1)/palettePageheight;
+ ras->totalSize += nPagW*nPagH*WD2PG;
+ }
+ }else
+ ras->paletteBase = 0;
+ }
+ ras->tex0 = tbw << 14 |
+ psm << 20 |
+ tw << 26 |
+ th << 30 |
+ tcc << 34 |
+ cpsm << 51 |
+ 0ULL << 55 | // csm0
+ 0ULL << 56 | // entry offset
+ cld << 61;
+
+
+
+ // allocate data and fill with GIF packets
+ ras->pixelSize = ALIGN16(ras->pixelSize);
+ int32 numLevels = MAXLEVEL(ras)+1;
+ // No GIF packet because we either don't want it (pre 0x310 rasters)
+ // or the data wouldn't fit into a DMA packet
+ if(noNewStyleRasters ||
+ (raster->width*raster->height*raster->depth/8/0x10) >= 0x7FFF){
+ ras->dataSize = ras->paletteSize+ras->pixelSize;
+ uint8 *data = (uint8*)mallocalign(ras->dataSize, 0x40);
+ assert(data);
+ ras->data = data;
+ raster->pixels = data;
+ if(ras->paletteSize)
+ raster->palette = data + ras->pixelSize;
+ if(raster->depth == 8)
+ ras->flags |= Ps2Raster::SWIZZLED8;
+ }else{
+ ras->flags |= Ps2Raster::NEWSTYLE;
+ uint64 paltrxpos = 0;
+ uint32 dsax = trxpos_hi[numLevels-1] & 0x7FF;
+ uint32 dsay = trxpos_hi[numLevels-1]>>16 & 0x7FF;
+ // Set swizzle flags and calculate TRXPOS for palette
+ if(psm == PSMT8){
+ ras->flags |= Ps2Raster::SWIZZLED8;
+ if(cpsm == PSMCT32 && bufferWidth[numLevels-1] == 2){ // one page
+ // unswizzle the starting block of the last buffer and palette
+ uint32 bufbase_B = (bufferBase[numLevels-1]&~0x1F) | (uint64)blockmaprev_PSMCT32[bufferBase[numLevels-1]&0x1F];
+ uint32 palbase_B = (ras->paletteBase&~0x1F) | (uint64)blockmaprev_PSMCT32[ras->paletteBase&0x1F];
+ // find start of page of last level (16,16 are PSMT8 block dimensions)
+ uint32 page_B = bufbase_B - 8*(dsay/16) - dsax/16;
+ // find palette DSAX/Y (in PSMCT32!)
+ dsay = (palbase_B - page_B)/8 * 8; // block/blocksPerPageX * blockHeight
+ dsax = (palbase_B - page_B)*8 % 64; // block*blockWidth % pageWidth
+ if(dsay < 0x800)
+ paltrxpos = dsay<<16 | dsax;
+ }
+ }
+ if(psm == PSMT4){
+ // swizzle flag depends on version :/
+ // but which version? ....
+ if(rw::version > 0x31000){
+ ras->flags |= Ps2Raster::SWIZZLED4;
+ // Where can this come from? if anything we're using PSMCT16S
+ // Looks like they wanted to swizzle palettes too...
+ if(cpsm == PSMCT16){
+ // unswizzle the starting block of the last buffer and palette
+ uint32 bufbase_B = (bufferBase[numLevels-1]&~0x1F) | (uint64)blockmaprev_PSMCT16[bufferBase[numLevels-1]&0x1F];
+ uint32 palbase_B = (ras->paletteBase&~0x1F) | (uint64)blockmaprev_PSMCT16[ras->paletteBase&0x1F];
+ // find start of page of last level (32,16 are PSMT4 block dimensions)
+ uint32 page_B = bufbase_B - 4*(dsay/32) - dsax/16;
+ // find palette DSAX/Y (in PSMCT16!)
+ dsay = (palbase_B - page_B)/4 * 8; // block/blocksPerPageX * blockHeight
+ dsax = (palbase_B - page_B)*16 % 128; // block*blockWidth % pageWidth
+ if(dsay < 0x800)
+ paltrxpos = dsay<<16 | dsax;
+ }
+ }
+ }
+ ras->pixelSize = 0x50*numLevels; // GIF packets
+ int32 minW, minH;
+ transferMinSize(psm, ras->flags, &minW, &minH);
+ w = raster->width;
+ h = raster->height;
+ n = numLevels;
+ while(n--){
+ mipw = max(w, minW);
+ miph = max(h, minH);
+ ras->pixelSize += ALIGN16(mipw*miph*raster->depth/8);
+ w /= 2;
+ h /= 2;
+ }
+ if(ras->paletteSize){
+ if(rw::version > 0x31000 && paletteHeight == 2)
+ paletteHeight = 3;
+ ras->paletteSize = 0x50 +
+ paletteDepth*paletteWidth*paletteHeight;
+ }
+ // One transfer per buffer width, 4 qwords:
+ // DMAcnt(2) [NOP, DIRECT]
+ // GIF tag A+D
+ // BITBLTBUF
+ // DMAref(pixel data) [NOP, DIRECT]
+ uint32 extrasize = 0x10; // PixelPtr
+ int32 numTransfers = 0;
+ for(n = 0; n < numLevels; n++)
+ if(trxpos_hi[n] == 0){
+ extrasize += 0x40;
+ numTransfers++;
+ }
+ if(ras->paletteSize){
+ extrasize += 0x40;
+ numTransfers++;
+ }
+ // What happens here?
+ if(ras->paletteSize && paltrxpos == 0)
+ ras->dataSize = ALIGN(ras->pixelSize,128) + ALIGN(ras->paletteSize,64) + extrasize + 0x70;
+ else
+ ras->dataSize = ALIGN(ras->paletteSize+ras->pixelSize,64) + extrasize + 0x70;
+ uint8 *data = (uint8*)mallocalign(ras->dataSize, 0x40);
+ uint32 *xferchain = (uint32*)(data + 0x10);
+ assert(data);
+ ras->data = data;
+ Ps2Raster::PixelPtr *pp = (Ps2Raster::PixelPtr*)data;
+ pp->numTransfers = numTransfers;
+ pp->numTotalTransfers = numTransfers;
+ pp->pixels = (uint8*)ALIGN((uintptr)data + extrasize, 128);
+ raster->pixels = (uint8*)pp;
+ if(ras->paletteSize)
+ raster->palette = pp->pixels + ALIGN(ras->pixelSize, 128) + 0x50;
+ uint32 *p = (uint32*)pp->pixels;
+ w = raster->width;
+ h = raster->height;
+ for(n = 0; n < numLevels; n++){
+ mipw = max(w, minW);
+ miph = max(h, minH);
+
+ // GIF tag
+ *p++ = 3; // NLOOP = 3
+ *p++ = 0x10000000; // NREG = 1
+ *p++ = 0xE; // A+D
+ *p++ = 0;
+
+ // TRXPOS
+ if((ras->flags & Ps2Raster::SWIZZLED8 && psm == PSMT8) ||
+ (ras->flags & Ps2Raster::SWIZZLED4 && psm == PSMT4)){
+ *p++ = 0; // SSAX/Y is always 0
+ *p++ = (trxpos_hi[n] & ~0x10001)/2; // divide both DSAX/Y by 2
+ }else{
+ *p++ = 0;
+ *p++ = trxpos_hi[n];
+ }
+ *p++ = 0x51;
+ *p++ = 0;
+
+ // TRXREG
+ if((ras->flags & Ps2Raster::SWIZZLED8 && psm == PSMT8) ||
+ (ras->flags & Ps2Raster::SWIZZLED4 && psm == PSMT4)){
+ *p++ = mipw/2;
+ *p++ = miph/2;
+ }else{
+ *p++ = mipw;
+ *p++ = miph;
+ }
+ *p++ = 0x52;
+ *p++ = 0;
+
+ // TRXDIR
+ *p++ = 0; // host -> local
+ *p++ = 0;
+ *p++ = 0x53;
+ *p++ = 0;
+
+ // GIF tag
+ uint32 sz = ALIGN16(mipw*miph*raster->depth/8)/16;
+ *p++ = sz & 0x7FFF;
+ *p++ = 0x08000000; // IMAGE
+ *p++ = 0;
+ *p++ = 0;
+
+ if(trxpos_hi[n] == 0){
+ // Add a transfer, see above for layout
+
+ *xferchain++ = 0x10000002; // DMAcnt, 2 qwords
+ *xferchain++ = 0;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000002; // VIF DIRECT 2 qwords
+
+ // GIF tag
+ *xferchain++ = 1; // NLOOP = 1
+ *xferchain++ = 0x10000000; // NREG = 1
+ *xferchain++ = 0xE; // A+D
+ *xferchain++ = 0;
+
+ // BITBLTBUF
+ if(ras->flags & Ps2Raster::SWIZZLED8 && psm == PSMT8){
+ // PSMT8 is swizzled to PSMCT32 and dimensions are halved
+ *xferchain++ = PSMCT32<<24 | bufferWidth[n]/2<<16; // src buffer
+ *xferchain++ = PSMCT32<<24 | bufferWidth[n]/2<<16 | bufferBase[n]; // dst buffer
+ }else if(ras->flags & Ps2Raster::SWIZZLED4 && psm == PSMT4){
+ // PSMT4 is swizzled to PSMCT16 and dimensions are halved
+ *xferchain++ = PSMCT16<<24 | bufferWidth[n]/2<<16; // src buffer
+ *xferchain++ = PSMCT16<<24 | bufferWidth[n]/2<<16 | bufferBase[n]; // dst buffer
+ }else{
+ *xferchain++ = psm<<24 | bufferWidth[n]<<16; // src buffer
+ *xferchain++ = psm<<24 | bufferWidth[n]<<16 | bufferBase[n]; // dst buffer
+ }
+ *xferchain++ = 0x50;
+ *xferchain++ = 0;
+
+ *xferchain++ = 0x30000000 | sz+5; // DMAref
+ // this obviously only works with 32 bit pointers, but it's only needed on the PS2 anyway
+ *xferchain++ = (uint32)(uintptr)p - 0x50;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000000 | sz+5; // VIF DIRECT
+ }else{
+ // Add to existing transfer
+ xferchain[-4] = 0x30000000 | (xferchain[-4]&0xFFFF) + sz+5; // last DMAref
+ xferchain[-1] = 0x50000000 | (xferchain[-1]&0xFFFF) + sz+5; // last DIRECT
+ }
+
+ p += sz*4;
+ w /= 2;
+ h /= 2;
+ }
+
+ if(ras->paletteSize){
+ // huh?
+ if(paltrxpos)
+ raster->palette = (uint8*)p + 0x50;
+ p = (uint32*)(raster->palette - 0x50);
+ // GIF tag
+ *p++ = 3; // NLOOP = 3
+ *p++ = 0x10000000; // NREG = 1
+ *p++ = 0xE; // A+D
+ *p++ = 0;
+
+ // TRXPOS
+ *(uint64*)p = (uint64)paltrxpos<<32;
+ p += 2;
+ *p++ = 0x51;
+ *p++ = 0;
+
+ // TRXREG
+ *p++ = paletteWidth;
+ *p++ = paletteHeight;
+ *p++ = 0x52;
+ *p++ = 0;
+
+ // TRXDIR
+ *p++ = 0; // host -> local
+ *p++ = 0;
+ *p++ = 0x53;
+ *p++ = 0;
+
+ // GIF tag
+ uint32 sz = ALIGN16(ras->paletteSize - 0x50)/16;
+ *p++ = sz & 0x7FFF;
+ *p++ = 0x08000000; // IMAGE
+ *p++ = 0;
+ *p++ = 0;
+
+ // Transfer
+ *xferchain++ = 0x10000002; // DMAcnt, 2 qwords
+ *xferchain++ = 0;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000002; // VIF DIRECT 2 qwords
+
+ // GIF tag
+ *xferchain++ = 1; // NLOOP = 1
+ *xferchain++ = 0x10000000; // NREG = 1
+ *xferchain++ = 0xE; // A+D
+ *xferchain++ = 0;
+
+ // BITBLTBUF
+ if(paltrxpos == 0){
+ *xferchain++ = cpsm<<24 | 1<<16; // src buffer
+ *xferchain++ = cpsm<<24 | 1<<16 | ras->paletteBase; // dst buffer
+ *xferchain++ = 0x50;
+ *xferchain++ = 0;
+ }else{
+ // copy last pixel bitbltbuf...if uploading palette separately it's still the same buffer
+ xferchain[0] = xferchain[-16];
+ xferchain[1] = xferchain[-15];
+ xferchain[2] = xferchain[-14];
+ xferchain[3] = xferchain[-13];
+ xferchain += 4;
+ // Add to last transfer
+ xferchain[-16] = 0x30000000 | (xferchain[-16]&0xFFFF) + sz+5; // last DMAref
+ xferchain[-13] = 0x50000000 | (xferchain[-13]&0xFFFF) + sz+5; // last DIRECT
+ pp->numTransfers--;
+ }
+
+ *xferchain++ = 0x30000000 | sz+5; // DMAref
+ // this obviously only works with 32 bit pointers, but it's only needed on the PS2 anyway
+ *xferchain++ = (uint32)(uintptr)p - 0x50;
+ *xferchain++ = 0; // VIF nop
+ *xferchain++ = 0x50000000 | sz+5; // VIF DIRECT
+ }
+ }
+ raster->originalPixels = raster->pixels;
+ raster->originalStride = raster->stride;
+ if(ras->flags & Ps2Raster::NEWSTYLE)
+ raster->pixels = ((Ps2Raster::PixelPtr*)raster->pixels)->pixels + 0x50;
+ return raster;
+}
+
+Raster*
+rasterCreate(Raster *raster)
+{
+ if(!getRasterFormat(raster))
+ return nil;
+
+ // init raster
+ raster->pixels = nil;
+ raster->palette = nil;
+ raster->originalWidth = raster->width;
+ raster->originalHeight = raster->height;
+ raster->originalPixels = raster->pixels;
+ if(raster->width == 0 || raster->height == 0){
+ raster->flags = Raster::DONTALLOCATE;
+ raster->stride = 0;
+ raster->originalStride = 0;
+ return raster;
+ }
+
+ switch(raster->type){
+ case Raster::NORMAL:
+ case Raster::TEXTURE:
+ return rasterCreateTexture(raster);
+ case Raster::ZBUFFER:
+ // TODO. only RW_PS2
+ // get info from video mode
+ raster->flags = Raster::DONTALLOCATE;
+ return raster;
+ case Raster::CAMERA:
+ // TODO. only RW_PS2
+ // get info from video mode
+ raster->flags = Raster::DONTALLOCATE;
+ return raster;
+ case Raster::CAMERATEXTURE:
+ // TODO. only RW_PS2
+ // check width/height and fall through to texture
+ return nil;
+ }
+ return nil;
+}
+
+static uint32
+swizzle(uint32 x, uint32 y, uint32 logw)
+{
+#define X(n) ((x>>(n))&1)
+#define Y(n) ((y>>(n))&1)
+
+ uint32 nx, ny, n;
+ x ^= (Y(1)^Y(2))<<2;
+ nx = (x&7) | ((x>>1)&~7);
+ ny = (y&1) | ((y>>1)&~1);
+ n = Y(1) | X(3)<<1;
+ return n | nx<<2 | ny<<(logw-1+2);
+}
+
+void
+unswizzleRaster(Raster *raster)
+{
+ uint8 tmpbuf[1024*4]; // 1024x4px, maximum possible width
+ uint32 mask;
+ int32 x, y, w, h;
+ int32 i;
+ int32 logw;
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ uint8 *px;
+
+ if((raster->format & (Raster::PAL4|Raster::PAL8)) == 0)
+ return;
+
+ int minw, minh;
+ transferMinSize(raster->format & Raster::PAL4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ w = max(raster->width, minw);
+ h = max(raster->height, minh);
+ px = raster->pixels;
+ logw = 0;
+ for(i = 1; i < w; i *= 2) logw++;
+ mask = (1<<(logw+2))-1;
+
+ if(raster->format & Raster::PAL4 && natras->flags & Ps2Raster::SWIZZLED4){
+ for(y = 0; y < h; y += 4){
+ memcpy(tmpbuf, &px[y<<(logw-1)], 2*w);
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ uint8 c = s & 1 ? tmpbuf[s>>1] >> 4 : tmpbuf[s>>1] & 0xF;
+ px[a>>1] = a & 1 ? (px[a>>1]&0xF) | c<<4 : (px[a>>1]&0xF0) | c;
+ }
+ }
+ }else if(raster->format & Raster::PAL8 && natras->flags & Ps2Raster::SWIZZLED8){
+ for(y = 0; y < h; y += 4){
+ memcpy(tmpbuf, &px[y<<logw], 4*w);
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ px[a] = tmpbuf[s];
+ }
+ }
+ }
+}
+
+void
+swizzleRaster(Raster *raster)
+{
+ uint8 tmpbuf[1024*4]; // 1024x4px, maximum possible width
+ uint32 mask;
+ int32 x, y, w, h;
+ int32 i;
+ int32 logw;
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ uint8 *px;
+
+ if((raster->format & (Raster::PAL4|Raster::PAL8)) == 0)
+ return;
+
+ int minw, minh;
+ transferMinSize(raster->format & Raster::PAL4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ w = max(raster->width, minw);
+ h = max(raster->height, minh);
+ px = raster->pixels;
+ logw = 0;
+ for(i = 1; i < raster->width; i *= 2) logw++;
+ mask = (1<<(logw+2))-1;
+
+ if(raster->format & Raster::PAL4 && natras->flags & Ps2Raster::SWIZZLED4){
+ for(y = 0; y < h; y += 4){
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ uint8 c = a & 1 ? px[a>>1] >> 4 : px[a>>1] & 0xF;
+ tmpbuf[s>>1] = s & 1 ? (tmpbuf[s>>1]&0xF) | c<<4 : (tmpbuf[s>>1]&0xF0) | c;
+ }
+ memcpy(&px[y<<(logw-1)], tmpbuf, 2*w);
+ }
+ }else if(raster->format & Raster::PAL8 && natras->flags & Ps2Raster::SWIZZLED8){
+ for(y = 0; y < h; y += 4){
+ for(i = 0; i < 4; i++)
+ for(x = 0; x < w; x++){
+ uint32 a = ((y+i)<<logw)+x;
+ uint32 s = swizzle(x, y+i, logw)&mask;
+ tmpbuf[s] = px[a];
+ }
+ memcpy(&px[y<<logw], tmpbuf, 4*w);
+ }
+ }
+}
+
+uint8*
+rasterLock(Raster *raster, int32 level, int32 lockMode)
+{
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ assert(raster->depth != 24);
+
+ if(level > 0){
+ int32 minw, minh;
+ int32 mipw, miph;
+ transferMinSize(raster->format & Raster::PAL4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ while(level--){
+ mipw = max(raster->width, minw);
+ miph = max(raster->height, minh);
+ raster->pixels += ALIGN16(mipw*miph*raster->depth/8) + 0x50;
+ raster->width /= 2;
+ raster->height /= 2;
+ }
+ }
+
+ if((lockMode & Raster::LOCKNOFETCH) == 0)
+ unswizzleRaster(raster);
+ if(lockMode & Raster::LOCKREAD) raster->privateFlags |= Raster::PRIVATELOCK_READ;
+ if(lockMode & Raster::LOCKWRITE) raster->privateFlags |= Raster::PRIVATELOCK_WRITE;
+ return raster->pixels;
+}
+
+void
+rasterUnlock(Raster *raster, int32 level)
+{
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ if(raster->format & (Raster::PAL4 | Raster::PAL8))
+ swizzleRaster(raster);
+
+ raster->width = raster->originalWidth;
+ raster->height = raster->originalHeight;
+ raster->pixels = raster->originalPixels;
+ raster->stride = raster->originalStride;
+ if(natras->flags & Ps2Raster::NEWSTYLE)
+ raster->pixels = ((Ps2Raster::PixelPtr*)raster->pixels)->pixels + 0x50;
+
+ raster->privateFlags &= ~(Raster::PRIVATELOCK_READ|Raster::PRIVATELOCK_WRITE);
+ // TODO: generate mipmaps
+}
+
+static void
+convertCSM1_16(uint32 *pal)
+{
+ int i, j;
+ uint32 tmp;
+ for(i = 0; i < 256; i++)
+ // palette index bits 0x08 and 0x10 are flipped
+ if((i & 0x18) == 0x10){
+ j = i ^ 0x18;
+ tmp = pal[i];
+ pal[i] = pal[j];
+ pal[j] = tmp;
+ }
+}
+
+static void
+convertCSM1_32(uint32 *pal)
+{
+ int i, j;
+ uint32 tmp;
+ for(i = 0; i < 256; i++)
+ // palette index bits 0x08 and 0x10 are flipped
+ if((i & 0x18) == 0x10){
+ j = i ^ 0x18;
+ tmp = pal[i];
+ pal[i] = pal[j];
+ pal[j] = tmp;
+ }
+}
+
+static void
+convertPalette(Raster *raster)
+{
+ if(raster->format & Raster::PAL8){
+ if((raster->format & 0xF00) == Raster::C8888)
+ convertCSM1_32((uint32*)raster->palette);
+ else if((raster->format & 0xF00) == Raster::C8888)
+ convertCSM1_16((uint32*)raster->palette);
+ }
+}
+
+// NB: RW doesn't convert the palette when locking/unlocking
+uint8*
+rasterLockPalette(Raster *raster, int32 lockMode)
+{
+ if((raster->format & (Raster::PAL4 | Raster::PAL8)) == 0)
+ return nil;
+ if((lockMode & Raster::LOCKNOFETCH) == 0)
+ convertPalette(raster);
+ if(lockMode & Raster::LOCKREAD) raster->privateFlags |= Raster::PRIVATELOCK_READ_PALETTE;
+ if(lockMode & Raster::LOCKWRITE) raster->privateFlags |= Raster::PRIVATELOCK_WRITE_PALETTE;
+ return raster->palette;
+}
+
+void
+rasterUnlockPalette(Raster *raster)
+{
+ if(raster->format & (Raster::PAL4 | Raster::PAL8))
+ convertPalette(raster);
+ raster->privateFlags &= ~(Raster::PRIVATELOCK_READ_PALETTE|Raster::PRIVATELOCK_WRITE_PALETTE);
+}
+
+// Almost the same as d3d9 and gl3 function
+bool32
+imageFindRasterFormat(Image *img, int32 type,
+ int32 *pWidth, int32 *pHeight, int32 *pDepth, int32 *pFormat)
+{
+ int32 width, height, depth, format;
+
+ assert((type&0xF) == Raster::TEXTURE);
+
+ for(width = 1; width < img->width; width <<= 1);
+ for(height = 1; height < img->height; height <<= 1);
+
+ depth = img->depth;
+
+ switch(depth){
+ case 32:
+ case 24:
+ // C888 24 bit is unsafe
+ format = Raster::C8888;
+ depth = 32;
+ break;
+ case 16:
+ format = Raster::C1555;
+ break;
+ case 8:
+ format = Raster::PAL8 | Raster::C8888;
+ break;
+ case 4:
+ format = Raster::PAL4 | Raster::C8888;
+ break;
+ default:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ format |= type;
+
+ *pWidth = width;
+ *pHeight = height;
+ *pDepth = depth;
+ *pFormat = format;
+
+ return 1;
+}
+
+bool32
+rasterFromImage(Raster *raster, Image *image)
+{
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+
+ int32 pallength = 0;
+ switch(image->depth){
+ case 24:
+ case 32:
+ if(raster->format != Raster::C8888 &&
+ raster->format != Raster::C888) // unsafe already
+ goto err;
+ break;
+ case 16:
+ if(raster->format != Raster::C1555) goto err;
+ break;
+ case 8:
+ if(raster->format != (Raster::PAL8 | Raster::C8888)) goto err;
+ pallength = 256;
+ break;
+ case 4:
+ if(raster->format != (Raster::PAL4 | Raster::C8888)) goto err;
+ pallength = 16;
+ break;
+ default:
+ err:
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ // unsafe
+ if((raster->format&0xF00) == Raster::C888){
+ RWERROR((ERR_INVRASTER));
+ return 0;
+ }
+
+ uint8 *in, *out;
+ if(image->depth <= 8){
+ in = image->palette;
+ out = raster->lockPalette(Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ memcpy(out, in, 4*pallength);
+ for(int32 i = 0; i < pallength; i++){
+ out[3] = out[3]*128/255;
+ out += 4;
+ }
+ raster->unlockPalette();
+ }
+
+ int minw, minh;
+ int tw;
+ transferMinSize(image->depth == 4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ tw = max(image->width, minw);
+ uint8 *src = image->pixels;
+ out = raster->lock(0, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ if(image->depth == 4){
+ compressPal4(out, tw/2, src, image->stride, image->width, image->height);
+ }else if(image->depth == 8){
+ copyPal8(out, tw, src, image->stride, image->width, image->height);
+ }else{
+ for(int32 y = 0; y < image->height; y++){
+ in = src;
+ for(int32 x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 16:
+ conv_ARGB1555_from_ABGR1555(out, in);
+ out += 2;
+ break;
+ case 24:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = 0x80;
+ out += 4;
+ break;
+ case 32:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3]*128/255;
+ out += 4;
+ break;
+ }
+ in += image->bpp;
+ }
+ src += image->stride;
+ }
+ }
+ raster->unlock(0);
+ return 1;
+}
+
+Image*
+rasterToImage(Raster *raster)
+{
+ Image *image;
+ int depth;
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+
+ int32 rasterFormat = raster->format & 0xF00;
+ switch(rasterFormat){
+ case Raster::C1555:
+ depth = 16;
+ break;
+ case Raster::C8888:
+ depth = 32;
+ break;
+ case Raster::C888:
+ depth = 24;
+ break;
+ case Raster::C555:
+ depth = 16;
+ break;
+
+ default:
+ case Raster::C565:
+ case Raster::C4444:
+ case Raster::LUM8:
+ assert(0 && "unsupported ps2 raster format");
+ }
+ int32 pallength = 0;
+ if((raster->format & Raster::PAL4) == Raster::PAL4){
+ depth = 4;
+ pallength = 16;
+ }else if((raster->format & Raster::PAL8) == Raster::PAL8){
+ depth = 8;
+ pallength = 256;
+ }
+
+ uint8 *in, *out;
+ image = Image::create(raster->width, raster->height, depth);
+ image->allocate();
+
+ if(pallength){
+ out = image->palette;
+ in = raster->lockPalette(Raster::LOCKREAD);
+ if(rasterFormat == Raster::C8888){
+ memcpy(out, in, pallength*4);
+ for(int32 i = 0; i < pallength; i++){
+ out[3] = out[3]*255/128;
+ out += 4;
+ }
+ }else
+ memcpy(out, in, pallength*2);
+ raster->unlockPalette();
+ }
+
+ int minw, minh;
+ int tw;
+ transferMinSize(depth == 4 ? PSMT4 : PSMT8, natras->flags, &minw, &minh);
+ tw = max(raster->width, minw);
+ uint8 *dst = image->pixels;
+ in = raster->lock(0, Raster::LOCKREAD);
+ if(depth == 4){
+ expandPal4(dst, image->stride, in, tw/2, raster->width, raster->height);
+ }else if(depth == 8){
+ copyPal8(dst, image->stride, in, tw, raster->width, raster->height);
+ }else{
+ for(int32 y = 0; y < image->height; y++){
+ out = dst;
+ for(int32 x = 0; x < image->width; x++){
+ switch(raster->format & 0xF00){
+ case Raster::C8888:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3]*255/128;
+ in += 4;
+ break;
+ case Raster::C888:
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ in += 4;
+ break;
+ case Raster::C1555:
+ conv_ARGB1555_from_ABGR1555(out, in);
+ in += 2;
+ break;
+ case Raster::C555:
+ conv_ARGB1555_from_ABGR1555(out, in);
+ out[1] |= 0x80;
+ in += 2;
+ break;
+ default:
+ assert(0 && "unknown ps2 raster format");
+ break;
+ }
+ out += image->bpp;
+ }
+ dst += image->stride;
+ }
+ }
+ raster->unlock(0);
+
+ return image;
+}
+
+int32
+rasterNumLevels(Raster *raster)
+{
+ Ps2Raster *ras = GETPS2RASTEREXT(raster);
+ if(raster->pixels == nil) return 0;
+ if(raster->format & Raster::MIPMAP)
+ return MAXLEVEL(ras)+1;
+ return 1;
+}
+
+static void*
+createNativeRaster(void *object, int32 offset, int32)
+{
+ Ps2Raster *raster = GETPS2RASTEREXT(object);
+ raster->tex0 = 0;
+ raster->paletteBase = 0;
+ raster->kl = defaultMipMapKL;
+ raster->tex1low = 0;
+ raster->unk2 = 0;
+ raster->miptbp1 = 0;
+ raster->miptbp2 = 0;
+ raster->pixelSize = 0;
+ raster->paletteSize = 0;
+ raster->totalSize = 0;
+ raster->flags = 0;
+
+ raster->dataSize = 0;
+ raster->data = nil;
+ return object;
+}
+
+static void*
+destroyNativeRaster(void *object, int32 offset, int32)
+{
+ Ps2Raster *raster = GETPS2RASTEREXT(object);
+ freealign(raster->data);
+ return object;
+}
+
+static void*
+copyNativeRaster(void *dst, void *src, int32 offset, int32)
+{
+ Ps2Raster *dstraster = GETPS2RASTEREXT(dst);
+ Ps2Raster *srcraster = GETPS2RASTEREXT(src);
+ *dstraster = *srcraster;
+ return dst;
+}
+
+static Stream*
+readMipmap(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ uint16 val = stream->readI32();
+ Texture *tex = (Texture*)object;
+ if(tex->raster == nil)
+ return stream;
+ Ps2Raster *raster = GETPS2RASTEREXT(tex->raster);
+ raster->kl = val;
+ return stream;
+}
+
+static Stream*
+writeMipmap(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ Texture *tex = (Texture*)object;
+ if(tex->raster){
+ stream->writeI32(defaultMipMapKL);
+ return stream;
+ }
+ Ps2Raster *raster = GETPS2RASTEREXT(tex->raster);
+ stream->writeI32(raster->kl);
+ return stream;
+}
+
+static int32
+getSizeMipmap(void*, int32, int32)
+{
+ return rw::platform == PLATFORM_PS2 ? 4 : 0;
+}
+
+void
+registerNativeRaster(void)
+{
+ nativeRasterOffset = Raster::registerPlugin(sizeof(Ps2Raster),
+ ID_RASTERPS2,
+ createNativeRaster,
+ destroyNativeRaster,
+ copyNativeRaster);
+
+ Texture::registerPlugin(0, ID_SKYMIPMAP, nil, nil, nil);
+ Texture::registerPluginStream(ID_SKYMIPMAP, readMipmap, writeMipmap, getSizeMipmap);
+}
+
+void
+printTEX0(uint64 tex0)
+{
+ printf("%016lX ", tex0);
+ uint32 tbp0 = tex0 & 0x3FFF; tex0 >>= 14;
+ uint32 tbw = tex0 & 0x3F; tex0 >>= 6;
+ uint32 psm = tex0 & 0x3F; tex0 >>= 6;
+ uint32 tw = tex0 & 0xF; tex0 >>= 4;
+ uint32 th = tex0 & 0xF; tex0 >>= 4;
+ uint32 tcc = tex0 & 0x1; tex0 >>= 1;
+ uint32 tfx = tex0 & 0x3; tex0 >>= 2;
+ uint32 cbp = tex0 & 0x3FFF; tex0 >>= 14;
+ uint32 cpsm = tex0 & 0xF; tex0 >>= 4;
+ uint32 csm = tex0 & 0x1; tex0 >>= 1;
+ uint32 csa = tex0 & 0x1F; tex0 >>= 5;
+ uint32 cld = tex0 & 0x7;
+ printf("TBP0:%4X TBW:%2X PSM:%2X TW:%X TH:%X TCC:%X TFX:%X CBP:%4X CPSM:%X CSM:%X CSA:%2X CLD:%X\n",
+ tbp0, tbw, psm, tw, th, tcc, tfx, cbp, cpsm, csm, csa, cld);
+}
+
+void
+printTEX1(uint64 tex1)
+{
+ printf("%016lX ", tex1);
+ uint32 lcm = tex1 & 0x1; tex1 >>= 2;
+ uint32 mxl = tex1 & 0x7; tex1 >>= 3;
+ uint32 mmag = tex1 & 0x1; tex1 >>= 1;
+ uint32 mmin = tex1 & 0x7; tex1 >>= 3;
+ uint32 mtba = tex1 & 0x1; tex1 >>= 10;
+ uint32 l = tex1 & 0x3; tex1 >>= 13;
+ uint32 k = tex1 & 0xFFF;
+ printf("LCM:%X MXL:%X MMAG:%X MMIN:%X MTBA:%X L:%X K:%X\n",
+ lcm, mxl, mmag, mmin, mtba, l, k);
+}
+
+void
+calcTEX1(Raster *raster, uint64 *tex1, int32 filter)
+{
+ enum {
+ NEAREST = 0,
+ LINEAR,
+ NEAREST_MIPMAP_NEAREST,
+ NEAREST_MIPMAP_LINEAR,
+ LINEAR_MIPMAP_NEAREST,
+ LINEAR_MIPMAP_LINEAR,
+ };
+ Ps2Raster *natras = GETPS2RASTEREXT(raster);
+ uint64 t1 = natras->tex1low;
+ uint64 k = natras->kl & 0xFFF;
+ uint64 l = (natras->kl >> 12) & 0x3;
+ t1 |= k << 32;
+ t1 |= l << 19;
+ switch(filter){
+ case Texture::NEAREST:
+ t1 |= (NEAREST << 5) |
+ (NEAREST << 6);
+ break;
+ case Texture::LINEAR:
+ t1 |= (LINEAR << 5) |
+ (LINEAR << 6);
+ break;
+ case Texture::MIPNEAREST:
+ t1 |= (NEAREST << 5) |
+ (NEAREST_MIPMAP_NEAREST << 6);
+ break;
+ case Texture::MIPLINEAR:
+ t1 |= (LINEAR << 5) |
+ (LINEAR_MIPMAP_NEAREST << 6);
+ break;
+ case Texture::LINEARMIPNEAREST:
+ t1 |= (NEAREST << 5) |
+ (NEAREST_MIPMAP_LINEAR << 6);
+ break;
+ case Texture::LINEARMIPLINEAR:
+ t1 |= (LINEAR << 5) |
+ (LINEAR_MIPMAP_LINEAR << 6);
+ break;
+ }
+ *tex1 = t1;
+}
+
+struct StreamRasterExt
+{
+ int32 width;
+ int32 height;
+ int32 depth;
+ uint16 rasterFormat;
+ int16 version;
+ uint64 tex0;
+ uint32 paletteOffset;
+ uint32 tex1low;
+ uint64 miptbp1;
+ uint64 miptbp2;
+ uint32 pixelSize;
+ uint32 paletteSize;
+ uint32 totalSize;
+ uint32 mipmapVal;
+};
+
+Texture*
+readNativeTexture(Stream *stream)
+{
+ uint32 length, oldversion, version;
+ uint32 fourcc;
+ Raster *raster;
+ Ps2Raster *natras;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ fourcc = stream->readU32();
+ if(fourcc != FOURCC_PS2){
+ RWERROR((ERR_PLATFORM, fourcc));
+ return nil;
+ }
+ Texture *tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+
+ // Texture
+ tex->filterAddressing = stream->readU32();
+ if(!findChunk(stream, ID_STRING, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRING"));
+ goto fail;
+ }
+ stream->read8(tex->name, length);
+ if(!findChunk(stream, ID_STRING, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRING"));
+ goto fail;
+ }
+ stream->read8(tex->mask, length);
+
+ // Raster
+ StreamRasterExt streamExt;
+ oldversion = rw::version;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ if(!findChunk(stream, ID_STRUCT, nil, &version)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ ASSERTLITTLE;
+ stream->read8(&streamExt, 0x40);
+/*
+printf("%X %X %X %X %X %016llX %X %X %016llX %016llX %X %X %X %X\n",
+streamExt.width,
+streamExt.height,
+streamExt.depth,
+streamExt.rasterFormat,
+streamExt.version,
+streamExt.tex0,
+streamExt.paletteOffset,
+streamExt.tex1low,
+streamExt.miptbp1,
+streamExt.miptbp2,
+streamExt.pixelSize,
+streamExt.paletteSize,
+streamExt.totalSize,
+streamExt.mipmapVal);
+*/
+ noNewStyleRasters = streamExt.version < 2;
+ rw::version = version;
+ raster = Raster::create(streamExt.width, streamExt.height,
+ streamExt.depth, streamExt.rasterFormat,
+ PLATFORM_PS2);
+ noNewStyleRasters = 0;
+ rw::version = oldversion;
+ tex->raster = raster;
+ natras = GETPS2RASTEREXT(raster);
+//printf("%X %X\n", natras->paletteBase, natras->tex1low);
+// printf("%08X%08X %08X%08X %08X%08X\n",
+// (uint32)natras->tex0, (uint32)(natras->tex0>>32),
+// (uint32)natras->miptbp1, (uint32)(natras->miptbp1>>32),
+// (uint32)natras->miptbp2, (uint32)(natras->miptbp2>>32));
+// printTEX0(natras->tex0);
+ uint64 tex1;
+ calcTEX1(raster, &tex1, tex->filterAddressing & 0xF);
+// printTEX1(tex1);
+
+ // TODO: GTA SA LD_OTB.txd loses here
+ assert(natras->pixelSize >= streamExt.pixelSize);
+ assert(natras->paletteSize >= streamExt.paletteSize);
+
+//if(natras->tex0 != streamExt.tex0){
+//printf("TEX0: %016llX\n %016llX\n", natras->tex0, streamExt.tex0);
+//printTEX0(natras->tex0);
+//printTEX0(streamExt.tex0);
+//fflush(stdout);
+//}
+//if(natras->tex1low != streamExt.tex1low)
+//printf("TEX1: %08X\n %08X\n", natras->tex1low, streamExt.tex1low);
+//if(natras->miptbp1 != streamExt.miptbp1)
+//printf("MIP1: %016llX\n %016llX\n", natras->miptbp1, streamExt.miptbp1);
+//if(natras->miptbp2 != streamExt.miptbp2)
+//printf("MIP2: %016llX\n %016llX\n", natras->miptbp2, streamExt.miptbp2);
+//if(natras->paletteBase != streamExt.paletteOffset)
+//printf("PAL: %08X\n %08X\n", natras->paletteBase, streamExt.paletteOffset);
+//if(natras->pixelSize != streamExt.pixelSize)
+//printf("PXS: %08X\n %08X\n", natras->pixelSize, streamExt.pixelSize);
+//if(natras->paletteSize != streamExt.paletteSize)
+//printf("PLS: %08X\n %08X\n", natras->paletteSize, streamExt.paletteSize);
+//if(natras->totalSize != streamExt.totalSize)
+//printf("TSZ: %08X\n %08X\n", natras->totalSize, streamExt.totalSize);
+
+ // junk addresses, no need to store them
+ streamExt.tex0 &= ~0x3FFFULL;
+ streamExt.tex0 &= ~(0x3FFFULL << 37);
+
+ assert(natras->tex0 == streamExt.tex0);
+ natras->tex0 = streamExt.tex0;
+ assert(natras->paletteBase == streamExt.paletteOffset);
+ natras->paletteBase = streamExt.paletteOffset;
+ assert(natras->tex1low == streamExt.tex1low);
+ natras->tex1low = streamExt.tex1low;
+ assert(natras->miptbp1 == streamExt.miptbp1);
+ natras->miptbp1 = streamExt.miptbp1;
+ assert(natras->miptbp2 == streamExt.miptbp2);
+ natras->miptbp2 = streamExt.miptbp2;
+ assert(natras->pixelSize == streamExt.pixelSize);
+ natras->pixelSize = streamExt.pixelSize;
+ assert(natras->paletteSize == streamExt.paletteSize);
+ natras->paletteSize = streamExt.paletteSize;
+ assert(natras->totalSize == streamExt.totalSize);
+ natras->totalSize = streamExt.totalSize;
+ natras->kl = streamExt.mipmapVal;
+//printf("%X %X\n", natras->paletteBase, natras->tex1low);
+// printf("%08X%08X %08X%08X %08X%08X\n",
+// (uint32)natras->tex0, (uint32)(natras->tex0>>32),
+// (uint32)natras->miptbp1, (uint32)(natras->miptbp1>>32),
+// (uint32)natras->miptbp2, (uint32)(natras->miptbp2>>32));
+// printTEX0(natras->tex0);
+ calcTEX1(raster, &tex1, tex->filterAddressing & 0xF);
+// printTEX1(tex1);
+
+ // this is weird stuff
+ if(streamExt.version < 2){
+ if(streamExt.version == 1){
+ // Version 1 has swizzled 8 bit textures
+ if(!(natras->flags & Ps2Raster::NEWSTYLE))
+ natras->flags |= Ps2Raster::SWIZZLED8;
+ else
+ assert(0 && "can't happen");
+ }else{
+ // Version 0 has no swizzling at all
+ if(!(natras->flags & Ps2Raster::NEWSTYLE))
+ natras->flags &= ~Ps2Raster::SWIZZLED8;
+ else
+ assert(0 && "can't happen");
+ }
+ }
+
+ if(!findChunk(stream, ID_STRUCT, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ goto fail;
+ }
+ if(streamExt.version < 2){
+ stream->read8(raster->pixels, length);
+ }else{
+ stream->read8(((Ps2Raster::PixelPtr*)raster->originalPixels)->pixels, natras->pixelSize);
+ stream->read8(raster->palette-0x50, natras->paletteSize);
+ }
+//printf("\n");
+ return tex;
+
+fail:
+ tex->destroy();
+ return nil;
+}
+
+void
+writeNativeTexture(Texture *tex, Stream *stream)
+{
+ Raster *raster = tex->raster;
+ Ps2Raster *ras = GETPS2RASTEREXT(raster);
+ writeChunkHeader(stream, ID_STRUCT, 8);
+ stream->writeU32(FOURCC_PS2);
+ stream->writeU32(tex->filterAddressing);
+ int32 len = strlen(tex->name)+4 & ~3;
+ writeChunkHeader(stream, ID_STRING, len);
+ stream->write8(tex->name, len);
+ len = strlen(tex->mask)+4 & ~3;
+ writeChunkHeader(stream, ID_STRING, len);
+ stream->write8(tex->mask, len);
+
+ int32 sz = ras->pixelSize + ras->paletteSize;
+ writeChunkHeader(stream, ID_STRUCT, 12 + 64 + 12 + sz);
+ writeChunkHeader(stream, ID_STRUCT, 64);
+ StreamRasterExt streamExt;
+ streamExt.width = raster->width;
+ streamExt.height = raster->height;
+ streamExt.depth = raster->depth;
+ streamExt.rasterFormat = raster->format | raster->type;
+ streamExt.version = 0;
+ if(ras->flags == Ps2Raster::SWIZZLED8 && raster->depth == 8)
+ streamExt.version = 1;
+ if(ras->flags & Ps2Raster::NEWSTYLE)
+ streamExt.version = 2;
+ streamExt.tex0 = ras->tex0;
+ streamExt.paletteOffset = ras->paletteBase;
+ streamExt.tex1low = ras->tex1low;
+ streamExt.miptbp1 = ras->miptbp1;
+ streamExt.miptbp2 = ras->miptbp2;
+ streamExt.pixelSize = ras->pixelSize;
+ streamExt.paletteSize = ras->paletteSize;
+ streamExt.totalSize = ras->totalSize;
+ streamExt.mipmapVal = ras->kl;
+ ASSERTLITTLE;
+ stream->write8(&streamExt, 64);
+
+ writeChunkHeader(stream, ID_STRUCT, sz);
+ if(streamExt.version < 2){
+ stream->write8(raster->pixels, sz);
+ }else{
+ stream->write8(((Ps2Raster::PixelPtr*)raster->originalPixels)->pixels, ras->pixelSize);
+ stream->write8(raster->palette-0x50, ras->paletteSize);
+ }
+}
+
+uint32
+getSizeNativeTexture(Texture *tex)
+{
+ uint32 size = 12 + 8;
+ size += 12 + strlen(tex->name)+4 & ~3;
+ size += 12 + strlen(tex->mask)+4 & ~3;
+ size += 12;
+ size += 12 + 64;
+ Ps2Raster *ras = GETPS2RASTEREXT(tex->raster);
+ size += 12 + ras->pixelSize + ras->paletteSize;
+ return size;
+}
+
+}
+}
diff --git a/src/ps2/ps2skin.cpp b/src/ps2/ps2skin.cpp
new file mode 100644
index 0000000..d4c9d79
--- /dev/null
+++ b/src/ps2/ps2skin.cpp
@@ -0,0 +1,334 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "../rwbase.h"
+#include "../rwerror.h"
+#include "../rwplg.h"
+#include "../rwpipeline.h"
+#include "../rwobjects.h"
+#include "../rwanim.h"
+#include "../rwengine.h"
+#include "../rwplugins.h"
+#include "rwps2.h"
+#include "rwps2plg.h"
+
+#include "rwps2impl.h"
+
+#define PLUGIN_ID ID_SKIN
+
+namespace rw {
+namespace ps2 {
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+ skinGlobals.pipelines[PLATFORM_PS2] = makeSkinPipeline();
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_PS2])->groupPipeline->destroy();
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_PS2])->groupPipeline = nil;
+ ((ObjPipeline*)skinGlobals.pipelines[PLATFORM_PS2])->destroy();
+ skinGlobals.pipelines[PLATFORM_PS2] = nil;
+ return o;
+}
+
+void
+initSkin(void)
+{
+ Driver::registerPlugin(PLATFORM_PS2, 0, ID_SKIN,
+ skinOpen, skinClose);
+}
+
+ObjPipeline*
+makeSkinPipeline(void)
+{
+ MatPipeline *pipe = MatPipeline::create();
+ pipe->pluginID = ID_SKIN;
+ pipe->pluginData = 1;
+ pipe->attribs[AT_XYZ] = &attribXYZ;
+ pipe->attribs[AT_UV] = &attribUV;
+ pipe->attribs[AT_RGBA] = &attribRGBA;
+ pipe->attribs[AT_NORMAL] = &attribNormal;
+ pipe->attribs[AT_NORMAL+1] = &attribWeights;
+ uint32 vertCount = MatPipeline::getVertCount(VU_Lights-0x100, 5, 3, 2);
+ pipe->setTriBufferSizes(5, vertCount);
+ pipe->vifOffset = pipe->inputStride*vertCount;
+ pipe->instanceCB = skinInstanceCB;
+ pipe->uninstanceCB = genericUninstanceCB;
+ pipe->preUninstCB = skinPreCB;
+ pipe->postUninstCB = skinPostCB;
+
+ ObjPipeline *opipe = ObjPipeline::create();
+ opipe->pluginID = ID_SKIN;
+ opipe->pluginData = 1;
+ opipe->groupPipeline = pipe;
+ return opipe;
+}
+
+Stream*
+readNativeSkin(Stream *stream, int32, void *object, int32 offset)
+{
+ uint8 header[4];
+ Geometry *geometry = (Geometry*)object;
+ uint32 platform;
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ platform = stream->readU32();
+ if(platform != PLATFORM_PS2){
+ RWERROR((ERR_PLATFORM, platform));
+ return nil;
+ }
+ stream->read8(header, 4);
+ Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
+ *PLUGINOFFSET(Skin*, geometry, offset) = skin;
+
+ // numUsedBones and numWeights appear in/after 34003
+ // but not in/before 33002 (probably rw::version >= 0x34000)
+ bool oldFormat = header[1] == 0;
+
+ // Use numBones for numUsedBones to allocate data
+ if(oldFormat)
+ skin->init(header[0], header[0], 0);
+ else
+ skin->init(header[0], header[1], 0);
+ skin->numWeights = header[2];
+
+ if(!oldFormat)
+ stream->read8(skin->usedBones, skin->numUsedBones);
+ if(skin->numBones)
+ stream->read32(skin->inverseMatrices, skin->numBones*64);
+
+ // dummy data in case we need to write data in the new format
+ if(oldFormat){
+ skin->numWeights = 4;
+ for(int32 i = 0; i < skin->numUsedBones; i++)
+ skin->usedBones[i] = i;
+ }
+
+ if(!oldFormat){
+ // TODO: what is this?
+ stream->seek(4*4);
+
+ readSkinSplitData(stream, skin);
+ }
+ return stream;
+}
+
+Stream*
+writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset)
+{
+ uint8 header[4];
+
+ writeChunkHeader(stream, ID_STRUCT, len-12);
+ stream->writeU32(PLATFORM_PS2);
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ // not sure which version introduced the new format
+ bool oldFormat = version < 0x34000;
+ header[0] = skin->numBones;
+ if(oldFormat){
+ header[1] = 0;
+ header[2] = 0;
+ }else{
+ header[1] = skin->numUsedBones;
+ header[2] = skin->numWeights;
+ }
+ header[3] = 0;
+ stream->write8(header, 4);
+
+ if(!oldFormat)
+ stream->write8(skin->usedBones, skin->numUsedBones);
+ stream->write32(skin->inverseMatrices, skin->numBones*64);
+ if(!oldFormat){
+ uint32 buffer[4] = { 0, 0, 0, 0, };
+ stream->write32(buffer, 4*4);
+
+ writeSkinSplitData(stream, skin);
+ }
+ return stream;
+}
+
+int32
+getSizeNativeSkin(void *object, int32 offset)
+{
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ if(skin == nil)
+ return -1;
+ int32 size = 12 + 4 + 4 + skin->numBones*64;
+ // not sure which version introduced the new format
+ if(version >= 0x34000)
+ size += skin->numUsedBones + 16 + skinSplitDataSize(skin);
+ return size;
+}
+
+void
+instanceSkinData(Geometry*, Mesh *m, Skin *skin, uint32 *data)
+{
+ uint16 j;
+ float32 *weights = (float32*)data;
+ uint32 *indices = data;
+ for(uint32 i = 0; i < m->numIndices; i++){
+ j = m->indices[i];
+ for(int32 k = 0; k < 4; k++){
+ *weights++ = skin->weights[j*4+k];
+ *indices &= ~0x3FF;
+ *indices++ |= skin->indices[j*4+k] && skin->weights[j*4+k] ?
+ (skin->indices[j*4+k]+1) << 2 : 0;
+ }
+ }
+}
+
+void
+skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data)
+{
+ Skin *skin = Skin::get(g);
+ if(skin == nil)
+ return;
+ instanceSkinData(g, m, skin, (uint32*)data[4]);
+}
+
+// TODO: call base function perhaps?
+int32
+findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v)
+{
+ Skin *skin = Skin::get(g);
+ float32 *wghts = nil;
+ uint8 *inds = nil;
+ if(skin){
+ wghts = skin->weights;
+ inds = skin->indices;
+ }
+
+ V3d *verts = g->morphTargets[0].vertices;
+ TexCoords *tex = g->texCoords[0];
+ TexCoords *tex1 = g->texCoords[1];
+ V3d *norms = g->morphTargets[0].normals;
+ RGBA *cols = g->colors;
+
+ for(int32 i = 0; i < g->numVertices; i++){
+ uint32 flag = flags ? flags[i] : ~0;
+ if(mask & flag & 0x1 && !equal(*verts, v->p))
+ goto cont;
+ if(mask & flag & 0x10 && !equal(*norms, v->n))
+ goto cont;
+ if(mask & flag & 0x100 && !equal(*cols, v->c))
+ goto cont;
+ if(mask & flag & 0x1000 && !equal(*tex, v->t))
+ goto cont;
+ if(mask & flag & 0x2000 && !equal(*tex1, v->t1))
+ goto cont;
+ if(mask & flag & 0x10000 &&
+ !(wghts[0] == v->w[0] && wghts[1] == v->w[1] &&
+ wghts[2] == v->w[2] && wghts[3] == v->w[3] &&
+ inds[0] == v->i[0] && inds[1] == v->i[1] &&
+ inds[2] == v->i[2] && inds[3] == v->i[3]))
+ goto cont;
+ return i;
+ cont:
+ verts++;
+ tex++;
+ tex1++;
+ norms++;
+ cols++;
+ wghts += 4;
+ inds += 4;
+ }
+ return -1;
+}
+
+void
+insertVertexSkin(Geometry *geo, int32 i, uint32 mask, Vertex *v)
+{
+ Skin *skin = Skin::get(geo);
+ insertVertex(geo, i, mask, v);
+ if(mask & 0x10000){
+ memcpy(&skin->weights[i*4], v->w, 16);
+ memcpy(&skin->indices[i*4], v->i, 4);
+ }
+}
+
+/*
+void
+skinUninstanceCB(MatPipeline*, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[])
+{
+ float32 *verts = (float32*)data[AT_XYZ];
+ float32 *texcoords = (float32*)data[AT_UV];
+ uint8 *colors = (uint8*)data[AT_RGBA];
+ int8 *norms = (int8*)data[AT_NORMAL];
+ uint32 *wghts = (uint32*)data[AT_NORMAL+1];
+ uint32 mask = 0x1; // vertices
+ if(geo->flags & Geometry::NORMALS)
+ mask |= 0x10;
+ if(geo->flags & Geometry::PRELIT)
+ mask |= 0x100;
+ if(geo->numTexCoordSets > 0)
+ mask |= 0x1000;
+ mask |= 0x10000;
+
+ Vertex v;
+ for(uint32 i = 0; i < mesh->numIndices; i++){
+ if(mask & 0x1)
+ memcpy(&v.p, verts, 12);
+ if(mask & 0x10){
+ v.n[0] = norms[0]/127.0f;
+ v.n[1] = norms[1]/127.0f;
+ v.n[2] = norms[2]/127.0f;
+ }
+ if(mask & 0x100)
+ memcpy(&v.c, colors, 4);
+ if(mask & 0x1000)
+ memcpy(&v.t, texcoords, 8);
+ for(int j = 0; j < 4; j++){
+ ((uint32*)v.w)[j] = wghts[j] & ~0x3FF;
+ v.i[j] = (wghts[j] & 0x3FF) >> 2;
+ if(v.i[j]) v.i[j]--;
+ if(v.w[j] == 0.0f) v.i[j] = 0;
+ }
+ int32 idx = findVertexSkin(geo, flags, mask, &v);
+ if(idx < 0)
+ idx = geo->numVertices++;
+ mesh->indices[i] = idx;
+ flags[idx] = mask;
+ insertVertexSkin(geo, idx, mask, &v);
+ verts += 3;
+ texcoords += 2;
+ colors += 4;
+ norms += 3;
+ wghts += 4;
+ }
+}
+*/
+
+void
+skinPreCB(MatPipeline*, Geometry *geo)
+{
+ Skin *skin = Skin::get(geo);
+ if(skin == nil)
+ return;
+ uint8 *data = skin->data;
+ float *invMats = skin->inverseMatrices;
+ // meshHeader->totalIndices is highest possible number of vertices again
+ skin->init(skin->numBones, skin->numBones, geo->meshHeader->totalIndices);
+ memcpy(skin->inverseMatrices, invMats, skin->numBones*64);
+ rwFree(data);
+}
+
+void
+skinPostCB(MatPipeline*, Geometry *geo)
+{
+ Skin *skin = Skin::get(geo);
+ if(skin){
+ skin->findNumWeights(geo->numVertices);
+ skin->findUsedBones(geo->numVertices);
+ }
+}
+
+}
+}
diff --git a/src/ps2/rwps2.h b/src/ps2/rwps2.h
new file mode 100644
index 0000000..d2945e9
--- /dev/null
+++ b/src/ps2/rwps2.h
@@ -0,0 +1,281 @@
+namespace rw {
+
+#ifdef RW_PS2
+struct EngineOpenParams
+{
+};
+#endif
+
+namespace ps2 {
+
+void registerPlatformPlugins(void);
+
+extern Device renderdevice;
+
+struct Im2DVertex
+{
+ float32 x, y, z, w;
+ float32 u, v, q, PAD;
+ uint32 r, g, b, a;
+
+ void setScreenX(float32 x) { this->x = x; }
+ void setScreenY(float32 y) { this->y = y; }
+ void setScreenZ(float32 z) { this->z = z; }
+ void setCameraZ(float32 z) { this->w = z; }
+ void setRecipCameraZ(float32 recipz) { this->q = recipz; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) {
+ this->r = r; this->g = g; this->b = b; this->a = a; }
+ void setU(float32 u, float recipz) { this->u = u; }
+ void setV(float32 v, float recipz) { this->v = v; }
+
+ float getScreenX(void) { return this->x; }
+ float getScreenY(void) { return this->y; }
+ float getScreenZ(void) { return this->z; }
+ float getCameraZ(void) { return this->w; }
+ float getRecipCameraZ(void) { return this->q; }
+ RGBA getColor(void) { return makeRGBA(this->r, this->g, this->b, this->a); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+
+struct Im3DVertex
+{
+ V3d position;
+ uint8 r, g, b, a;
+ float32 u, v;
+
+ void setX(float32 x) { this->position.x = x; }
+ void setY(float32 y) { this->position.y = y; }
+ void setZ(float32 z) { this->position.z = z; }
+ void setColor(uint8 r, uint8 g, uint8 b, uint8 a) {
+ this->r = r; this->g = g; this->b = b; this->a = a; }
+ void setU(float32 u) { this->u = u; }
+ void setV(float32 v) { this->v = v; }
+
+ float getX(void) { return this->position.x; }
+ float getY(void) { return this->position.y; }
+ float getZ(void) { return this->position.z; }
+ RGBA getColor(void) { return makeRGBA(this->r, this->g, this->b, this->a); }
+ float getU(void) { return this->u; }
+ float getV(void) { return this->v; }
+};
+
+
+struct InstanceData
+{
+ uint32 dataSize;
+ uint8 *dataRaw;
+ uint8 *data;
+ Material *material;
+};
+
+struct InstanceDataHeader : rw::InstanceDataHeader
+{
+ uint32 numMeshes;
+ InstanceData *instanceMeshes;
+};
+
+enum {
+ VU_Lights = 0x3d0
+};
+
+enum PS2Attribs {
+ AT_V2_32 = 0x64000000,
+ AT_V2_16 = 0x65000000,
+ AT_V2_8 = 0x66000000,
+ AT_V3_32 = 0x68000000,
+ AT_V3_16 = 0x69000000,
+ AT_V3_8 = 0x6A000000,
+ AT_V4_32 = 0x6C000000,
+ AT_V4_16 = 0x6D000000,
+ AT_V4_8 = 0x6E000000,
+ AT_UNSGN = 0x00004000,
+
+ AT_RW = 0x6
+};
+
+// Not really types as in RW but offsets
+enum PS2AttibTypes {
+ AT_XYZ = 0,
+ AT_UV = 1,
+ AT_RGBA = 2,
+ AT_NORMAL = 3
+};
+
+void *destroyNativeData(void *object, int32, int32);
+Stream *readNativeData(Stream *stream, int32 len, void *object, int32, int32);
+Stream *writeNativeData(Stream *stream, int32 len, void *object, int32, int32);
+int32 getSizeNativeData(void *object, int32, int32);
+void registerNativeDataPlugin(void);
+
+void printDMA(InstanceData *inst);
+void printDMAVIF(InstanceData *inst);
+void sizedebug(InstanceData *inst);
+
+void fixDmaOffsets(InstanceData *inst); // only RW_PS2
+int32 unfixDmaOffsets(InstanceData *inst);
+
+struct PipeAttribute
+{
+ const char *name;
+ uint32 attrib;
+};
+
+extern bool adcHack; // use MASK write to initialize ADC field in vertex cluster
+extern PipeAttribute attribXYZ;
+extern PipeAttribute attribXYZW;
+extern PipeAttribute attribUV;
+extern PipeAttribute attribUV2;
+extern PipeAttribute attribRGBA;
+extern PipeAttribute attribNormal;
+extern PipeAttribute attribWeights;
+
+class MatPipeline : public rw::Pipeline
+{
+public:
+ uint32 vifOffset;
+ uint32 inputStride;
+ // number of vertices for tri strips and lists
+ uint32 triStripCount, triListCount;
+ PipeAttribute *attribs[10];
+ void (*instanceCB)(MatPipeline*, Geometry*, Mesh*, uint8**);
+ void (*uninstanceCB)(MatPipeline*, Geometry*, uint32*, Mesh*, uint8**);
+ void (*preUninstCB)(MatPipeline*, Geometry*);
+ void (*postUninstCB)(MatPipeline*, Geometry*);
+ // RW has more:
+ // instanceTestCB()
+ // resEntryAllocCB()
+ // bridgeCB()
+ // postMeshCB()
+ // vu1code
+ // primtype
+
+ static uint32 getVertCount(uint32 top, uint32 inAttribs,
+ uint32 outAttribs, uint32 outBufs) {
+ return (top-outBufs)/(inAttribs*2+outAttribs*outBufs);
+ }
+
+ void init(void);
+ static MatPipeline *create(void);
+ void destroy(void);
+ void dump(void);
+ void setTriBufferSizes(uint32 inputStride, uint32 bufferSize);
+ void instance(Geometry *g, InstanceData *inst, Mesh *m);
+ uint8 *collectData(Geometry *g, InstanceData *inst, Mesh *m, uint8 *data[]);
+};
+
+class ObjPipeline : public rw::ObjPipeline
+{
+public:
+ void init(void);
+ static ObjPipeline *create(void);
+
+ MatPipeline *groupPipeline;
+ // RW has more:
+ // setupCB()
+ // finalizeCB()
+ // lightOffset
+ // lightSize
+};
+
+struct Vertex {
+ V3d p;
+ TexCoords t;
+ TexCoords t1;
+ RGBA c;
+ V3d n;
+ // skin
+ float32 w[4];
+ uint8 i[4];
+};
+
+void insertVertex(Geometry *geo, int32 i, uint32 mask, Vertex *v);
+
+extern ObjPipeline *defaultObjPipe;
+extern MatPipeline *defaultMatPipe;
+
+void genericUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]);
+void genericPreCB(MatPipeline *pipe, Geometry *geo); // skin and ADC
+//void defaultUninstanceCB(MatPipeline *pipe, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]);
+void skinInstanceCB(MatPipeline *, Geometry *g, Mesh *m, uint8 **data);
+//void skinUninstanceCB(MatPipeline*, Geometry *geo, uint32 flags[], Mesh *mesh, uint8 *data[]);
+
+ObjPipeline *makeDefaultPipeline(void);
+void dumpPipeline(rw::Pipeline *pipe);
+
+// ADC plugin
+
+// Each element in adcBits corresponds to an index in Mesh->indices,
+// this assumes the Mesh indices are ADC formatted.
+// ADCData->numBits != Mesh->numIndices. ADCData->numBits is probably
+// equal to Mesh->numIndices before the Mesh gets ADC formatted.
+//
+// Can't convert between ADC-formatted and non-ADC-formatted yet :(
+
+struct ADCData
+{
+ bool32 adcFormatted;
+ int8 *adcBits;
+ int32 numBits;
+};
+extern int32 adcOffset;
+void registerADCPlugin(void);
+
+int8 *getADCbits(Geometry *geo);
+int8 *getADCbitsForMesh(Geometry *geo, Mesh *mesh);
+void convertADC(Geometry *g);
+void unconvertADC(Geometry *geo);
+void allocateADC(Geometry *geo);
+
+// PDS plugin
+
+Pipeline *getPDSPipe(uint32 data);
+void registerPDSPipe(Pipeline *pipe);
+void registerPDSPlugin(int32 n);
+void registerPluginPDSPipes(void);
+
+// Native Texture and Raster
+
+struct Ps2Raster
+{
+ enum Flags {
+ NEWSTYLE = 0x1, // has GIF tags and transfer DMA chain
+ SWIZZLED8 = 0x2,
+ SWIZZLED4 = 0x4
+ };
+ struct PixelPtr {
+ // RW has pixels as second element but we don't want this struct
+ // to be longer than 16 bytes
+ uint8 *pixels;
+ // palette can be allocated in last level, in that case numTransfers is
+ // one less than numTotalTransfers.
+ int32 numTransfers;
+ int32 numTotalTransfers;
+ };
+
+ uint64 tex0;
+ uint32 paletteBase; // block address from beginning of GS data (words/64)
+ uint16 kl;
+ uint8 tex1low; // MXL and LCM of TEX1
+ uint8 unk2;
+ uint64 miptbp1;
+ uint64 miptbp2;
+ uint32 pixelSize; // in bytes
+ uint32 paletteSize; // in bytes
+ uint32 totalSize; // total size of texture on GS in words
+ int8 flags;
+
+ uint8 *data; //tmp
+ uint32 dataSize;
+};
+
+extern int32 nativeRasterOffset;
+void registerNativeRaster(void);
+#define GETPS2RASTEREXT(raster) PLUGINOFFSET(rw::ps2::Ps2Raster, raster, rw::ps2::nativeRasterOffset)
+
+Texture *readNativeTexture(Stream *stream);
+void writeNativeTexture(Texture *tex, Stream *stream);
+uint32 getSizeNativeTexture(Texture *tex);
+
+}
+}
diff --git a/src/ps2/rwps2impl.h b/src/ps2/rwps2impl.h
new file mode 100644
index 0000000..f455bd2
--- /dev/null
+++ b/src/ps2/rwps2impl.h
@@ -0,0 +1,16 @@
+namespace rw {
+namespace ps2 {
+
+Raster *rasterCreate(Raster *raster);
+uint8 *rasterLock(Raster*, int32 level, int32 lockMode);
+void rasterUnlock(Raster*, int32 level);
+uint8 *rasterLockPalette(Raster*, int32 lockMode);
+void rasterUnlockPalette(Raster*);
+int32 rasterNumLevels(Raster*);
+bool32 imageFindRasterFormat(Image *img, int32 type,
+ int32 *width, int32 *height, int32 *depth, int32 *format);
+bool32 rasterFromImage(Raster *raster, Image *image);
+Image *rasterToImage(Raster *raster);
+
+}
+}
diff --git a/src/ps2/rwps2plg.h b/src/ps2/rwps2plg.h
new file mode 100644
index 0000000..8776a51
--- /dev/null
+++ b/src/ps2/rwps2plg.h
@@ -0,0 +1,27 @@
+namespace rw {
+namespace ps2 {
+
+// MatFX plugin
+
+void initMatFX(void);
+ObjPipeline *makeMatFXPipeline(void);
+
+// Skin plugin
+
+void initSkin(void);
+ObjPipeline *makeSkinPipeline(void);
+
+void insertVertexSkin(Geometry *geo, int32 i, uint32 mask, Vertex *v);
+int32 findVertexSkin(Geometry *g, uint32 flags[], uint32 mask, Vertex *v);
+
+Stream *readNativeSkin(Stream *stream, int32, void *object, int32 offset);
+Stream *writeNativeSkin(Stream *stream, int32 len, void *object, int32 offset);
+int32 getSizeNativeSkin(void *object, int32 offset);
+
+void instanceSkinData(Geometry *g, Mesh *m, Skin *skin, uint32 *data);
+
+void skinPreCB(MatPipeline*, Geometry*);
+void skinPostCB(MatPipeline*, Geometry*);
+
+}
+}
diff --git a/src/raster.cpp b/src/raster.cpp
new file mode 100644
index 0000000..c7ebffe
--- /dev/null
+++ b/src/raster.cpp
@@ -0,0 +1,559 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+//#include "ps2/rwps2.h"
+#include "d3d/rwd3d.h"
+#include "d3d/rwxbox.h"
+//#include "d3d/rwd3d8.h"
+//#include "d3d/rwd3d9.h"
+#include "gl/rwgl3.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+
+int32 Raster::numAllocated;
+
+struct RasterGlobals
+{
+ int32 sp;
+ Raster *stack[32];
+};
+int32 rasterModuleOffset;
+
+#define RASTERGLOBAL(v) (PLUGINOFFSET(RasterGlobals, engine, rasterModuleOffset)->v)
+
+static void*
+rasterOpen(void *object, int32 offset, int32 size)
+{
+ int i;
+ rasterModuleOffset = offset;
+ RASTERGLOBAL(sp) = -1;
+ for(i = 0; i < (int)nelem(RASTERGLOBAL(stack)); i++)
+ RASTERGLOBAL(stack)[i] = nil;
+ return object;
+}
+
+static void*
+rasterClose(void *object, int32 offset, int32 size)
+{
+ return object;
+}
+
+void
+Raster::registerModule(void)
+{
+ Engine::registerPlugin(sizeof(RasterGlobals), ID_RASTERMODULE, rasterOpen, rasterClose);
+}
+
+Raster*
+Raster::create(int32 width, int32 height, int32 depth, int32 format, int32 platform)
+{
+ // TODO: pass arguments through to the driver and create the raster there
+ Raster *raster = (Raster*)rwMalloc(s_plglist.size, MEMDUR_EVENT); // TODO
+ assert(raster != nil);
+ numAllocated++;
+ raster->parent = raster;
+ raster->offsetX = 0;
+ raster->offsetY = 0;
+ raster->platform = platform ? platform : rw::platform;
+ raster->type = format & 0x7;
+ raster->flags = format & 0xF8;
+ raster->privateFlags = 0;
+ raster->format = format & 0xFF00;
+ raster->width = width;
+ raster->height = height;
+ raster->depth = depth;
+ raster->stride = 0;
+ raster->pixels = raster->palette = nil;
+ s_plglist.construct(raster);
+
+// printf("%d %d %d %d\n", raster->type, raster->width, raster->height, raster->depth);
+ return engine->driver[raster->platform]->rasterCreate(raster);
+}
+
+void
+Raster::subRaster(Raster *parent, Rect *r)
+{
+ if((this->flags & DONTALLOCATE) == 0)
+ return;
+ this->width = r->w;
+ this->height = r->h;
+ this->offsetX = parent->offsetX + r->x;
+ this->offsetY = parent->offsetY + r->y;
+ this->parent = parent->parent;
+}
+
+void
+Raster::destroy(void)
+{
+ s_plglist.destruct(this);
+ rwFree(this);
+ numAllocated--;
+}
+
+uint8*
+Raster::lock(int32 level, int32 lockMode)
+{
+ return engine->driver[this->platform]->rasterLock(this, level, lockMode);
+}
+
+void
+Raster::unlock(int32 level)
+{
+ engine->driver[this->platform]->rasterUnlock(this, level);
+}
+
+uint8*
+Raster::lockPalette(int32 lockMode)
+{
+ return engine->driver[this->platform]->rasterLockPalette(this, lockMode);
+}
+
+void
+Raster::unlockPalette(void)
+{
+ engine->driver[this->platform]->rasterUnlockPalette(this);
+}
+
+int32
+Raster::getNumLevels(void)
+{
+ return engine->driver[this->platform]->rasterNumLevels(this);
+}
+
+int32
+Raster::calculateNumLevels(int32 width, int32 height)
+{
+ int32 size = width >= height ? width : height;
+ int32 n;
+ for(n = 0; size != 0; n++)
+ size /= 2;
+ return n;
+}
+
+bool
+Raster::formatHasAlpha(int32 format)
+{
+ return (format & 0xF00) == Raster::C8888 ||
+ (format & 0xF00) == Raster::C1555 ||
+ (format & 0xF00) == Raster::C4444;
+}
+
+bool32
+Raster::imageFindRasterFormat(Image *image, int32 type,
+ int32 *pWidth, int32 *pHeight, int32 *pDepth, int32 *pFormat,
+ int32 platform)
+{
+ return engine->driver[platform ? platform : rw::platform]->imageFindRasterFormat(
+ image, type, pWidth, pHeight, pDepth, pFormat);
+}
+
+Raster*
+Raster::setFromImage(Image *image, int32 platform)
+{
+ if(engine->driver[platform ? platform : rw::platform]->rasterFromImage(this, image))
+ return this;
+ return nil;
+}
+
+Raster*
+Raster::createFromImage(Image *image, int32 platform)
+{
+ Raster *raster;
+ int32 width, height, depth, format;
+ if(!imageFindRasterFormat(image, TEXTURE, &width, &height, &depth, &format, platform))
+ return nil;
+ raster = Raster::create(width, height, depth, format, platform);
+ if(raster == nil)
+ return nil;
+ return raster->setFromImage(image, platform);
+}
+
+Image*
+Raster::toImage(void)
+{
+ return engine->driver[this->platform]->rasterToImage(this);
+}
+
+void
+Raster::show(uint32 flags)
+{
+ engine->device.showRaster(this, flags);
+}
+
+Raster*
+Raster::pushContext(Raster *raster)
+{
+ RasterGlobals *g = PLUGINOFFSET(RasterGlobals, engine, rasterModuleOffset);
+ if(g->sp >= (int32)nelem(g->stack)-1)
+ return nil;
+ return g->stack[++g->sp] = raster;
+}
+
+Raster*
+Raster::popContext(void)
+{
+ RasterGlobals *g = PLUGINOFFSET(RasterGlobals, engine, rasterModuleOffset);
+ if(g->sp < 0)
+ return nil;
+ return g->stack[g->sp--];
+}
+
+Raster*
+Raster::getCurrentContext(void)
+{
+ RasterGlobals *g = PLUGINOFFSET(RasterGlobals, engine, rasterModuleOffset);
+ if(g->sp < 0 || g->sp >= (int32)nelem(g->stack))
+ return nil;
+ return g->stack[g->sp];
+}
+
+bool32
+Raster::renderFast(int32 x, int32 y)
+{
+ return engine->device.rasterRenderFast(this,x, y);
+}
+
+void
+conv_RGBA8888_from_RGBA8888(uint8 *out, uint8 *in)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+}
+
+void
+conv_BGRA8888_from_RGBA8888(uint8 *out, uint8 *in)
+{
+ out[2] = in[0];
+ out[1] = in[1];
+ out[0] = in[2];
+ out[3] = in[3];
+}
+
+void
+conv_RGBA8888_from_RGB888(uint8 *out, uint8 *in)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = 0xFF;
+}
+
+void
+conv_BGRA8888_from_RGB888(uint8 *out, uint8 *in)
+{
+ out[2] = in[0];
+ out[1] = in[1];
+ out[0] = in[2];
+ out[3] = 0xFF;
+}
+
+void
+conv_RGB888_from_RGB888(uint8 *out, uint8 *in)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+}
+
+void
+conv_BGR888_from_RGB888(uint8 *out, uint8 *in)
+{
+ out[2] = in[0];
+ out[1] = in[1];
+ out[0] = in[2];
+}
+
+void
+conv_ARGB1555_from_ARGB1555(uint8 *out, uint8 *in)
+{
+ out[0] = in[0];
+ out[1] = in[1];
+}
+
+void
+conv_ARGB1555_from_RGB555(uint8 *out, uint8 *in)
+{
+ out[0] = in[0];
+ out[1] = in[1] | 0x80;
+}
+
+void
+conv_RGBA5551_from_ARGB1555(uint8 *out, uint8 *in)
+{
+ uint32 r, g, b, a;
+ a = (in[1]>>7) & 1;
+ r = (in[1]>>2) & 0x1F;
+ g = (in[1]&3)<<3 | ((in[0]>>5)&7);
+ b = in[0] & 0x1F;
+ out[0] = a | b<<1 | g<<6;
+ out[1] = g>>2 | r<<3;
+}
+
+void
+conv_ARGB1555_from_RGBA5551(uint8 *out, uint8 *in)
+{
+ uint32 r, g, b, a;
+ a = in[0] & 1;
+ b = (in[0]>>1) & 0x1F;
+ g = (in[1]&7)<<2 | ((in[0]>>6)&3);
+ r = (in[1]>>3) & 0x1F;
+ out[0] = b | g<<5;
+ out[1] = g>>3 | r<<2 | a<<7;
+}
+
+void
+conv_RGBA8888_from_ARGB1555(uint8 *out, uint8 *in)
+{
+ uint32 r, g, b, a;
+ a = (in[1]>>7) & 1;
+ r = (in[1]>>2) & 0x1F;
+ g = (in[1]&3)<<3 | ((in[0]>>5)&7);
+ b = in[0] & 0x1F;
+ out[0] = r*0xFF/0x1f;
+ out[1] = g*0xFF/0x1f;
+ out[2] = b*0xFF/0x1f;
+ out[3] = a*0xFF;
+}
+
+void
+conv_ABGR1555_from_ARGB1555(uint8 *out, uint8 *in)
+{
+ uint32 r, b;
+ r = (in[1]>>2) & 0x1F;
+ b = in[0] & 0x1F;
+ out[1] = (in[1]&0x83) | b<<2;
+ out[0] = (in[0]&0xE0) | r;
+}
+
+void
+expandPal4(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h)
+{
+ int32 x, y;
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w/2; x++){
+ dst[y*dststride + x*2 + 0] = src[y*srcstride + x] & 0xF;
+ dst[y*dststride + x*2 + 1] = src[y*srcstride + x] >> 4;
+ }
+}
+void
+compressPal4(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h)
+{
+ int32 x, y;
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w/2; x++)
+ dst[y*dststride + x] = src[y*srcstride + x*2 + 0] | src[y*srcstride + x*2 + 1] << 4;
+}
+
+void
+expandPal4_BE(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h)
+{
+ int32 x, y;
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w/2; x++){
+ dst[y*dststride + x*2 + 1] = src[y*srcstride + x] & 0xF;
+ dst[y*dststride + x*2 + 0] = src[y*srcstride + x] >> 4;
+ }
+}
+void
+compressPal4_BE(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h)
+{
+ int32 x, y;
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w/2; x++)
+ dst[y*dststride + x] = src[y*srcstride + x*2 + 1] | src[y*srcstride + x*2 + 0] << 4;
+}
+
+void
+copyPal8(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h)
+{
+ int32 x, y;
+ for(y = 0; y < h; y++)
+ for(x = 0; x < w; x++)
+ dst[y*dststride + x] = src[y*srcstride + x];
+}
+
+
+
+// Platform conversion
+
+static rw::Raster*
+xbox_to_d3d(rw::Raster *ras)
+{
+ using namespace rw;
+
+ int dxt = 0;
+ xbox::XboxRaster *xboxras = GETXBOXRASTEREXT(ras);
+ if(xboxras->customFormat){
+ switch(xboxras->format){
+ case xbox::D3DFMT_DXT1: dxt = 1; break;
+ case xbox::D3DFMT_DXT3: dxt = 3; break;
+ case xbox::D3DFMT_DXT5: dxt = 5; break;
+ }
+ }
+ if(dxt == 0)
+ return nil;
+
+ Raster *newras = Raster::create(ras->width, ras->height, ras->depth,
+ ras->format | Raster::TEXTURE | Raster::DONTALLOCATE);
+ int numLevels = ras->getNumLevels();
+ d3d::allocateDXT(newras, dxt, numLevels, xboxras->hasAlpha);
+ for(int i = 0; i < numLevels; i++){
+ uint8 *srcpx = ras->lock(i, Raster::LOCKREAD);
+ // uint8 *dstpx = newras->lock(i, Raster::LOCKWRITE | Raster::LOCKNOFETCH);
+ d3d::setTexels(newras, srcpx, i);
+ // flipDXT(dxt, dstpx, srcpx, ras->width, ras->height);
+ ras->unlock(i);
+ // newras->unlock(i);
+ }
+
+ return newras;
+}
+
+static rw::Raster*
+d3d_to_gl3(rw::Raster *ras)
+{
+#ifdef RW_GL3
+ using namespace rw;
+
+ if(!gl3::gl3Caps.dxtSupported)
+ return nil;
+
+ int dxt = 0;
+ d3d::D3dRaster *d3dras = GETD3DRASTEREXT(ras);
+ if(d3dras->customFormat){
+ switch(d3dras->format){
+ case d3d::D3DFMT_DXT1: dxt = 1; break;
+ case d3d::D3DFMT_DXT3: dxt = 3; break;
+ case d3d::D3DFMT_DXT5: dxt = 5; break;
+ }
+ }
+ if(dxt == 0)
+ return nil;
+
+ Raster *newras = Raster::create(ras->width, ras->height, ras->depth,
+ ras->format | Raster::TEXTURE | Raster::DONTALLOCATE);
+ int numLevels = ras->getNumLevels();
+ gl3::allocateDXT(newras, dxt, numLevels, d3dras->hasAlpha);
+ for(int i = 0; i < numLevels; i++){
+ uint8 *srcpx = ras->lock(i, Raster::LOCKREAD);
+ uint8 *dstpx = newras->lock(i, Raster::LOCKWRITE | Raster::LOCKNOFETCH);
+ flipDXT(dxt, dstpx, srcpx, ras->width, ras->height);
+ ras->unlock(i);
+ newras->unlock(i);
+ }
+
+ return newras;
+#else
+ return nil;
+#endif
+}
+
+static rw::Raster*
+xbox_to_gl3(rw::Raster *ras)
+{
+#ifdef RW_GL3
+ using namespace rw;
+
+ int dxt = 0;
+ xbox::XboxRaster *xboxras = GETXBOXRASTEREXT(ras);
+ if(xboxras->customFormat){
+ switch(xboxras->format){
+ case xbox::D3DFMT_DXT1: dxt = 1; break;
+ case xbox::D3DFMT_DXT3: dxt = 3; break;
+ case xbox::D3DFMT_DXT5: dxt = 5; break;
+ }
+ }
+ if(dxt == 0)
+ return nil;
+
+ Raster *newras = Raster::create(ras->width, ras->height, ras->depth,
+ ras->format | Raster::TEXTURE | Raster::DONTALLOCATE);
+ int numLevels = ras->getNumLevels();
+ gl3::allocateDXT(newras, dxt, numLevels, xboxras->hasAlpha);
+ for(int i = 0; i < numLevels; i++){
+ uint8 *srcpx = ras->lock(i, Raster::LOCKREAD);
+ uint8 *dstpx = newras->lock(i, Raster::LOCKWRITE | Raster::LOCKNOFETCH);
+ flipDXT(dxt, dstpx, srcpx, ras->width, ras->height);
+ ras->unlock(i);
+ newras->unlock(i);
+ }
+
+ return newras;
+#else
+ return nil;
+#endif
+}
+
+rw::Raster*
+Raster::convertTexToCurrentPlatform(rw::Raster *ras)
+{
+ using namespace rw;
+
+ if(ras->platform == rw::platform)
+ return ras;
+ // compatible platforms
+ if((ras->platform == PLATFORM_D3D8 && rw::platform == PLATFORM_D3D9) ||
+ (ras->platform == PLATFORM_D3D9 && rw::platform == PLATFORM_D3D8))
+ return ras;
+
+ // special cased conversion for DXT
+ if((ras->platform == PLATFORM_D3D8 || ras->platform == PLATFORM_D3D9) && rw::platform == PLATFORM_GL3){
+ Raster *newras = d3d_to_gl3(ras);
+ if(newras){
+ ras->destroy();
+ return newras;
+ }
+ }else if(ras->platform == PLATFORM_XBOX && (rw::platform == PLATFORM_D3D9 || rw::platform == PLATFORM_D3D8)){
+ Raster *newras = xbox_to_d3d(ras);
+ if(newras){
+ ras->destroy();
+ return newras;
+ }
+ }else if(ras->platform == PLATFORM_XBOX && rw::platform == PLATFORM_GL3){
+ Raster *newras = xbox_to_gl3(ras);
+ if(newras){
+ ras->destroy();
+ return newras;
+ }
+ }
+
+ // fall back to going through Image directly
+ int32 width, height, depth, format;
+ Image *img = ras->toImage();
+ // TODO: maybe don't *always* do this?
+ img->unpalettize();
+ Raster::imageFindRasterFormat(img, Raster::TEXTURE, &width, &height, &depth, &format);
+ format |= ras->format & (Raster::MIPMAP | Raster::AUTOMIPMAP);
+ Raster *newras = Raster::create(width, height, depth, format);
+ newras->setFromImage(img);
+ img->destroy();
+ int numLevels = ras->getNumLevels();
+ for(int i = 1; i < numLevels; i++){
+ ras->lock(i, Raster::LOCKREAD);
+ img = ras->toImage();
+ // TODO: maybe don't *always* do this?
+ img->unpalettize();
+ newras->lock(i, Raster::LOCKWRITE|Raster::LOCKNOFETCH);
+ newras->setFromImage(img);
+ newras->unlock(i);
+ ras->unlock(i);
+ }
+ ras->destroy();
+ ras = newras;
+ return ras;
+}
+
+
+}
diff --git a/src/render.cpp b/src/render.cpp
new file mode 100644
index 0000000..6822c24
--- /dev/null
+++ b/src/render.cpp
@@ -0,0 +1,96 @@
+#include <stdio.h>
+
+#include "rwbase.h"
+#include "rwplg.h"
+#include "rwengine.h"
+#include "rwrender.h"
+
+namespace rw {
+
+void SetRenderState(int32 state, uint32 value){
+ engine->device.setRenderState(state, (void*)(uintptr)value); }
+
+void SetRenderStatePtr(int32 state, void *value){
+ engine->device.setRenderState(state, value); }
+
+uint32 GetRenderState(int32 state){
+ return (uint32)(uintptr)engine->device.getRenderState(state); }
+
+void *GetRenderStatePtr(int32 state){
+ return engine->device.getRenderState(state); }
+
+// Im2D
+
+namespace im2d {
+
+float32 GetNearZ(void) { return engine->device.zNear; }
+float32 GetFarZ(void) { return engine->device.zFar; }
+void
+RenderLine(void *verts, int32 numVerts, int32 vert1, int32 vert2)
+{
+ engine->device.im2DRenderLine(verts, numVerts, vert1, vert2);
+}
+void
+RenderTriangle(void *verts, int32 numVerts, int32 vert1, int32 vert2, int32 vert3)
+{
+ engine->device.im2DRenderTriangle(verts, numVerts, vert1, vert2, vert3);
+}
+void
+RenderIndexedPrimitive(PrimitiveType type, void *verts, int32 numVerts, void *indices, int32 numIndices)
+{
+ engine->device.im2DRenderIndexedPrimitive(type, verts, numVerts, indices, numIndices);
+}
+void
+RenderPrimitive(PrimitiveType type, void *verts, int32 numVerts)
+{
+ engine->device.im2DRenderPrimitive(type, verts, numVerts);
+}
+
+}
+
+// Im3D
+
+namespace im3d {
+
+void
+Transform(void *vertices, int32 numVertices, Matrix *world, uint32 flags)
+{
+ engine->device.im3DTransform(vertices, numVertices, world, flags);
+}
+void
+RenderLine(int32 vert1, int32 vert2)
+{
+ int16 indices[2];
+ indices[0] = vert1;
+ indices[1] = vert2;
+ RenderIndexedPrimitive(rw::PRIMTYPELINELIST, indices, 2);
+}
+void
+RenderTriangle(int32 vert1, int32 vert2, int32 vert3)
+{
+ int16 indices[3];
+ indices[0] = vert1;
+ indices[1] = vert2;
+ indices[2] = vert3;
+ RenderIndexedPrimitive(rw::PRIMTYPETRILIST, indices, 3);
+}
+void
+RenderPrimitive(PrimitiveType primType)
+{
+ engine->device.im3DRenderPrimitive(primType);
+}
+void
+RenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices)
+{
+ engine->device.im3DRenderIndexedPrimitive(primType, indices, numIndices);
+}
+void
+End(void)
+{
+ engine->device.im3DEnd();
+}
+
+}
+
+}
+
diff --git a/src/rwanim.h b/src/rwanim.h
new file mode 100644
index 0000000..e6dd1d5
--- /dev/null
+++ b/src/rwanim.h
@@ -0,0 +1,195 @@
+#include <stddef.h>
+
+namespace rw {
+
+struct Animation;
+
+// These sizes of these are sadly not platform independent
+// because pointer sizes can vary.
+
+struct KeyFrameHeader
+{
+ KeyFrameHeader *prev;
+ float32 time;
+
+ KeyFrameHeader *next(int32 sz){
+ return (KeyFrameHeader*)((uint8*)this + sz); }
+};
+
+struct InterpFrameHeader
+{
+ KeyFrameHeader *keyFrame1;
+ KeyFrameHeader *keyFrame2;
+};
+
+struct AnimInterpolatorInfo
+{
+ typedef void (*ApplyCB)(void *result, void *frame);
+ typedef void (*BlendCB)(void *out, void *in1, void *in2, float32 a);
+ typedef void (*InterpCB)(void *out, void *in1, void *in2, float32 t,
+ void *custom);
+ typedef void (*AddCB)(void *out, void *in1, void *in2);
+ typedef void (*MulRecipCB)(void *frame, void *start);
+
+ int32 id;
+ int32 interpKeyFrameSize;
+ int32 animKeyFrameSize;
+ int32 customDataSize;
+
+ ApplyCB applyCB;
+ BlendCB blendCB;
+ InterpCB interpCB;
+ AddCB addCB;
+ MulRecipCB mulRecipCB;
+ void (*streamRead)(Stream *stream, Animation *anim);
+ void (*streamWrite)(Stream *stream, Animation *anim);
+ uint32 (*streamGetSize)(Animation *anim);
+
+ static void registerInterp(AnimInterpolatorInfo *interpInfo);
+ static void unregisterInterp(AnimInterpolatorInfo *interpInfo);
+ static AnimInterpolatorInfo *find(int32 id);
+};
+
+struct Animation
+{
+ AnimInterpolatorInfo *interpInfo;
+ int32 numFrames;
+ int32 flags;
+ float32 duration;
+ void *keyframes;
+ void *customData;
+
+ static Animation *create(AnimInterpolatorInfo*, int32 numFrames,
+ int32 flags, float duration);
+ void destroy(void);
+ int32 getNumNodes(void);
+ KeyFrameHeader *getAnimFrame(int32 n){
+ return (KeyFrameHeader*)((uint8*)this->keyframes +
+ n*this->interpInfo->animKeyFrameSize);
+ }
+ static Animation *streamRead(Stream *stream);
+ static Animation *streamReadLegacy(Stream *stream);
+ bool streamWrite(Stream *stream);
+ bool streamWriteLegacy(Stream *stream);
+ uint32 streamGetSize(void);
+};
+
+struct AnimInterpolator
+{
+ Animation *currentAnim;
+ float32 currentTime;
+ void *nextFrame;
+ int32 maxInterpKeyFrameSize;
+ int32 currentInterpKeyFrameSize;
+ int32 currentAnimKeyFrameSize;
+ int32 numNodes;
+ // TODO some callbacks, parent/sub
+ // cached from the InterpolatorInfo
+ AnimInterpolatorInfo::ApplyCB applyCB;
+ AnimInterpolatorInfo::BlendCB blendCB;
+ AnimInterpolatorInfo::InterpCB interpCB;
+ AnimInterpolatorInfo::AddCB addCB;
+ // after this interpolated frames
+
+ static AnimInterpolator *create(int32 numNodes, int32 maxKeyFrameSize);
+ void destroy(void);
+ bool32 setCurrentAnim(Animation *anim);
+ void addTime(float32 t);
+ void *getFrames(void){ return this+1;}
+ InterpFrameHeader *getInterpFrame(int32 n){
+ return (InterpFrameHeader*)((uint8*)getFrames() +
+ n*currentInterpKeyFrameSize);
+ }
+ KeyFrameHeader *getAnimFrame(int32 n){
+ return (KeyFrameHeader*)((uint8*)currentAnim->keyframes +
+ n*currentAnimKeyFrameSize);
+ }
+};
+
+//
+// UV anim
+//
+
+struct UVAnimParamData
+{
+ float32 theta; // rotation
+ float32 s0; // scale x
+ float32 s1; // scale y
+ float32 skew; // skew
+ float32 x; // x pos
+ float32 y; // y pos
+};
+
+struct UVAnimKeyFrame
+{
+ UVAnimKeyFrame *prev;
+ float32 time;
+ float32 uv[6];
+};
+
+struct UVAnimInterpFrame
+{
+ UVAnimKeyFrame *keyFrame1;
+ UVAnimKeyFrame *keyFrame2;
+ float32 uv[6];
+};
+
+struct UVAnimDictionary;
+
+// RW does it differently...maybe we should implement RtDict
+// and make it more general?
+
+struct UVAnimCustomData
+{
+ char name[32];
+ int32 nodeToUVChannel[8];
+ int32 refCount;
+
+ void destroy(Animation *anim);
+ static UVAnimCustomData *get(Animation *anim){
+ return (UVAnimCustomData*)anim->customData; }
+};
+
+// This should be more general probably
+struct UVAnimDictEntry
+{
+ Animation *anim;
+ LLLink inDict;
+ static UVAnimDictEntry *fromDict(LLLink *lnk){
+ return LLLinkGetData(lnk, UVAnimDictEntry, inDict); }
+};
+
+// This too
+struct UVAnimDictionary
+{
+ LinkList animations;
+
+ static UVAnimDictionary *create(void);
+ void destroy(void);
+ int32 count(void) { return this->animations.count(); }
+ void add(Animation *anim);
+ Animation *find(const char *name);
+
+ static UVAnimDictionary *streamRead(Stream *stream);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+};
+
+extern UVAnimDictionary *currentUVAnimDictionary;
+
+// Material plugin
+struct UVAnim
+{
+ Matrix *uv[2];
+ AnimInterpolator *interp[8];
+
+ static bool32 exists(Material *mat);
+ static void addTime(Material *mat, float32 t);
+ static void applyUpdate(Material *mat);
+};
+
+extern int32 uvAnimOffset;
+
+void registerUVAnimPlugin(void);
+
+}
diff --git a/src/rwbase.h b/src/rwbase.h
new file mode 100644
index 0000000..6bc8a54
--- /dev/null
+++ b/src/rwbase.h
@@ -0,0 +1,716 @@
+#ifndef RW_PS2
+#include <stdint.h>
+#endif
+#include <math.h>
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+// TODO: clean up the opengl defines
+// and figure out what we even want here...
+#ifdef RW_GL3
+#define RW_OPENGL
+#define RWDEVICE gl3
+// doesn't help
+//#define RW_GL_USE_VAOS
+#endif
+
+#ifdef RW_GLES2
+#define RW_GLES
+#endif
+#ifdef RW_GLES3
+#define RW_GLES
+#endif
+
+#ifdef RW_D3D9
+#define RWDEVICE d3d
+#define RWHALFPIXEL
+#endif
+
+#ifdef RW_D3D8
+#define RWDEVICE d3d
+#endif
+
+#ifdef RW_PS2
+#define RWHALFPIXEL
+#define RWDEVICE ps2
+#endif
+
+#ifdef RW_WDGL
+#define RW_OPENGL
+#endif
+
+namespace rw {
+
+#ifdef RW_PS2
+ typedef char int8;
+ typedef short int16;
+ typedef int int32;
+ typedef long long int64;
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+ typedef unsigned int uint32;
+ typedef unsigned long long uint64;
+ typedef unsigned int uintptr;
+#else
+ /* get rid of the stupid _t */
+ typedef int8_t int8;
+ typedef int16_t int16;
+ typedef int32_t int32;
+ typedef int64_t int64;
+ typedef uint8_t uint8;
+ typedef uint16_t uint16;
+ typedef uint32_t uint32;
+ typedef uint64_t uint64;
+ typedef uintptr_t uintptr;
+#endif
+
+typedef float float32;
+typedef int32 bool32;
+typedef uint8 byte;
+typedef uint32 uint;
+
+#ifndef nil
+#define nil NULL
+#endif
+
+#ifndef nelem
+#define nelem(A) (sizeof(A) / sizeof A[0])
+#endif
+
+#ifdef __GNUC__
+#define RWALIGN(n) __attribute__ ((aligned (n)))
+#else
+#ifdef _MSC_VER
+#define RWALIGN(n) __declspec(align(n))
+#else
+#define RWALIGN(n) // unknown compiler...ignore
+#endif
+#endif
+
+// Lists
+
+struct LLLink
+{
+ LLLink *next;
+ LLLink *prev;
+ void init(void){
+ this->next = nil;
+ this->prev = nil;
+ }
+ void remove(void){
+ this->prev->next = this->next;
+ this->next->prev = this->prev;
+ }
+};
+
+#define LLLinkGetData(linkvar,type,entry) \
+ ((type*)(((rw::uint8*)(linkvar))-offsetof(type,entry)))
+
+// Have to be careful since the link might be deleted.
+#define FORLIST(_link, _list) \
+ for(rw::LLLink *_next = nil, *_link = (_list).link.next; \
+ _next = (_link)->next, (_link) != (_list).end(); \
+ (_link) = _next)
+
+struct LinkList
+{
+ LLLink link;
+ void init(void){
+ this->link.next = &this->link;
+ this->link.prev = &this->link;
+ }
+ bool32 isEmpty(void){
+ return this->link.next == &this->link;
+ }
+ void add(LLLink *link){
+ link->next = this->link.next;
+ link->prev = &this->link;
+ this->link.next->prev = link;
+ this->link.next = link;
+ }
+ void append(LLLink *link){
+ link->next = &this->link;
+ link->prev = this->link.prev;
+ this->link.prev->next = link;
+ this->link.prev = link;
+ }
+ LLLink *end(void){
+ return &this->link;
+ }
+ int32 count(void){
+ int32 n = 0;
+ FORLIST(lnk, (*this))
+ n++;
+ return n;
+ }
+};
+
+// Mathematical types
+
+struct RGBA
+{
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ uint8 alpha;
+};
+inline RGBA makeRGBA(uint8 r, uint8 g, uint8 b, uint8 a) { RGBA c; c.red = r; c.green = g; c.blue = b; c.alpha = a; return c; }
+inline bool32 equal(const RGBA &c1, const RGBA &c2) { return c1.red == c2.red && c1.green == c2.green && c1.blue == c2.blue && c1.alpha == c2.alpha; }
+#define RWRGBAINT(r, g, b, a) ((uint32)((((a)&0xff)<<24)|(((b)&0xff)<<16)|(((g)&0xff)<<8)|((r)&0xff)))
+
+struct RGBAf
+{
+ float32 red;
+ float32 green;
+ float32 blue;
+ float32 alpha;
+};
+inline RGBAf makeRGBAf(float32 r, float32 g, float32 b, float32 a) { RGBAf c; c.red = r; c.green = g; c.blue = b; c.alpha = a; return c; }
+inline bool32 equal(const RGBAf &c1, const RGBAf &c2) { return c1.red == c2.red && c1.green == c2.green && c1.blue == c2.blue && c1.alpha == c2.alpha; }
+inline RGBAf add(const RGBAf &a, const RGBAf &b) { return makeRGBAf(a.red+b.red, a.green+b.green, a.blue+b.blue, a.alpha+b.alpha); }
+inline RGBAf modulate(const RGBAf &a, const RGBAf &b) { return makeRGBAf(a.red*b.red, a.green*b.green, a.blue*b.blue, a.alpha*b.alpha); }
+inline RGBAf scale(const RGBAf &a, float32 f) { return makeRGBAf(a.red*f, a.green*f, a.blue*f, a.alpha*f); }
+inline void clamp(RGBAf *a) {
+ if(a->red > 1.0f) a->red = 1.0f;
+ if(a->red < 0.0f) a->red = 0.0f;
+ if(a->green > 1.0f) a->green = 1.0f;
+ if(a->green < 0.0f) a->green = 0.0f;
+ if(a->blue > 1.0f) a->blue = 1.0f;
+ if(a->blue < 0.0f) a->blue = 0.0f;
+ if(a->alpha > 1.0f) a->alpha = 1.0f;
+ if(a->alpha < 0.0f) a->alpha = 0.0f;
+}
+
+inline void convColor(RGBA *i, const RGBAf *f){
+ int32 c;
+ c = (int32)(f->red*255.0f + 0.5f);
+ i->red = (uint8)c;
+ c = (int32)(f->green*255.0f + 0.5f);
+ i->green = (uint8)c;
+ c = (int32)(f->blue*255.0f + 0.5f);
+ i->blue = (uint8)c;
+ c = (int32)(f->alpha*255.0f + 0.5f);
+ i->alpha = (uint8)c;
+}
+
+inline void convColor(RGBAf *f, const RGBA *i){
+ f->red = i->red/255.0f;
+ f->green = i->green/255.0f;
+ f->blue = i->blue/255.0f;
+ f->alpha = i->alpha/255.0f;
+}
+
+struct TexCoords
+{
+ float32 u, v;
+};
+inline bool32 equal(const TexCoords &t1, const TexCoords &t2) { return t1.u == t2.u && t1.v == t2.v; }
+
+struct V2d;
+struct V3d;
+struct Quat;
+struct Matrix;
+
+struct V2d
+{
+ float32 x, y;
+ void set(float32 x, float32 y){
+ this->x = x; this->y = y; }
+};
+
+inline V2d makeV2d(float32 x, float32 y) { V2d v; v.x = x; v.y = y; return v; }
+inline bool32 equal(const V2d &v1, const V2d &v2) { return v1.x == v2.x && v1.y == v2.y; }
+inline V2d neg(const V2d &a) { return makeV2d(-a.x, -a.y); }
+inline V2d add(const V2d &a, const V2d &b) { return makeV2d(a.x+b.x, a.y+b.y); }
+inline V2d sub(const V2d &a, const V2d &b) { return makeV2d(a.x-b.x, a.y-b.y); }
+inline V2d scale(const V2d &a, float32 r) { return makeV2d(a.x*r, a.y*r); }
+inline float32 length(const V2d &v) { return sqrtf(v.x*v.x + v.y*v.y); }
+inline V2d normalize(const V2d &v) { return scale(v, 1.0f/length(v)); }
+
+struct V3d
+{
+ float32 x, y, z;
+ void set(float32 x, float32 y, float32 z){
+ this->x = x; this->y = y; this->z = z; }
+ static void transformPoints(V3d *out, const V3d *in, int32 n, const Matrix *m);
+ static void transformVectors(V3d *out, const V3d *in, int32 n, const Matrix *m);
+};
+
+inline V3d makeV3d(float32 x, float32 y, float32 z) { V3d v; v.x = x; v.y = y; v.z = z; return v; }
+inline bool32 equal(const V3d &v1, const V3d &v2) { return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z; }
+inline V3d neg(const V3d &a) { return makeV3d(-a.x, -a.y, -a.z); }
+inline V3d add(const V3d &a, const V3d &b) { return makeV3d(a.x+b.x, a.y+b.y, a.z+b.z); }
+inline V3d sub(const V3d &a, const V3d &b) { return makeV3d(a.x-b.x, a.y-b.y, a.z-b.z); }
+inline V3d scale(const V3d &a, float32 r) { return makeV3d(a.x*r, a.y*r, a.z*r); }
+inline float32 length(const V3d &v) { return sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); }
+inline V3d normalize(const V3d &v) { return scale(v, 1.0f/length(v)); }
+inline V3d setlength(const V3d &v, float32 l) { return scale(v, l/length(v)); }
+V3d cross(const V3d &a, const V3d &b);
+inline float32 dot(const V3d &a, const V3d &b) { return a.x*b.x + a.y*b.y + a.z*b.z; }
+inline V3d lerp(const V3d &a, const V3d &b, float32 r){
+ return makeV3d(a.x + r*(b.x - a.x),
+ a.y + r*(b.y - a.y),
+ a.z + r*(b.z - a.z));
+};
+
+struct V4d
+{
+ float32 x, y, z, w;
+};
+inline bool32 equal(const V4d &v1, const V4d &v2) { return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z && v1.w == v2.w; }
+
+enum CombineOp
+{
+ COMBINEREPLACE,
+ COMBINEPRECONCAT,
+ COMBINEPOSTCONCAT
+};
+
+
+Quat makeQuat(float32 w, float32 x, float32 y, float32 z);
+Quat makeQuat(float32 w, const V3d &vec);
+
+struct Quat
+{
+ // order is important for streaming and RW compatibility
+ float32 x, y, z, w;
+
+ static Quat rotation(float32 angle, const V3d &axis){
+ return makeQuat(cosf(angle/2.0f), scale(normalize(axis), sinf(angle/2.0f))); }
+ void set(float32 w, float32 x, float32 y, float32 z){
+ this->w = w; this->x = x; this->y = y; this->z = z; }
+ V3d vec(void){ return makeV3d(x, y, z); }
+
+ Quat *rotate(const V3d *axis, float32 angle, CombineOp op = rw::COMBINEPOSTCONCAT);
+};
+
+inline Quat makeQuat(float32 w, float32 x, float32 y, float32 z) { Quat q; q.x = x; q.y = y; q.z = z; q.w = w; return q; }
+inline Quat makeQuat(float32 w, const V3d &vec) { return makeQuat(w, vec.x, vec.y, vec.z); }
+inline Quat add(const Quat &q, const Quat &p) { return makeQuat(q.w+p.w, q.x+p.x, q.y+p.y, q.z+p.z); }
+inline Quat sub(const Quat &q, const Quat &p) { return makeQuat(q.w-p.w, q.x-p.x, q.y-p.y, q.z-p.z); }
+inline Quat negate(const Quat &q) { return makeQuat(-q.w, -q.x, -q.y, -q.z); }
+inline float32 dot(const Quat &q, const Quat &p) { return q.w*p.w + q.x*p.x + q.y*p.y + q.z*p.z; }
+inline Quat scale(const Quat &q, float32 r) { return makeQuat(q.w*r, q.x*r, q.y*r, q.z*r); }
+inline float32 length(const Quat &q) { return sqrtf(q.w*q.w + q.x*q.x + q.y*q.y + q.z*q.z); }
+inline Quat normalize(const Quat &q) { return scale(q, 1.0f/length(q)); }
+inline Quat conj(const Quat &q) { return makeQuat(q.w, -q.x, -q.y, -q.z); }
+Quat mult(const Quat &q, const Quat &p);
+inline V3d rotate(const V3d &v, const Quat &q) { return mult(mult(q, makeQuat(0.0f, v)), conj(q)).vec(); }
+Quat lerp(const Quat &q, const Quat &p, float32 r);
+Quat slerp(const Quat &q, const Quat &p, float32 a);
+
+struct RawMatrix
+{
+ V3d right;
+ float32 rightw;
+ V3d up;
+ float32 upw;
+ V3d at;
+ float32 atw;
+ V3d pos;
+ float32 posw;
+
+ // NB: this is dst = src2*src1, i.e. src1 is applied first, then src2
+ static void mult(RawMatrix *dst, RawMatrix *src1, RawMatrix *src2);
+ static void transpose(RawMatrix *dst, RawMatrix *src);
+ static void setIdentity(RawMatrix *dst);
+};
+
+struct Matrix
+{
+ enum Type {
+ TYPENORMAL = 1,
+ TYPEORTHOGONAL = 2,
+ TYPEORTHONORMAL = 3,
+ TYPEMASK = 3
+ };
+ enum Flags {
+ IDENTITY = 0x20000
+ };
+ struct Tolerance {
+ float32 normal;
+ float32 orthogonal;
+ float32 identity;
+ };
+
+ V3d right;
+ uint32 flags;
+ V3d up;
+ uint32 pad1;
+ V3d at;
+ uint32 pad2;
+ V3d pos;
+ uint32 pad3;
+
+ static Matrix *create(void);
+ void destroy(void);
+ void setIdentity(void);
+ void optimize(Tolerance *tolerance = nil);
+ void update(void) { flags &= ~(int(IDENTITY) | int(TYPEMASK)); }
+ static Matrix *mult(Matrix *dst, const Matrix *src1, const Matrix *src2);
+ static Matrix *invert(Matrix *dst, const Matrix *src);
+ static Matrix *transpose(Matrix *dst, const Matrix *src);
+ Matrix *rotate(const V3d *axis, float32 angle, CombineOp op = rw::COMBINEPOSTCONCAT);
+ Matrix *rotate(const Quat &q, CombineOp op = rw::COMBINEPOSTCONCAT);
+ Matrix *translate(const V3d *translation, CombineOp op = rw::COMBINEPOSTCONCAT);
+ Matrix *scale(const V3d *scl, CombineOp op = rw::COMBINEPOSTCONCAT);
+ Matrix *transform(const Matrix *mat, CombineOp op = rw::COMBINEPOSTCONCAT);
+ Quat getRotation(void);
+ void lookAt(const V3d &dir, const V3d &up);
+
+ // helper functions. consider private
+ static void mult_(Matrix *dst, const Matrix *src1, const Matrix *src2);
+ static void invertOrthonormal(Matrix *dst, const Matrix *src);
+ static Matrix *invertGeneral(Matrix *dst, const Matrix *src);
+ static void makeRotation(Matrix *dst, const V3d *axis, float32 angle);
+ static void makeRotation(Matrix *dst, const Quat &q);
+private:
+ float32 normalError(void);
+ float32 orthogonalError(void);
+ float32 identityError(void);
+};
+
+inline void convMatrix(Matrix *dst, RawMatrix *src){
+ *dst = *(Matrix*)src;
+ dst->optimize();
+}
+
+inline void convMatrix(RawMatrix *dst, Matrix *src){
+ *dst = *(RawMatrix*)src;
+ dst->rightw = 0.0;
+ dst->upw = 0.0;
+ dst->atw = 0.0;
+ dst->posw = 1.0;
+}
+
+struct Line
+{
+ V3d start;
+ V3d end;
+};
+
+struct Rect
+{
+ int32 x, y;
+ int32 w, h;
+};
+
+struct Sphere
+{
+ V3d center;
+ float32 radius;
+};
+
+struct Plane
+{
+ V3d normal;
+ float32 distance;
+};
+
+struct BBox
+{
+ V3d sup;
+ V3d inf;
+
+ void initialize(V3d *point);
+ void addPoint(V3d *point);
+ void calculate(V3d *points, int32 n);
+ bool containsPoint(V3d *point);
+};
+
+enum PrimitiveType
+{
+ PRIMTYPENONE = 0,
+ PRIMTYPELINELIST,
+ PRIMTYPEPOLYLINE,
+ PRIMTYPETRILIST,
+ PRIMTYPETRISTRIP,
+ PRIMTYPETRIFAN,
+ PRIMTYPEPOINTLIST
+};
+
+/*
+ * Memory
+ */
+
+void memNative32_func(void *data, uint32 size);
+void memNative16_func(void *data, uint32 size);
+void memLittle32_func(void *data, uint32 size);
+void memLittle16_func(void *data, uint32 size);
+
+#ifdef BIGENDIAN
+inline void memNative32(void *data, uint32 size) { memNative32_func(data, size); }
+inline void memNative16(void *data, uint32 size) { memNative16_func(data, size); }
+inline void memLittle32(void *data, uint32 size) { memLittle32_func(data, size); }
+inline void memLittle16(void *data, uint32 size) { memLittle16_func(data, size); }
+#define ASSERTLITTLE assert(0 && "unsafe code on big-endian")
+#else
+inline void memNative32(void *data, uint32 size) { }
+inline void memNative16(void *data, uint32 size) { }
+inline void memLittle32(void *data, uint32 size) { }
+inline void memLittle16(void *data, uint32 size) { }
+#define ASSERTLITTLE
+#endif
+
+/*
+ * Streams
+ */
+
+void makePath(char *filename);
+
+class Stream
+{
+public:
+ virtual ~Stream(void) { close(); }
+ virtual void close(void) {}
+ virtual uint32 write8(const void *data, uint32 length) = 0;
+ virtual uint32 read8(void *data, uint32 length) = 0;
+ virtual void seek(int32 offset, int32 whence = 1) = 0;
+ virtual uint32 tell(void) = 0;
+ virtual bool eof(void) = 0;
+ uint32 write32(const void *data, uint32 length);
+ uint32 write16(const void *data, uint32 length);
+ uint32 read32(void *data, uint32 length);
+ uint32 read16(void *data, uint32 length);
+ int32 writeI8(int8 val);
+ int32 writeU8(uint8 val);
+ int32 writeI16(int16 val);
+ int32 writeU16(uint16 val);
+ int32 writeI32(int32 val);
+ int32 writeU32(uint32 val);
+ int32 writeF32(float32 val);
+ int8 readI8(void);
+ uint8 readU8(void);
+ int16 readI16(void);
+ uint16 readU16(void);
+ int32 readI32(void);
+ uint32 readU32(void);
+ float32 readF32(void);
+};
+
+class StreamMemory : public Stream
+{
+public:
+ uint8 *data;
+ uint32 length;
+ uint32 capacity;
+ uint32 position;
+
+ void close(void);
+ uint32 write8(const void *data, uint32 length);
+ uint32 read8(void *data, uint32 length);
+ void seek(int32 offset, int32 whence = 1);
+ uint32 tell(void);
+ bool eof(void);
+ StreamMemory *open(uint8 *data, uint32 length, uint32 capacity = 0);
+ uint32 getLength(void);
+
+ enum {
+ S_EOF = 0xFFFFFFFF
+ };
+};
+
+class StreamFile : public Stream
+{
+public:
+ void *file;
+ StreamFile(void) { file = nil; }
+ void close(void);
+ uint32 write8(const void *data, uint32 length);
+ uint32 read8(void *data, uint32 length);
+ void seek(int32 offset, int32 whence = 1);
+ uint32 tell(void);
+ bool eof(void);
+ StreamFile *open(const char *path, const char *mode);
+};
+
+enum Platform
+{
+ PLATFORM_NULL = 0,
+ // D3D7
+ PLATFORM_GL = 2,
+ // MAC
+ PLATFORM_PS2 = 4,
+ PLATFORM_XBOX = 5,
+ // GAMECUBE
+ // SOFTRAS
+ PLATFORM_D3D8 = 8,
+ PLATFORM_D3D9 = 9,
+ // PSP
+
+ // non-stock-RW platforms
+
+ PLATFORM_WDGL = 11, // WarDrum OpenGL
+ PLATFORM_GL3 = 12, // my GL3 implementation
+
+ NUM_PLATFORMS,
+
+ FOURCC_PS2 = 0x00325350 // 'PS2\0'
+};
+
+#define MAKEPLUGINID(v, id) (((v & 0xFFFFFF) << 8) | (id & 0xFF))
+#define MAKEPIPEID(v, id) (((v & 0xFFFF) << 16) | (id & 0xFFFF))
+
+enum VendorID
+{
+ VEND_CORE = 0,
+ VEND_CRITERIONTK = 1,
+ VEND_CRITERIONINT = 4,
+ VEND_CRITERIONWORLD = 5,
+ // Used for rasters (platform-specific)
+ VEND_RASTER = 10,
+ // Used for driver/device allocation tags
+ VEND_DRIVER = 11
+};
+
+// TODO: modules (VEND_CRITERIONINT)
+
+enum PluginID
+{
+ // Core
+ ID_NAOBJECT = MAKEPLUGINID(VEND_CORE, 0x00),
+ ID_STRUCT = MAKEPLUGINID(VEND_CORE, 0x01),
+ ID_STRING = MAKEPLUGINID(VEND_CORE, 0x02),
+ ID_EXTENSION = MAKEPLUGINID(VEND_CORE, 0x03),
+ ID_CAMERA = MAKEPLUGINID(VEND_CORE, 0x05),
+ ID_TEXTURE = MAKEPLUGINID(VEND_CORE, 0x06),
+ ID_MATERIAL = MAKEPLUGINID(VEND_CORE, 0x07),
+ ID_MATLIST = MAKEPLUGINID(VEND_CORE, 0x08),
+ ID_WORLD = MAKEPLUGINID(VEND_CORE, 0x0B),
+ ID_MATRIX = MAKEPLUGINID(VEND_CORE, 0x0D),
+ ID_FRAMELIST = MAKEPLUGINID(VEND_CORE, 0x0E),
+ ID_GEOMETRY = MAKEPLUGINID(VEND_CORE, 0x0F),
+ ID_CLUMP = MAKEPLUGINID(VEND_CORE, 0x10),
+ ID_LIGHT = MAKEPLUGINID(VEND_CORE, 0x12),
+ ID_ATOMIC = MAKEPLUGINID(VEND_CORE, 0x14),
+ ID_TEXTURENATIVE = MAKEPLUGINID(VEND_CORE, 0x15),
+ ID_TEXDICTIONARY = MAKEPLUGINID(VEND_CORE, 0x16),
+ ID_IMAGE = MAKEPLUGINID(VEND_CORE, 0x18),
+ ID_GEOMETRYLIST = MAKEPLUGINID(VEND_CORE, 0x1A),
+ ID_ANIMANIMATION = MAKEPLUGINID(VEND_CORE, 0x1B),
+ ID_RIGHTTORENDER = MAKEPLUGINID(VEND_CORE, 0x1F),
+ ID_UVANIMDICT = MAKEPLUGINID(VEND_CORE, 0x2B),
+
+ // Toolkit
+ ID_SKYMIPMAP = MAKEPLUGINID(VEND_CRITERIONTK, 0x10),
+ ID_SKIN = MAKEPLUGINID(VEND_CRITERIONTK, 0x16),
+ ID_HANIM = MAKEPLUGINID(VEND_CRITERIONTK, 0x1E),
+ ID_USERDATA = MAKEPLUGINID(VEND_CRITERIONTK, 0x1F),
+ ID_MATFX = MAKEPLUGINID(VEND_CRITERIONTK, 0x20),
+ ID_ANISOT = MAKEPLUGINID(VEND_CRITERIONTK, 0x27),
+ ID_PDS = MAKEPLUGINID(VEND_CRITERIONTK, 0x31),
+ ID_ADC = MAKEPLUGINID(VEND_CRITERIONTK, 0x34),
+ ID_UVANIMATION = MAKEPLUGINID(VEND_CRITERIONTK, 0x35),
+
+ // World
+ ID_MESH = MAKEPLUGINID(VEND_CRITERIONWORLD, 0x0E),
+ ID_NATIVEDATA = MAKEPLUGINID(VEND_CRITERIONWORLD, 0x10),
+ ID_VERTEXFMT = MAKEPLUGINID(VEND_CRITERIONWORLD, 0x11),
+
+ // custom native raster
+ ID_RASTERGL = MAKEPLUGINID(VEND_RASTER, PLATFORM_GL),
+ ID_RASTERPS2 = MAKEPLUGINID(VEND_RASTER, PLATFORM_PS2),
+ ID_RASTERXBOX = MAKEPLUGINID(VEND_RASTER, PLATFORM_XBOX),
+ ID_RASTERD3D8 = MAKEPLUGINID(VEND_RASTER, PLATFORM_D3D8),
+ ID_RASTERD3D9 = MAKEPLUGINID(VEND_RASTER, PLATFORM_D3D9),
+ ID_RASTERWDGL = MAKEPLUGINID(VEND_RASTER, PLATFORM_WDGL),
+ ID_RASTERGL3 = MAKEPLUGINID(VEND_RASTER, PLATFORM_GL3),
+
+ // anything driver/device related (only as allocation tag)
+ ID_DRIVER = MAKEPLUGINID(VEND_DRIVER, 0)
+};
+
+enum CoreModuleID
+{
+ ID_NAMODULE = MAKEPLUGINID(VEND_CRITERIONINT, 0x00),
+ // vector
+ // matrix
+ ID_FRAMEMODULE = MAKEPLUGINID(VEND_CRITERIONINT, 0x03),
+ // stream
+ // camera
+ ID_IMAGEMODULE = MAKEPLUGINID(VEND_CRITERIONINT, 0x06),
+ ID_RASTERMODULE = MAKEPLUGINID(VEND_CRITERIONINT, 0x07),
+ ID_TEXTUREMODULE = MAKEPLUGINID(VEND_CRITERIONINT, 0x08)
+ // pip
+ // immediate
+ // resources
+ // device
+ // color
+ // unused
+ // error
+ // metrics
+ // driver
+ // chunk group
+};
+
+#define ECODE(c, s) c
+
+enum Errors
+{
+ ERR_NONE = 0x80000000,
+#include "base.err"
+};
+
+#undef ECODE
+
+extern int32 version;
+extern int32 build;
+extern int32 platform;
+extern bool32 streamAppendFrames;
+extern char *debugFile;
+
+int strcmp_ci(const char *s1, const char *s2);
+int strncmp_ci(const char *s1, const char *s2, int n);
+
+// 0x04000000 3.1
+// 0x08000000 3.2
+// 0x0C000000 3.3
+// 0x10000000 3.4
+// 0x14000000 3.5
+// 0x18000000 3.6
+// 0x1C000000 3.7
+
+inline uint32
+libraryIDPack(int version, int build)
+{
+ if(version <= 0x31000)
+ return version>>8;
+ return ((version-0x30000) & 0x3FF00) << 14 | (version&0x3F) << 16 |
+ (build & 0xFFFF);
+}
+
+inline int
+libraryIDUnpackVersion(uint32 libid)
+{
+ if(libid & 0xFFFF0000)
+ return ((libid>>14 & 0x3FF00) + 0x30000) |
+ (libid>>16 & 0x3F);
+ else
+ return libid<<8;
+}
+
+inline int
+libraryIDUnpackBuild(uint32 libid)
+{
+ if(libid & 0xFFFF0000)
+ return libid & 0xFFFF;
+ else
+ return 0;
+}
+
+struct ChunkHeaderInfo
+{
+ uint32 type;
+ uint32 length;
+ uint32 version, build;
+};
+
+// TODO?: make these methods of ChunkHeaderInfo?
+bool writeChunkHeader(Stream *s, int32 type, int32 size);
+bool readChunkHeaderInfo(Stream *s, ChunkHeaderInfo *header);
+bool findChunk(Stream *s, uint32 type, uint32 *length, uint32 *version);
+
+int32 findPointer(void *p, void **list, int32 num);
+uint8 *getFileContents(const char *name, uint32 *len);
+}
diff --git a/src/rwcharset.h b/src/rwcharset.h
new file mode 100644
index 0000000..3988654
--- /dev/null
+++ b/src/rwcharset.h
@@ -0,0 +1,25 @@
+namespace rw {
+
+struct Charset
+{
+ struct Desc {
+ int32 count; // num of chars
+ int32 tileWidth, tileHeight; // chars in raster
+ int32 width, height; // of char
+ int32 width_internal, height_internal; // + border
+ } desc;
+ Raster *raster;
+
+ static bool32 open(void);
+ static void close(void);
+ static Charset *create(const RGBA *foreground, const RGBA *background);
+ void destroy(void);
+ Charset *setColors(const RGBA *foreground, const RGBA *background);
+ void print(const char *str, int32 x, int32 y, bool32 hideSpaces);
+ void printBuffered(const char *str, int32 x, int32 y, bool32 hideSpaces);
+ static void flushBuffer(void);
+private:
+ void printChar(int32 c, int32 x, int32 y);
+};
+
+}
diff --git a/src/rwengine.h b/src/rwengine.h
new file mode 100644
index 0000000..995f3ce
--- /dev/null
+++ b/src/rwengine.h
@@ -0,0 +1,275 @@
+namespace rw {
+
+// uhhhm..... why are these not actual functions?
+enum DeviceReq
+{
+ // Device initialization before Engine/Driver plugins are opened
+ DEVICEINIT,
+ // Device de-initialization after Engine/Driver plugins are closed
+ DEVICETERM,
+
+ // Device/Context creation
+ DEVICEOPEN,
+ // Device/Context shutdown
+ DEVICECLOSE,
+
+ // Device initialization after Engine/Driver plugins are opened
+ DEVICEFINALIZE,
+ // TODO? counterpart to FINALIZE?
+
+ // Video adapters
+ DEVICEGETNUMSUBSYSTEMS,
+ DEVICEGETCURRENTSUBSYSTEM,
+ DEVICESETSUBSYSTEM,
+ DEVICEGETSUBSSYSTEMINFO,
+
+ // Video modes
+ DEVICEGETNUMVIDEOMODES,
+ DEVICEGETCURRENTVIDEOMODE,
+ DEVICESETVIDEOMODE,
+ DEVICEGETVIDEOMODEINFO,
+
+ // Multisampling
+ DEVICEGETMAXMULTISAMPLINGLEVELS,
+ DEVICEGETMULTISAMPLINGLEVELS,
+ DEVICESETMULTISAMPLINGLEVELS,
+
+};
+
+typedef int DeviceSystem(DeviceReq req, void *arg, int32 n);
+
+struct Camera;
+struct Image;
+struct Texture;
+struct Raster;
+class ObjPipeline;
+
+// This is for the render device, we only have one
+struct Device
+{
+ float32 zNear, zFar;
+ void (*beginUpdate)(Camera*);
+ void (*endUpdate)(Camera*);
+ void (*clearCamera)(Camera*, RGBA *col, uint32 mode);
+ void (*showRaster)(Raster *raster, uint32 flags);
+ bool32 (*rasterRenderFast)(Raster *raster, int32 x, int32 y);
+ void (*setRenderState)(int32 state, void *value);
+ void *(*getRenderState)(int32 state);
+
+ void (*im2DRenderLine)(void *vertices, int32 numVertices, int32 vert1, int32 vert2);
+ void (*im2DRenderTriangle)(void *vertices, int32 numVertices, int32 vert1, int32 vert2, int32 vert3);
+ void (*im2DRenderPrimitive)(PrimitiveType primType, void *vertices, int32 numVertices);
+ void (*im2DRenderIndexedPrimitive)(PrimitiveType primType, void *vertices, int32 numVertices, void *indices, int32 numIndices);
+
+ void (*im3DTransform)(void *vertices, int32 numVertices, Matrix *world, uint32 flags);
+ void (*im3DRenderPrimitive)(PrimitiveType primType);
+ void (*im3DRenderIndexedPrimitive)(PrimitiveType primType, void *indices, int32 numIndices);
+ void (*im3DEnd)(void);
+
+ DeviceSystem *system;
+};
+
+// This is for platform-dependent but portable things
+// so the engine has one for every platform
+struct Driver
+{
+ ObjPipeline *defaultPipeline;
+ int32 rasterNativeOffset;
+
+ Raster* (*rasterCreate)(Raster*);
+ uint8 *(*rasterLock)(Raster*, int32 level, int32 lockMode);
+ void (*rasterUnlock)(Raster*, int32 level);
+ uint8 *(*rasterLockPalette)(Raster*, int32 lockMode);
+ void (*rasterUnlockPalette)(Raster*);
+ int32 (*rasterNumLevels)(Raster*);
+ bool32 (*imageFindRasterFormat)(Image *img, int32 type,
+ int32 *width, int32 *height, int32 *depth, int32 *format);
+ bool32 (*rasterFromImage)(Raster*, Image*);
+ Image *(*rasterToImage)(Raster*);
+
+ static PluginList s_plglist[NUM_PLATFORMS];
+ static int32 registerPlugin(int32 platform, int32 size, uint32 id,
+ Constructor ctor, Destructor dtor){
+ return s_plglist[platform].registerPlugin(size, id,
+ ctor, dtor, nil);
+ }
+};
+
+struct EngineOpenParams;
+
+enum MemHint
+{
+ MEMDUR_NA = 0,
+ // used inside function
+ MEMDUR_FUNCTION = 0x10000,
+ // used for one frame
+ MEMDUR_FRAME = 0x20000,
+ // used for longer time
+ MEMDUR_EVENT = 0x30000,
+ // used while the engine is running
+ MEMDUR_GLOBAL = 0x40000
+};
+
+struct MemoryFunctions
+{
+ void *(*rwmalloc)(size_t sz, uint32 hint);
+ void *(*rwrealloc)(void *p, size_t sz, uint32 hint);
+ void (*rwfree)(void *p);
+ // These are temporary until we have better error handling
+ // TODO: Maybe don't put them here since they shouldn't really be switched out
+ void *(*rwmustmalloc)(size_t sz, uint32 hint);
+ void *(*rwmustrealloc)(void *p, size_t sz, uint32 hint);
+};
+
+struct FileFunctions
+{
+ void *(*rwfopen)(const char *path, const char *mode);
+ int (*rwfclose)(void *fp);
+ int (*rwfseek)(void *fp, long offset, int whence);
+ long (*rwftell)(void *fp);
+ size_t (*rwfread)(void *ptr, size_t size, size_t nmemb, void *fp);
+ size_t (*rwfwrite)(const void *ptr, size_t size, size_t nmemb, void *fp);
+ int (*rwfeof)(void *fp);
+};
+
+struct SubSystemInfo
+{
+ char name[80];
+};
+
+enum VideoModeFlags
+{
+ VIDEOMODEEXCLUSIVE = 1
+};
+
+struct VideoMode
+{
+ int32 width;
+ int32 height;
+ int32 depth;
+ uint32 flags;
+};
+
+struct Camera;
+struct World;
+
+// This is for platform independent things
+// TODO: move more stuff into this
+struct Engine
+{
+ enum State {
+ Dead = 0,
+ Initialized,
+ Opened,
+ Started
+ };
+ Camera *currentCamera;
+ World *currentWorld;
+ LinkList frameDirtyList;
+ FileFunctions filefuncs;
+
+ // Dynamically allocated because of plugins
+ Driver *driver[NUM_PLATFORMS];
+ Device device;
+ ObjPipeline *dummyDefaultPipeline;
+
+ // These must always be available
+ static MemoryFunctions memfuncs;
+ static State state;
+
+ static bool32 init(MemoryFunctions *memfuncs = nil);
+ static bool32 open(EngineOpenParams*);
+ static bool32 start(void);
+ static void term(void);
+ static void close(void);
+ static void stop(void);
+
+ static int32 getNumSubSystems(void);
+ static int32 getCurrentSubSystem(void);
+ static bool32 setSubSystem(int32 subsys);
+ static SubSystemInfo *getSubSystemInfo(SubSystemInfo *info, int32 subsys);
+
+ static int32 getNumVideoModes(void);
+ static int32 getCurrentVideoMode(void);
+ static bool32 setVideoMode(int32 mode);
+ static VideoMode *getVideoModeInfo(VideoMode *info, int32 mode);
+
+ static uint32 getMaxMultiSamplingLevels(void);
+ static uint32 getMultiSamplingLevels(void);
+ static bool32 setMultiSamplingLevels(uint32 levels);
+
+ static PluginList s_plglist;
+ static int32 registerPlugin(int32 size, uint32 id,
+ Constructor ctor, Destructor dtor){
+ return s_plglist.registerPlugin(size, id, ctor, dtor, nil);
+ }
+};
+
+extern Engine *engine;
+
+#define RWTOSTR_(X) #X
+#define RWTOSTR(X) RWTOSTR_(X)
+#define RWHERE "file: " __FILE__ " line: " RWTOSTR(__LINE__)
+
+extern const char *allocLocation;
+
+inline void *malloc_LOC(size_t sz, uint32 hint, const char *here) { allocLocation = here; return rw::Engine::memfuncs.rwmalloc(sz,hint); }
+inline void *realloc_LOC(void *p, size_t sz, uint32 hint, const char *here) { allocLocation = here; return rw::Engine::memfuncs.rwrealloc(p,sz,hint); }
+inline void *mustmalloc_LOC(size_t sz, uint32 hint, const char *here) { allocLocation = here; return rw::Engine::memfuncs.rwmustmalloc(sz,hint); }
+inline void *mustrealloc_LOC(void *p, size_t sz, uint32 hint, const char *here) { allocLocation = here; return rw::Engine::memfuncs.rwmustrealloc(p,sz,hint); }
+
+char *strdup_LOC(const char *s, uint32 hint, const char *here);
+
+#define rwMalloc(s, h) rw::malloc_LOC(s,h,RWHERE)
+#define rwMallocT(t, s, h) (t*)rw::malloc_LOC((s)*sizeof(t),h,RWHERE)
+#define rwRealloc(p, s, h) rw::realloc_LOC(p,s,h,RWHERE)
+#define rwReallocT(t, p, s, h) (t*)rw::realloc_LOC(p,(s)*sizeof(t),h,RWHERE)
+#define rwFree(p) rw::Engine::memfuncs.rwfree(p)
+#define rwNew(s, h) rw::mustmalloc_LOC(s,h,RWHERE)
+#define rwNewT(t, s, h) (t*)rw::mustmalloc_LOC((s)*sizeof(t),h,RWHERE)
+#define rwResize(p, s, h) rw::mustrealloc_LOC(p,s,h,RWHERE)
+#define rwResizeT(t, p, s, h) (t*)rw::mustrealloc_LOC(p,(s)*sizeof(t),h,RWHERE)
+#define rwStrdup(s, h) rw::strdup_LOC(s,h,RWHERE)
+
+extern MemoryFunctions defaultMemfuncs;
+extern MemoryFunctions managedMemfuncs;
+void printleaks(void); // when using managed mem funcs
+
+namespace null {
+ void beginUpdate(Camera*);
+ void endUpdate(Camera*);
+ void clearCamera(Camera*, RGBA *col, uint32 mode);
+ void showRaster(Raster*, uint32 flags);
+
+ void setRenderState(int32 state, void *value);
+ void *getRenderState(int32 state);
+
+ bool32 rasterRenderFast(Raster *raster, int32 x, int32 y);
+
+ Raster *rasterCreate(Raster*);
+ uint8 *rasterLock(Raster*, int32 level, int32 lockMode);
+ void rasterUnlock(Raster*, int32 level);
+ uint8 *rasterLockPalette(Raster*, int32 lockMode);
+ void rasterUnlockPalette(Raster*);
+ int32 rasterNumLevels(Raster*);
+ bool32 imageFindRasterFormat(Image *img, int32 type,
+ int32 *width, int32 *height, int32 *depth, int32 *format);
+ bool32 rasterFromImage(Raster*, Image*);
+ Image *rasterToImage(Raster*);
+
+ void im2DRenderLine(void*, int32, int32, int32);
+ void im2DRenderTriangle(void*, int32, int32, int32, int32);
+ void im2DRenderPrimitive(PrimitiveType, void*, int32);
+ void im2DRenderIndexedPrimitive(PrimitiveType, void*, int32, void*, int32);
+
+ void im3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags);
+ void im3DRenderPrimitive(PrimitiveType primType);
+ void im3DRenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices);
+ void im3DEnd(void);
+
+ int deviceSystem(DeviceReq req, void *arg0, int32 n);
+
+ extern Device renderdevice;
+}
+
+}
diff --git a/src/rwerror.h b/src/rwerror.h
new file mode 100644
index 0000000..2575626
--- /dev/null
+++ b/src/rwerror.h
@@ -0,0 +1,25 @@
+namespace rw {
+
+struct Error
+{
+ uint32 plugin;
+ uint32 code;
+};
+
+void setError(Error *e);
+Error *getError(Error *e);
+
+#define _ERRORCODE(code, ...) code
+char *dbgsprint(uint32 code, ...);
+
+/* ecode is supposed to be in format "(errorcode, printf-arguments..)" */
+#define RWERROR(ecode) do{ \
+ rw::Error _e; \
+ _e.plugin = PLUGIN_ID; \
+ _e.code = _ERRORCODE ecode; \
+ fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \
+ fprintf(stderr, "%s\n", rw::dbgsprint ecode); \
+ rw::setError(&_e); \
+}while(0)
+
+}
diff --git a/src/rwobjects.h b/src/rwobjects.h
new file mode 100644
index 0000000..da95a5c
--- /dev/null
+++ b/src/rwobjects.h
@@ -0,0 +1,906 @@
+#include <stddef.h>
+
+namespace rw {
+
+struct Object
+{
+ uint8 type;
+ uint8 subType;
+ uint8 flags;
+ uint8 privateFlags;
+ void *parent;
+
+ void init(uint8 type, uint8 subType){
+ this->type = type;
+ this->subType = subType;
+ this->flags = 0;
+ this->privateFlags = 0;
+ this->parent = nil;
+ }
+ void copy(Object *o){
+ this->type = o->type;
+ this->subType = o->subType;
+ this->flags = o->flags;
+ this->privateFlags = o->privateFlags;
+ this->parent = nil;
+ }
+};
+
+struct Frame
+{
+ PLUGINBASE
+ typedef Frame *(*Callback)(Frame *f, void *data);
+ enum { ID = 0 };
+ enum { // private flags
+ // The hierarchy has unsynched frames
+ HIERARCHYSYNCLTM = 0x01, // LTM not synched
+ HIERARCHYSYNCOBJ = 0x02, // attached objects not synched
+ HIERARCHYSYNC = HIERARCHYSYNCLTM | HIERARCHYSYNCOBJ,
+ // This frame is not synched
+ SUBTREESYNCLTM = 0x04,
+ SUBTREESYNCOBJ = 0x08,
+ SUBTREESYNC = SUBTREESYNCLTM | SUBTREESYNCOBJ,
+ SYNCLTM = HIERARCHYSYNCLTM | SUBTREESYNCLTM,
+ SYNCOBJ = HIERARCHYSYNCOBJ | SUBTREESYNCOBJ
+ // STATIC = 0x10
+ };
+
+ Object object;
+ LLLink inDirtyList;
+ LinkList objectList;
+ Matrix matrix;
+ Matrix ltm;
+
+ Frame *child;
+ Frame *next;
+ Frame *root;
+
+ static int32 numAllocated;
+
+ static Frame *create(void);
+ Frame *cloneHierarchy(void);
+ void destroy(void);
+ void destroyHierarchy(void);
+ Frame *addChild(Frame *f, bool32 append = 0);
+ Frame *removeChild(void);
+ Frame *forAllChildren(Callback cb, void *data);
+ Frame *getParent(void) const {
+ return (Frame*)this->object.parent; }
+ int32 count(void);
+ bool32 dirty(void) const {
+ return !!(this->root->object.privateFlags & HIERARCHYSYNC); }
+ Matrix *getLTM(void);
+ void rotate(const V3d *axis, float32 angle, CombineOp op = rw::COMBINEPOSTCONCAT);
+ void rotate(const Quat *q, CombineOp op = rw::COMBINEPOSTCONCAT);
+ void translate(const V3d *trans, CombineOp op = rw::COMBINEPOSTCONCAT);
+ void scale(const V3d *scale, CombineOp op = rw::COMBINEPOSTCONCAT);
+ void transform(const Matrix *mat, CombineOp op = rw::COMBINEPOSTCONCAT);
+ void updateObjects(void);
+
+
+ void syncHierarchyLTM(void);
+ void setHierarchyRoot(Frame *root);
+ Frame *cloneAndLink(void);
+ void purgeClone(void);
+
+#ifndef RWPUBLIC
+ static void registerModule(void);
+#endif
+ static void syncDirty(void);
+};
+
+struct FrameList_
+{
+ int32 numFrames;
+ Frame **frames;
+
+ FrameList_ *streamRead(Stream *stream);
+ void streamWrite(Stream *stream);
+ static uint32 streamGetSize(Frame *f);
+};
+Frame **makeFrameList(Frame *frame, Frame **flist);
+
+struct ObjectWithFrame
+{
+ typedef void (*Sync)(ObjectWithFrame*);
+
+ Object object;
+ LLLink inFrame;
+ Sync syncCB;
+
+ void setFrame(Frame *f){
+ if(this->object.parent)
+ this->inFrame.remove();
+ this->object.parent = f;
+ if(f){
+ f->objectList.add(&this->inFrame);
+ f->updateObjects();
+ }
+ }
+ void sync(void){ this->syncCB(this); }
+ static ObjectWithFrame *fromFrame(LLLink *lnk){
+ return LLLinkGetData(lnk, ObjectWithFrame, inFrame);
+ }
+};
+
+struct Image
+{
+ int32 flags;
+ int32 width, height;
+ int32 depth;
+ int32 bpp; // bytes per pixel
+ int32 stride;
+ uint8 *pixels;
+ uint8 *palette;
+
+ static int32 numAllocated;
+
+ static Image *create(int32 width, int32 height, int32 depth);
+ void destroy(void);
+ void allocate(void);
+ void free(void);
+ void setPixels(uint8 *pixels);
+ void setPixelsDXT(int32 type, uint8 *pixels);
+ void setPalette(uint8 *palette);
+ void compressPalette(void); // turn 8 bit into 4 bit if possible
+ bool32 hasAlpha(void);
+ void convertTo32(void);
+ void palettize(int32 depth);
+ void unpalettize(bool forceAlpha = false);
+ void makeMask(void);
+ void applyMask(Image *mask);
+ void removeMask(void);
+ Image *extractMask(void);
+
+ static void setSearchPath(const char*);
+ static void printSearchPath(void);
+ static char *getFilename(const char*);
+ static Image *read(const char *imageName);
+ static Image *readMasked(const char *imageName, const char *maskName);
+
+
+ typedef Image *(*fileRead)(const char *afilename);
+ typedef void (*fileWrite)(Image *image, const char *filename);
+ static bool32 registerFileFormat(const char *ext, fileRead read, fileWrite write);
+
+#ifndef RWPUBLIC
+ static void registerModule(void);
+#endif
+};
+
+Image *readTGA(const char *filename);
+void writeTGA(Image *image, const char *filename);
+Image *readBMP(const char *filename);
+void writeBMP(Image *image, const char *filename);
+Image *readPNG(const char *filename);
+void writePNG(Image *image, const char *filename);
+
+enum { QUANTDEPTH = 8 };
+
+struct ColorQuant
+{
+ struct Node {
+ uint32 r, g, b, a;
+ int32 numPixels;
+ Node *parent;
+ Node *children[16];
+ LLLink link;
+
+ void destroy(void);
+ void addColor(RGBA color);
+ bool isLeaf(void) { for(int32 i = 0; i < 16; i++) if(this->children[i]) return false; return true; }
+ };
+
+ Node *root;
+ LinkList leaves;
+
+ void init(void);
+ void destroy(void);
+ Node *createNode(int32 level);
+ Node *getNode(Node *root, uint32 addr, int32 level);
+ Node *findNode(Node *root, uint32 addr, int32 level);
+ void reduceNode(Node *node);
+ void addColor(RGBA color);
+ uint8 findColor(RGBA color);
+ void addImage(Image *img);
+ void makePalette(int32 numColors, RGBA *colors);
+ void matchImage(uint8 *dstPixels, uint32 dstStride, Image *src);
+};
+
+// used to emulate d3d and xbox textures
+struct RasterLevels
+{
+ int32 numlevels;
+ uint32 format;
+ struct Level {
+ int32 width, height, size;
+ uint8 *data;
+ } levels[1]; // 0 is illegal :/
+};
+
+struct Raster
+{
+ enum { FLIPWAITVSYNCH = 1 };
+
+ PLUGINBASE
+ int32 platform;
+
+ // TODO: use bytes
+ int32 type;
+ int32 flags;
+ int32 privateFlags;
+ int32 format;
+ int32 width, height, depth;
+ int32 stride;
+ uint8 *pixels;
+ uint8 *palette;
+ // remember for locked rasters
+ uint8 *originalPixels;
+ int32 originalWidth;
+ int32 originalHeight;
+ int32 originalStride;
+ // subraster
+ Raster *parent;
+ int32 offsetX, offsetY;
+
+ static int32 numAllocated;
+
+ static Raster *create(int32 width, int32 height, int32 depth,
+ int32 format, int32 platform = 0);
+ void subRaster(Raster *parent, Rect *r);
+ void destroy(void);
+ static bool32 imageFindRasterFormat(Image *image, int32 type,
+ int32 *pWidth, int32 *pHeight, int32 *pDepth, int32 *pFormat, int32 platform = 0);
+ Raster *setFromImage(Image *image, int32 platform = 0);
+ static Raster *createFromImage(Image *image, int32 platform = 0);
+ Image *toImage(void);
+ uint8 *lock(int32 level, int32 lockMode);
+ void unlock(int32 level);
+ uint8 *lockPalette(int32 lockMode);
+ void unlockPalette(void);
+ int32 getNumLevels(void);
+ static int32 calculateNumLevels(int32 width, int32 height);
+ static bool formatHasAlpha(int32 format);
+
+ void show(uint32 flags);
+
+ static Raster *pushContext(Raster *raster);
+ static Raster *popContext(void);
+ static Raster *getCurrentContext(void);
+ bool32 renderFast(int32 x, int32 y);
+
+ static Raster *convertTexToCurrentPlatform(Raster *ras);
+#ifndef RWPUBLIC
+ static void registerModule(void);
+#endif
+
+ enum Format {
+ DEFAULT = 0,
+ C1555 = 0x0100,
+ C565 = 0x0200,
+ C4444 = 0x0300,
+ LUM8 = 0x0400,
+ C8888 = 0x0500,
+ C888 = 0x0600,
+ D16 = 0x0700,
+ D24 = 0x0800,
+ D32 = 0x0900,
+ C555 = 0x0A00,
+ AUTOMIPMAP = 0x1000,
+ PAL8 = 0x2000,
+ PAL4 = 0x4000,
+ MIPMAP = 0x8000
+ };
+ enum Type {
+ NORMAL = 0x00,
+ ZBUFFER = 0x01,
+ CAMERA = 0x02,
+ TEXTURE = 0x04,
+ CAMERATEXTURE = 0x05,
+ DONTALLOCATE = 0x80
+ };
+ enum LockMode {
+ LOCKWRITE = 1,
+ LOCKREAD = 2,
+ LOCKNOFETCH = 4, // don't fetch pixel data
+ LOCKRAW = 8,
+ };
+
+ enum
+ {
+ // from RW
+ PRIVATELOCK_READ = 0x02,
+ PRIVATELOCK_WRITE = 0x04,
+ PRIVATELOCK_READ_PALETTE = 0x08,
+ PRIVATELOCK_WRITE_PALETTE = 0x10,
+ };
+};
+
+void conv_RGBA8888_from_RGBA8888(uint8 *out, uint8 *in);
+void conv_BGRA8888_from_RGBA8888(uint8 *out, uint8 *in);
+void conv_RGBA8888_from_RGB888(uint8 *out, uint8 *in);
+void conv_BGRA8888_from_RGB888(uint8 *out, uint8 *in);
+void conv_RGB888_from_RGB888(uint8 *out, uint8 *in);
+void conv_BGR888_from_RGB888(uint8 *out, uint8 *in);
+void conv_ARGB1555_from_ARGB1555(uint8 *out, uint8 *in);
+void conv_ARGB1555_from_RGB555(uint8 *out, uint8 *in);
+void conv_RGBA5551_from_ARGB1555(uint8 *out, uint8 *in);
+void conv_ARGB1555_from_RGBA5551(uint8 *out, uint8 *in);
+void conv_RGBA8888_from_ARGB1555(uint8 *out, uint8 *in);
+void conv_ABGR1555_from_ARGB1555(uint8 *out, uint8 *in);
+inline void conv_8_from_8(uint8 *out, uint8 *in) { *out = *in; }
+// some swaps are the same, so these are just more descriptive names
+inline void conv_RGBA8888_from_BGRA8888(uint8 *out, uint8 *in) { conv_BGRA8888_from_RGBA8888(out, in); }
+inline void conv_RGB888_from_BGR888(uint8 *out, uint8 *in) { conv_BGR888_from_RGB888(out, in); }
+inline void conv_ARGB1555_from_ABGR1555(uint8 *out, uint8 *in) { conv_ABGR1555_from_ARGB1555(out, in); }
+
+void expandPal4(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h);
+void compressPal4(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h);
+void expandPal4_BE(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h);
+void compressPal4_BE(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h);
+void copyPal8(uint8 *dst, uint32 dststride, uint8 *src, uint32 srcstride, int32 w, int32 h);
+
+void flipDXT(int32 type, uint8 *dst, uint8 *src, uint32 width, uint32 height);
+
+
+#define IGNORERASTERIMP 0
+
+struct TexDictionary;
+
+struct Texture
+{
+ enum FilterMode {
+ NEAREST = 1,
+ LINEAR,
+ MIPNEAREST, // one mipmap
+ MIPLINEAR,
+ LINEARMIPNEAREST, // mipmap interpolated
+ LINEARMIPLINEAR
+ };
+ enum Addressing {
+ WRAP = 1,
+ MIRROR,
+ CLAMP,
+ BORDER
+ };
+
+ PLUGINBASE
+ Raster *raster;
+ TexDictionary *dict;
+ LLLink inDict;
+ char name[32];
+ char mask[32];
+ uint32 filterAddressing; // VVVVUUUU FFFFFFFF
+ int32 refCount;
+
+ LLLink inGlobalList; // actually not in RW
+
+ static int32 numAllocated;
+
+ static Texture *create(Raster *raster);
+ void addRef(void) { this->refCount++; }
+ void destroy(void);
+ static Texture *fromDict(LLLink *lnk){
+ return LLLinkGetData(lnk, Texture, inDict); }
+ FilterMode getFilter(void) { return (FilterMode)(filterAddressing & 0xFF); }
+ void setFilter(FilterMode f) { filterAddressing = (filterAddressing & ~0xFF) | f; }
+ Addressing getAddressU(void) { return (Addressing)((filterAddressing >> 8) & 0xF); }
+ Addressing getAddressV(void) { return (Addressing)((filterAddressing >> 12) & 0xF); }
+ void setAddressU(Addressing u) { filterAddressing = (filterAddressing & ~0xF00) | u<<8; }
+ void setAddressV(Addressing v) { filterAddressing = (filterAddressing & ~0xF000) | v<<12; }
+ static Texture *streamRead(Stream *stream);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+ static Texture *read(const char *name, const char *mask);
+ static Texture *streamReadNative(Stream *stream);
+ void streamWriteNative(Stream *stream);
+ uint32 streamGetSizeNative(void);
+
+ static Texture *(*findCB)(const char *name);
+ static Texture *(*readCB)(const char *name, const char *mask);
+ static void setLoadTextures(bool32); // default: true
+ static void setCreateDummies(bool32); // default: false
+ static void setMipmapping(bool32); // default: false
+ static void setAutoMipmapping(bool32); // default: false
+ static bool32 getMipmapping(void);
+ static bool32 getAutoMipmapping(void);
+
+ void setMaxAnisotropy(int32 maxaniso); // only if plugin is attached
+ int32 getMaxAnisotropy(void);
+
+#ifndef RWPUBLIC
+ static void registerModule(void);
+#endif
+};
+
+extern int32 anisotOffset;
+#define GETANISOTROPYEXT(texture) PLUGINOFFSET(int32, texture, rw::anisotOffset)
+void registerAnisotropyPlugin(void);
+int32 getMaxSupportedMaxAnisotropy(void);
+
+struct SurfaceProperties
+{
+ float32 ambient;
+ float32 specular;
+ float32 diffuse;
+};
+
+struct Material
+{
+ PLUGINBASE
+ Texture *texture;
+ RGBA color;
+ SurfaceProperties surfaceProps;
+ Pipeline *pipeline;
+ int32 refCount;
+
+ static int32 numAllocated;
+
+ static Material *create(void);
+ void addRef(void) { this->refCount++; }
+ Material *clone(void);
+ void destroy(void);
+ void setTexture(Texture *tex);
+ static Material *streamRead(Stream *stream);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+};
+
+void registerMaterialRightsPlugin(void);
+
+struct Mesh
+{
+ uint16 *indices;
+ uint32 numIndices;
+ Material *material;
+};
+
+struct MeshHeader
+{
+ enum {
+ TRISTRIP = 1
+ };
+ uint32 flags;
+ uint16 numMeshes;
+ uint16 serialNum;
+ uint32 totalIndices;
+ uint32 pad; // needed for alignment of Meshes
+ // after this the meshes
+
+ Mesh *getMeshes(void) { return (Mesh*)(this+1); }
+ void setupIndices(void);
+ uint32 guessNumTriangles(void);
+};
+
+struct Geometry;
+
+struct MorphTarget
+{
+ Geometry *parent;
+ Sphere boundingSphere;
+ V3d *vertices;
+ V3d *normals;
+
+ Sphere calculateBoundingSphere(void) const;
+};
+
+struct InstanceDataHeader
+{
+ uint32 platform;
+};
+
+struct Triangle
+{
+ uint16 v[3];
+ uint16 matId;
+};
+
+struct MaterialList
+{
+ Material **materials;
+ int32 numMaterials;
+ int32 space;
+
+ void init(void);
+ void deinit(void);
+ int32 appendMaterial(Material *mat);
+ int32 findIndex(Material *mat);
+ static MaterialList *streamRead(Stream *stream, MaterialList *matlist);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+};
+
+struct Geometry
+{
+ PLUGINBASE
+ enum { ID = 8 };
+ Object object;
+ uint32 flags;
+ uint16 lockedSinceInst;
+ int32 numTriangles;
+ int32 numVertices;
+ int32 numMorphTargets;
+ int32 numTexCoordSets;
+
+ Triangle *triangles;
+ RGBA *colors;
+ TexCoords *texCoords[8];
+
+ MorphTarget *morphTargets;
+ MaterialList matList;
+
+ MeshHeader *meshHeader;
+ InstanceDataHeader *instData;
+
+ int32 refCount;
+
+ static int32 numAllocated;
+
+ static Geometry *create(int32 numVerts, int32 numTris, uint32 flags);
+ void addRef(void) { this->refCount++; }
+ void destroy(void);
+ void lock(int32 lockFlags);
+ void unlock(void);
+ void addMorphTargets(int32 n);
+ void calculateBoundingSphere(void);
+ bool32 hasColoredMaterial(void);
+ void allocateData(void);
+ MeshHeader *allocateMeshes(int32 numMeshes, uint32 numIndices, bool32 noIndices);
+ void generateTriangles(int8 *adc = nil);
+ void buildMeshes(void);
+ void buildTristrips(void); // private, used by buildMeshes
+ void correctTristripWinding(void);
+ void removeUnusedMaterials(void);
+ static Geometry *streamRead(Stream *stream);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+
+ enum Flags
+ {
+ TRISTRIP = 0x01,
+ POSITIONS = 0x02,
+ TEXTURED = 0x04,
+ PRELIT = 0x08,
+ NORMALS = 0x10,
+ LIGHT = 0x20,
+ MODULATE = 0x40,
+ TEXTURED2 = 0x80,
+ // When this flag is set the geometry has
+ // native geometry. When streamed out this geometry
+ // is written out instead of the platform independent data.
+ // When streamed in with this flag, the geometry is mostly empty.
+ NATIVE = 0x01000000,
+ // Just for documentation: RW sets this flag
+ // to prevent rendering when executing a pipeline,
+ // so only instancing will occur.
+ // librw's pipelines are different so it's unused here.
+ NATIVEINSTANCE = 0x02000000
+ };
+
+ enum LockFlags
+ {
+ LOCKPOLYGONS = 0x0001,
+ LOCKVERTICES = 0x0002,
+ LOCKNORMALS = 0x0004,
+ LOCKPRELIGHT = 0x0008,
+
+ LOCKTEXCOORDS = 0x0010,
+ LOCKTEXCOORDS1 = 0x0010,
+ LOCKTEXCOORDS2 = 0x0020,
+ LOCKTEXCOORDS3 = 0x0040,
+ LOCKTEXCOORDS4 = 0x0080,
+ LOCKTEXCOORDS5 = 0x0100,
+ LOCKTEXCOORDS6 = 0x0200,
+ LOCKTEXCOORDS7 = 0x0400,
+ LOCKTEXCOORDS8 = 0x0800,
+ LOCKTEXCOORDSALL = 0x0ff0,
+
+ LOCKALL = 0x0fff
+ };
+};
+
+void registerMeshPlugin(void);
+void registerNativeDataPlugin(void);
+
+struct Clump;
+struct World;
+
+struct Atomic
+{
+ PLUGINBASE
+ typedef void (*RenderCB)(Atomic *atomic);
+ enum { ID = 1 };
+ enum {
+ // flags
+ COLLISIONTEST = 0x01, // unused here
+ RENDER = 0x04,
+ // private flags
+ WORLDBOUNDDIRTY = 0x01,
+ // for setGeometry
+ SAMEBOUNDINGSPHERE = 0x01
+ };
+
+ ObjectWithFrame object;
+ Geometry *geometry;
+ Sphere boundingSphere;
+ Sphere worldBoundingSphere;
+ Clump *clump;
+ LLLink inClump;
+ ObjPipeline *pipeline;
+ RenderCB renderCB;
+
+ World *world;
+ ObjectWithFrame::Sync originalSync;
+
+ static int32 numAllocated;
+
+ static Atomic *create(void);
+ Atomic *clone(void);
+ void destroy(void);
+ void setFrame(Frame *f) {
+ this->object.setFrame(f);
+ this->object.object.privateFlags |= WORLDBOUNDDIRTY;
+ }
+ Frame *getFrame(void) const { return (Frame*)this->object.object.parent; }
+ static Atomic *fromClump(LLLink *lnk){
+ return LLLinkGetData(lnk, Atomic, inClump); }
+ void setGeometry(Geometry *geo, uint32 flags);
+ Sphere *getWorldBoundingSphere(void);
+ ObjPipeline *getPipeline(void);
+ void instance(void);
+ void uninstance(void);
+ void render(void) { this->renderCB(this); }
+ void setRenderCB(RenderCB renderCB){
+ this->renderCB = renderCB;
+ if(this->renderCB == nil)
+ this->renderCB = defaultRenderCB;
+ };
+ void setFlags(uint32 flags) { this->object.object.flags = flags; }
+ uint32 getFlags(void) const { return this->object.object.flags; }
+ static Atomic *streamReadClump(Stream *stream,
+ FrameList_ *frameList, Geometry **geometryList);
+ bool streamWriteClump(Stream *stream, FrameList_ *frmlst);
+ uint32 streamGetSize(void);
+
+ static void defaultRenderCB(Atomic *atomic);
+};
+
+void registerAtomicRightsPlugin(void);
+
+struct Light
+{
+ PLUGINBASE
+ enum { ID = 3 };
+ ObjectWithFrame object;
+ float32 radius;
+ RGBAf color;
+ float32 minusCosAngle;
+ LLLink inWorld;
+
+ // clump extension
+ Clump *clump;
+ LLLink inClump;
+
+ // world extension
+ World *world;
+ ObjectWithFrame::Sync originalSync;
+
+ static int32 numAllocated;
+
+ static Light *create(int32 type);
+ void destroy(void);
+ void setFrame(Frame *f) { this->object.setFrame(f); }
+ Frame *getFrame(void) const { return (Frame*)this->object.object.parent; }
+ static Light *fromClump(LLLink *lnk){
+ return LLLinkGetData(lnk, Light, inClump); }
+ static Light *fromWorld(LLLink *lnk){
+ return LLLinkGetData(lnk, Light, inWorld); }
+ void setAngle(float32 angle);
+ float32 getAngle(void);
+ void setColor(float32 r, float32 g, float32 b);
+ int32 getType(void){ return this->object.object.subType; }
+ void setFlags(uint32 flags) { this->object.object.flags = flags; }
+ uint32 getFlags(void) { return this->object.object.flags; }
+ static Light *streamRead(Stream *stream);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+
+ enum Type {
+ DIRECTIONAL = 1,
+ AMBIENT,
+ POINT = 0x80, // positioned
+ SPOT,
+ SOFTSPOT
+ };
+ enum Flags {
+ LIGHTATOMICS = 1,
+ LIGHTWORLD = 2
+ };
+};
+
+struct FrustumPlane
+{
+ Plane plane;
+ /* Used for BBox tests:
+ * 0 = inf is closer to normal direction
+ * 1 = sup is closer to normal direction */
+ uint8 closestX;
+ uint8 closestY;
+ uint8 closestZ;
+};
+
+struct Camera
+{
+ PLUGINBASE
+ enum { ID = 4 };
+ enum { PERSPECTIVE = 1, PARALLEL };
+ enum { CLEARIMAGE = 0x1, CLEARZ = 0x2, CLEARSTENCIL = 0x4 };
+ // return value of frustumTestSphere
+ enum { SPHEREOUTSIDE, SPHEREBOUNDARY, SPHEREINSIDE };
+
+ ObjectWithFrame object;
+ void (*beginUpdateCB)(Camera*);
+ void (*endUpdateCB)(Camera*);
+ V2d viewWindow;
+ V2d viewOffset;
+ float32 nearPlane, farPlane;
+ float32 fogPlane;
+ int32 projection;
+
+ Matrix viewMatrix;
+ float32 zScale, zShift;
+
+ FrustumPlane frustumPlanes[6];
+ V3d frustumCorners[8];
+ BBox frustumBoundBox;
+
+ Raster *frameBuffer;
+ Raster *zBuffer;
+
+ // Device dependent view and projection matrices
+ // optional
+ RawMatrix devView;
+ RawMatrix devProj;
+
+ // clump link handled by plugin in RW
+ Clump *clump;
+ LLLink inClump;
+
+ // world extension
+ /* RW: frustum sectors, space, position */
+ World *world;
+ ObjectWithFrame::Sync originalSync;
+ void (*originalBeginUpdate)(Camera*);
+ void (*originalEndUpdate)(Camera*);
+
+ static int32 numAllocated;
+
+ static Camera *create(void);
+ Camera *clone(void);
+ void destroy(void);
+ void setFrame(Frame *f) { this->object.setFrame(f); }
+ Frame *getFrame(void)const { return (Frame*)this->object.object.parent; }
+ static Camera *fromClump(LLLink *lnk){
+ return LLLinkGetData(lnk, Camera, inClump); }
+ void beginUpdate(void) { this->beginUpdateCB(this); }
+ void endUpdate(void) { this->endUpdateCB(this); }
+ void clear(RGBA *col, uint32 mode);
+ void showRaster(uint32 flags);
+ void setNearPlane(float32);
+ void setFarPlane(float32);
+ void setViewWindow(const V2d *window);
+ void setViewOffset(const V2d *offset);
+ void setProjection(int32 proj);
+ int32 frustumTestSphere(const Sphere *s) const;
+ static Camera *streamRead(Stream *stream);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+
+ // fov in degrees
+ void setFOV(float32 fov, float32 ratio);
+};
+
+struct Clump
+{
+ PLUGINBASE
+ enum { ID = 2 };
+ Object object;
+ LinkList atomics;
+ LinkList lights;
+ LinkList cameras;
+
+ World *world;
+ LLLink inWorld;
+
+ static int32 numAllocated;
+
+ static Clump *create(void);
+ Clump *clone(void);
+ void destroy(void);
+ static Clump *fromWorld(LLLink *lnk){
+ return LLLinkGetData(lnk, Clump, inWorld); }
+ int32 countAtomics(void) { return this->atomics.count(); }
+ void addAtomic(Atomic *a);
+ void removeAtomic(Atomic *a);
+ int32 countLights(void) { return this->lights.count(); }
+ void addLight(Light *l);
+ void removeLight(Light *l);
+ int32 countCameras(void) { return this->cameras.count(); }
+ void addCamera(Camera *c);
+ void removeCamera(Camera *c);
+ void setFrame(Frame *f){
+ this->object.parent = f; }
+ Frame *getFrame(void) const {
+ return (Frame*)this->object.parent; }
+ static Clump *streamRead(Stream *stream);
+ bool streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+ void render(void);
+};
+
+// used by enumerateLights for lighting callback
+struct WorldLights
+{
+ int32 numAmbients;
+ RGBAf ambient; // all ambients added
+ int32 numDirectionals;
+ Light **directionals; // only directionals
+ int32 numLocals;
+ Light **locals; // points, (soft)spots
+};
+
+// A bit of a stub right now
+struct World
+{
+ PLUGINBASE
+ enum { ID = 7 };
+ Object object;
+ LinkList localLights; // these have positions (type >= 0x80)
+ LinkList globalLights; // these do not (type < 0x80)
+ LinkList clumps;
+
+ static int32 numAllocated;
+
+ static World *create(BBox *bbox = nil); // TODO: should probably make this non-optional
+ void destroy(void);
+ void addLight(Light *light);
+ void removeLight(Light *light);
+ void addCamera(Camera *cam);
+ void removeCamera(Camera *cam);
+ void addAtomic(Atomic *atomic);
+ void removeAtomic(Atomic *atomic);
+ void addClump(Clump *clump);
+ void removeClump(Clump *clump);
+ void render(void);
+ void enumerateLights(Atomic *atomic, WorldLights *lightData);
+ void enumerateLights(WorldLights *lightData);
+};
+
+struct TexDictionary
+{
+ PLUGINBASE
+ enum { ID = 6 };
+ Object object;
+ LinkList textures;
+ LLLink inGlobalList;
+
+ static int32 numAllocated;
+
+ static TexDictionary *create(void);
+ static TexDictionary *fromLink(LLLink *lnk){
+ return LLLinkGetData(lnk, TexDictionary, inGlobalList); }
+ void destroy(void);
+ int32 count(void) { return this->textures.count(); }
+ void add(Texture *t);
+ void addFront(Texture *t);
+ void remove(Texture *t);
+ Texture *find(const char *name);
+ static TexDictionary *streamRead(Stream *stream);
+ void streamWrite(Stream *stream);
+ uint32 streamGetSize(void);
+
+ static void setCurrent(TexDictionary *txd);
+ static TexDictionary *getCurrent(void);
+};
+
+}
diff --git a/src/rwpipeline.h b/src/rwpipeline.h
new file mode 100644
index 0000000..9a0410a
--- /dev/null
+++ b/src/rwpipeline.h
@@ -0,0 +1,65 @@
+namespace rw {
+
+struct Atomic;
+
+class Pipeline
+{
+public:
+ uint32 pluginID;
+ uint32 pluginData;
+ int32 platform;
+
+ void init(uint32 platform) {
+ this->pluginID = 0;
+ this->pluginData = 0;
+ this->platform = platform;
+ }
+};
+
+class ObjPipeline : public Pipeline
+{
+public:
+ void init(uint32 platform);
+ static ObjPipeline *create(void); // always PLATFORM_NULL
+ void destroy(void);
+
+ // not the most beautiful way of doing things but still
+ // better than virtual methods (i hope?).
+ struct {
+ void (*instance)(ObjPipeline *pipe, Atomic *atomic);
+ void (*uninstance)(ObjPipeline *pipe, Atomic *atomic);
+ void (*render)(ObjPipeline *pipe, Atomic *atomic);
+ } impl;
+ // just for convenience
+ void instance(Atomic *atomic) { this->impl.instance(this, atomic); }
+ void uninstance(Atomic *atomic) { this->impl.uninstance(this, atomic); }
+ void render(Atomic *atomic) { this->impl.render(this, atomic); }
+};
+
+void findMinVertAndNumVertices(uint16 *indices, uint32 numIndices, uint32 *minVert, int32 *numVertices);
+
+// everything xbox, d3d8 and d3d9 may want to use
+enum {
+ VERT_BYTE2 = 1,
+ VERT_BYTE3,
+ VERT_SHORT2,
+ VERT_SHORT3,
+ VERT_NORMSHORT2,
+ VERT_NORMSHORT3,
+ VERT_FLOAT2,
+ VERT_FLOAT3,
+ VERT_FLOAT4,
+ VERT_ARGB,
+ VERT_RGBA,
+ VERT_COMPNORM
+};
+
+void instV4d(int type, uint8 *dst, V4d *src, uint32 numVertices, uint32 stride);
+void instV3d(int type, uint8 *dst, V3d *src, uint32 numVertices, uint32 stride);
+void uninstV3d(int type, V3d *dst, uint8 *src, uint32 numVertices, uint32 stride);
+void instTexCoords(int type, uint8 *dst, TexCoords *src, uint32 numVertices, uint32 stride);
+void uninstTexCoords(int type, TexCoords *dst, uint8 *src, uint32 numVertices, uint32 stride);
+bool32 instColor(int type, uint8 *dst, RGBA *src, uint32 numVertices, uint32 stride);
+void uninstColor(int type, RGBA *dst, uint8 *src, uint32 numVertices, uint32 stride);
+
+}
diff --git a/src/rwplg.h b/src/rwplg.h
new file mode 100644
index 0000000..ad2116a
--- /dev/null
+++ b/src/rwplg.h
@@ -0,0 +1,85 @@
+namespace rw {
+
+#define PLUGINOFFSET(type, base, offset) \
+ ((type*)((char*)(base) + (offset)))
+
+typedef void *(*Constructor)(void *object, int32 offset, int32 size);
+typedef void *(*Destructor)(void *object, int32 offset, int32 size);
+typedef void *(*CopyConstructor)(void *dst, void *src, int32 offset, int32 size);
+typedef Stream *(*StreamRead)(Stream *stream, int32 length, void *object, int32 offset, int32 size);
+typedef Stream *(*StreamWrite)(Stream *stream, int32 length, void *object, int32 offset, int32 size);
+typedef int32 (*StreamGetSize)(void *object, int32 offset, int32 size);
+typedef void (*RightsCallback)(void *object, int32 offset, int32 size, uint32 data);
+typedef void (*AlwaysCallback)(void *object, int32 offset, int32 size);
+
+struct PluginList
+{
+ int32 size;
+ int32 defaultSize;
+ LinkList plugins;
+
+ PluginList(void) {}
+ PluginList(int32 defSize)
+ : size(defSize), defaultSize(defSize)
+ { plugins.init(); }
+
+ static void open(void);
+ static void close(void);
+
+ void construct(void *);
+ void destruct(void *);
+ void copy(void *dst, void *src);
+ bool streamRead(Stream *stream, void *);
+ void streamWrite(Stream *stream, void *);
+ int streamGetSize(void *);
+ void streamSkip(Stream *stream);
+ void assertRights(void *, uint32 pluginID, uint32 data);
+
+ int32 registerPlugin(int32 size, uint32 id,
+ Constructor, Destructor, CopyConstructor);
+ int32 registerStream(uint32 id, StreamRead, StreamWrite, StreamGetSize);
+ int32 setStreamRightsCallback(uint32 id, RightsCallback cb);
+ int32 setStreamAlwaysCallback(uint32 id, AlwaysCallback cb);
+ int32 getPluginOffset(uint32 id);
+};
+
+struct Plugin
+{
+ int32 offset;
+ int32 size;
+ uint32 id;
+ Constructor constructor;
+ Destructor destructor;
+ CopyConstructor copy;
+ StreamRead read;
+ StreamWrite write;
+ StreamGetSize getSize;
+ RightsCallback rightsCallback;
+ AlwaysCallback alwaysCallback;
+ PluginList *parentList;
+ LLLink inParentList;
+ LLLink inGlobalList;
+};
+
+#define PLUGINBASE \
+ static PluginList s_plglist; \
+ static int32 registerPlugin(int32 size, uint32 id, Constructor ctor, \
+ Destructor dtor, CopyConstructor copy){ \
+ return s_plglist.registerPlugin(size, id, ctor, dtor, copy); \
+ } \
+ static int32 registerPluginStream(uint32 id, StreamRead read, \
+ StreamWrite write, StreamGetSize getSize){ \
+ return s_plglist.registerStream(id, read, write, getSize); \
+ } \
+ static int32 setStreamRightsCallback(uint32 id, RightsCallback cb){ \
+ return s_plglist.setStreamRightsCallback(id, cb); \
+ } \
+ static int32 setStreamAlwaysCallback(uint32 id, AlwaysCallback cb){ \
+ return s_plglist.setStreamAlwaysCallback(id, cb); \
+ } \
+ static int32 getPluginOffset(uint32 id){ \
+ return s_plglist.getPluginOffset(id); \
+ }
+
+
+}
diff --git a/src/rwplugins.h b/src/rwplugins.h
new file mode 100644
index 0000000..51696b6
--- /dev/null
+++ b/src/rwplugins.h
@@ -0,0 +1,252 @@
+namespace rw {
+
+/*
+ * HAnim
+ */
+
+struct HAnimKeyFrame
+{
+ HAnimKeyFrame *prev;
+ float32 time;
+ Quat q;
+ V3d t;
+};
+
+struct HAnimInterpFrame
+{
+ HAnimKeyFrame *keyFrame1;
+ HAnimKeyFrame *keyFrame2;
+ Quat q;
+ V3d t;
+};
+
+struct HAnimNodeInfo
+{
+ int32 id;
+ int32 index;
+ int32 flags;
+ Frame *frame;
+};
+
+struct HAnimHierarchy
+{
+ int32 flags;
+ int32 numNodes;
+ Matrix *matrices;
+ void *matricesUnaligned;
+ HAnimNodeInfo *nodeInfo;
+ Frame *parentFrame;
+ HAnimHierarchy *parentHierarchy; // mostly unused
+ AnimInterpolator *interpolator;
+
+ static HAnimHierarchy *create(int32 numNodes, int32 *nodeFlags,
+ int32 *nodeIDs, int32 flags, int32 maxKeySize);
+ void destroy(void);
+ void attachByIndex(int32 id);
+ void attach(void);
+ int32 getIndex(int32 id);
+ int32 getIndex(Frame *f);
+ void updateMatrices(void);
+
+ static HAnimHierarchy *get(Frame *f);
+ static HAnimHierarchy *get(Clump *c){
+ return find(c->getFrame()); }
+ static HAnimHierarchy *find(Frame *f);
+
+ enum Flags {
+ SUBHIERARCHY = 0x1,
+ NOMATRICES = 0x2,
+
+ UPDATEMODELLINGMATRICES = 0x1000,
+ UPDATELTMS = 0x2000,
+ LOCALSPACEMATRICES = 0x4000
+ };
+ enum NodeFlag {
+ POP = 1,
+ PUSH
+ };
+};
+
+struct HAnimData
+{
+ int32 id;
+ HAnimHierarchy *hierarchy;
+
+ static HAnimData *get(Frame *f);
+};
+
+extern int32 hAnimOffset;
+extern bool32 hAnimDoStream;
+void registerHAnimPlugin(void);
+
+
+/*
+ * MatFX
+ */
+
+struct MatFX
+{
+ enum {
+ NOTHING = 0,
+ BUMPMAP,
+ ENVMAP,
+ BUMPENVMAP, // BUMP | ENV
+ DUAL,
+ UVTRANSFORM,
+ DUALUVTRANSFORM
+ };
+ struct Bump {
+ Frame *frame;
+ Texture *bumpedTex;
+ Texture *tex;
+ float coefficient;
+ };
+ struct Env {
+ Frame *frame;
+ Texture *tex;
+ float coefficient;
+ int32 fbAlpha;
+ };
+ struct Dual {
+ Texture *tex;
+ int32 srcBlend;
+ int32 dstBlend;
+ };
+ struct UVtransform {
+ Matrix *baseTransform;
+ Matrix *dualTransform;
+ };
+ struct {
+ uint32 type;
+ union {
+ Bump bump;
+ Env env;
+ Dual dual;
+ UVtransform uvtransform;
+ };
+ } fx[2];
+ uint32 type;
+
+ static void setEffects(Material *m, uint32 flags);
+ static uint32 getEffects(const Material *m);
+ static MatFX *get(const Material *m);
+ int32 getEffectIndex(uint32 type);
+ // Bump
+ void setBumpTexture(Texture *t);
+ void setBumpCoefficient(float32 coef);
+ Texture *getBumpTexture(void);
+ float32 getBumpCoefficient(void);
+ // Env
+ void setEnvTexture(Texture *t);
+ void setEnvFrame(Frame *f);
+ void setEnvCoefficient(float32 coef);
+ void setEnvFBAlpha(bool32 useFBAlpha);
+ Texture *getEnvTexture(void);
+ Frame *getEnvFrame(void);
+ float32 getEnvCoefficient(void);
+ bool32 getEnvFBAlpha(void);
+ // Dual
+ void setDualTexture(Texture *t);
+ void setDualSrcBlend(int32 blend);
+ void setDualDestBlend(int32 blend);
+ Texture *getDualTexture(void);
+ int32 getDualSrcBlend(void);
+ int32 getDualDestBlend(void);
+ // UV transform
+ void setUVTransformMatrices(Matrix *base, Matrix *dual);
+ void getUVTransformMatrices(Matrix **base, Matrix **dual);
+
+ static void enableEffects(Atomic *atomic);
+ static void disableEffects(Atomic *atomic);
+ static bool32 getEffects(Atomic *atomic);
+
+ static bool32 envMapFlipU; // PS2 does this for some reason
+ static bool32 envMapApplyLight; // modulate env map by lighting
+ static bool32 envMapUseMatColor; // modulate env map by material color
+ static RGBA envMapColor; // if !envMapUseMatColor, use this
+};
+
+struct MatFXGlobals
+{
+ int32 atomicOffset;
+ int32 materialOffset;
+ ObjPipeline *pipelines[NUM_PLATFORMS];
+ ObjPipeline *dummypipe;
+};
+extern MatFXGlobals matFXGlobals;
+void registerMatFXPlugin(void);
+
+
+/*
+ * Skin
+ */
+
+struct SkinGlobals
+{
+ int32 geoOffset;
+ int32 atomicOffset;
+ ObjPipeline *pipelines[NUM_PLATFORMS];
+ ObjPipeline *dummypipe;
+};
+extern SkinGlobals skinGlobals;
+
+struct Skin
+{
+ int32 numBones;
+ int32 numUsedBones;
+ int32 numWeights;
+ uint8 *usedBones;
+ float *inverseMatrices;
+ uint8 *indices;
+ float *weights;
+
+ // split skin
+
+ // points into rle for each mesh
+ struct RLEcount {
+ uint8 start;
+ uint8 size;
+ };
+ // run length encoded used bones
+ struct RLE {
+ uint8 startbone; // into remapIndices
+ uint8 n;
+ };
+ int32 boneLimit;
+ int32 numMeshes;
+ int32 rleSize;
+ int8 *remapIndices;
+ RLEcount *rleCount;
+ RLE *rle;
+
+ uint8 *data; // only used by delete
+ void *platformData; // a place to store platform specific stuff
+ bool32 legacyType; // old skin attached to atomic, needed for always CB
+
+ void init(int32 numBones, int32 numUsedBones, int32 numVertices);
+ void findNumWeights(int32 numVertices);
+ void findUsedBones(int32 numVertices);
+
+ static void setPipeline(Atomic *a, int32 type);
+ static Skin *get(const Geometry *geo){
+ return *PLUGINOFFSET(Skin*, geo, skinGlobals.geoOffset);
+ }
+ static void set(Geometry *geo, Skin *skin){
+ *PLUGINOFFSET(Skin*, geo, skinGlobals.geoOffset) = skin;
+ }
+ static void setHierarchy(Atomic *atomic, HAnimHierarchy *hier){
+ *PLUGINOFFSET(HAnimHierarchy*, atomic,
+ skinGlobals.atomicOffset) = hier;
+ }
+ static HAnimHierarchy *getHierarchy(const Atomic *atomic){
+ return *PLUGINOFFSET(HAnimHierarchy*, atomic,
+ skinGlobals.atomicOffset);
+ }
+};
+
+Stream *readSkinSplitData(Stream *stream, Skin *skin);
+Stream *writeSkinSplitData(Stream *stream, Skin *skin);
+int32 skinSplitDataSize(Skin *skin);
+void registerSkinPlugin(void);
+
+}
diff --git a/src/rwrender.h b/src/rwrender.h
new file mode 100644
index 0000000..49fe0c4
--- /dev/null
+++ b/src/rwrender.h
@@ -0,0 +1,140 @@
+namespace rw {
+
+// Render states
+
+enum RenderState
+{
+ TEXTURERASTER,
+ TEXTUREADDRESS,
+ TEXTUREADDRESSU,
+ TEXTUREADDRESSV,
+ TEXTUREFILTER,
+ VERTEXALPHA,
+ SRCBLEND,
+ DESTBLEND,
+ ZTESTENABLE,
+ ZWRITEENABLE,
+ FOGENABLE,
+ FOGCOLOR,
+ CULLMODE,
+ // TODO:
+ // fog type, density ?
+ // ? shademode
+
+ STENCILENABLE,
+ STENCILFAIL,
+ STENCILZFAIL,
+ STENCILPASS,
+ STENCILFUNCTION,
+ STENCILFUNCTIONREF,
+ STENCILFUNCTIONMASK,
+ STENCILFUNCTIONWRITEMASK,
+
+ // platform specific or opaque?
+ ALPHATESTFUNC,
+ ALPHATESTREF,
+
+ // emulation of PS2 GS alpha test
+ // in the mode where it still writes color but nor depth
+ GSALPHATEST,
+ GSALPHATESTREF
+};
+
+enum AlphaTestFunc
+{
+ ALPHAALWAYS,
+ ALPHAGREATEREQUAL,
+ ALPHALESS
+};
+
+enum StencilOp
+{
+ STENCILKEEP = 1,
+ STENCILZERO,
+ STENCILREPLACE,
+ STENCILINCSAT,
+ STENCILDECSAT,
+ STENCILINVERT,
+ STENCILINC,
+ STENCILDEC
+};
+
+enum StencilFunc
+{
+ STENCILNEVER = 1,
+ STENCILLESS,
+ STENCILEQUAL,
+ STENCILLESSEQUAL,
+ STENCILGREATER,
+ STENCILNOTEQUAL,
+ STENCILGREATEREQUAL,
+ STENCILALWAYS
+};
+
+enum CullMode
+{
+ CULLNONE = 1,
+ CULLBACK,
+ CULLFRONT
+};
+
+enum BlendFunction
+{
+ BLENDZERO = 1,
+ BLENDONE,
+ BLENDSRCCOLOR,
+ BLENDINVSRCCOLOR,
+ BLENDSRCALPHA,
+ BLENDINVSRCALPHA,
+ BLENDDESTALPHA,
+ BLENDINVDESTALPHA,
+ BLENDDESTCOLOR,
+ BLENDINVDESTCOLOR,
+ BLENDSRCALPHASAT
+ // TODO: add more perhaps
+};
+
+void SetRenderState(int32 state, uint32 value);
+void SetRenderStatePtr(int32 state, void *value);
+uint32 GetRenderState(int32 state);
+void *GetRenderStatePtr(int32 state);
+
+// Im2D
+
+namespace im2d {
+
+float32 GetNearZ(void);
+float32 GetFarZ(void);
+void RenderLine(void *verts, int32 numVerts, int32 vert1, int32 vert2);
+void RenderTriangle(void *verts, int32 numVerts, int32 vert1, int32 vert2, int32 vert3);
+void RenderIndexedPrimitive(PrimitiveType, void *verts, int32 numVerts, void *indices, int32 numIndices);
+void RenderPrimitive(PrimitiveType type, void *verts, int32 numVerts);
+
+}
+
+// Im3D
+
+namespace im3d {
+
+enum TransformFlags
+{
+ VERTEXUV = 1, // has tex Coords
+ ALLOPAQUE = 2, // no vertex alpha
+ NOCLIP = 4, // don't frustum clip
+ VERTEXXYZ = 8, // has position
+ VERTEXRGBA = 16, // has color
+ LIGHTING = 32, // do lighting, assumes normals (librw extension)
+ EVERYTHING = VERTEXUV|VERTEXXYZ|VERTEXRGBA
+};
+
+void Transform(void *vertices, int32 numVertices, Matrix *world, uint32 flags);
+void RenderLine(int32 vert1, int32 vert2);
+void RenderTriangle(int32 vert1, int32 vert2, int32 vert3);
+void RenderPrimitive(PrimitiveType primType);
+void RenderIndexedPrimitive(PrimitiveType primType, void *indices, int32 numIndices);
+void End(void);
+
+}
+
+}
+
diff --git a/src/rwuserdata.h b/src/rwuserdata.h
new file mode 100644
index 0000000..00d7355
--- /dev/null
+++ b/src/rwuserdata.h
@@ -0,0 +1,94 @@
+namespace rw {
+
+enum UserDataType
+{
+ USERDATANA = 0,
+ USERDATAINT = 1,
+ USERDATAFLOAT = 2,
+ USERDATASTRING = 3
+};
+
+struct UserDataArray
+{
+ char *name;
+ uint32 datatype;
+ int32 numElements;
+ void *data;
+
+ int32 getInt(int32 n) { return ((int32*)this->data)[n]; }
+ float32 getFloat(int32 n) { return ((float32*)this->data)[n]; }
+ char *getString(int32 n) { return ((char**)this->data)[n]; }
+ void setInt(int32 n, int32 i) { ((int32*)this->data)[n] = i; }
+ void setFloat(int32 n, float32 f) { ((float32*)this->data)[n] = f; }
+ void setString(int32 n, const char *s);
+
+ static int32 geometryAdd(Geometry *g, const char *name, int32 datatype, int32 numElements);
+ static void geometryRemove(Geometry *g, int32 n);
+ static int32 geometryGetCount(Geometry *g);
+ static UserDataArray *geometryGet(Geometry *g, int32 n);
+ static int32 geometryFindIndex(Geometry *g, const char *name);
+
+ static int32 frameAdd(Frame *f, const char *name, int32 datatype, int32 numElements);
+ static void frameRemove(Frame *f, int32 n);
+ static int32 frameGetCount(Frame *f);
+ static UserDataArray *frameGet(Frame *f, int32 n);
+ static int32 frameFindIndex(Frame *f, const char *name);
+
+ static int32 cameraAdd(Camera *c, const char *name, int32 datatype, int32 numElements);
+ static void cameraRemove(Camera *c, int32 n);
+ static int32 cameraGetCount(Camera *c);
+ static UserDataArray *cameraGet(Camera *c, int32 n);
+ static int32 cameraFindIndex(Camera *c, const char *name);
+
+ static int32 lightAdd(Light *l, const char *name, int32 datatype, int32 numElements);
+ static void lightRemove(Light *l, int32 n);
+ static int32 lightGetCount(Light *l);
+ static UserDataArray *lightGet(Light *l, int32 n);
+ static int32 lightFindIndex(Light *l, const char *name);
+
+ static int32 materialAdd(Material *m, const char *name, int32 datatype, int32 numElements);
+ static void materialRemove(Material *m, int32 n);
+ static int32 materialGetCount(Material *m);
+ static UserDataArray *materialGet(Material *m, int32 n);
+ static int32 materialFindIndex(Material *m, const char *name);
+
+ static int32 textureAdd(Texture *t, const char *name, int32 datatype, int32 numElements);
+ static void textureRemove(Texture *t, int32 n);
+ static int32 textureGetCount(Texture *t);
+ static UserDataArray *textureGet(Texture *t, int32 n);
+ static int32 textureFindIndex(Texture *t, const char *name);
+};
+
+struct UserDataExtension
+{
+ int32 numArrays;
+ UserDataArray *arrays;
+
+ int32 add(const char *name, int32 datatype, int32 numElements);
+ void remove(int32 n);
+ int32 getCount(void) { return numArrays; }
+ UserDataArray *get(int32 n) { return n >= numArrays ? nil : &arrays[n]; }
+ int32 findIndex(const char *name);
+
+ static UserDataExtension *get(Geometry *geo);
+ static UserDataExtension *get(Frame *frame);
+ static UserDataExtension *get(Camera *cam);
+ static UserDataExtension *get(Light *light);
+ static UserDataExtension *get(Material *mat);
+ static UserDataExtension *get(Texture *tex);
+};
+
+struct UserDataGlobals
+{
+ int32 geometryOffset;
+ int32 frameOffset;
+ int32 cameraOffset;
+ int32 lightOffset;
+ int32 materialOffset;
+ int32 textureOffset;
+};
+extern UserDataGlobals userDataGlobals;
+
+void registerUserDataPlugin(void);
+
+}
diff --git a/src/skin.cpp b/src/skin.cpp
new file mode 100644
index 0000000..abaa548
--- /dev/null
+++ b/src/skin.cpp
@@ -0,0 +1,486 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwanim.h"
+#include "rwengine.h"
+#include "rwplugins.h"
+#include "ps2/rwps2.h"
+#include "ps2/rwps2plg.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+#include "gl/rwwdgl.h"
+#include "gl/rwgl3.h"
+#include "gl/rwgl3plg.h"
+
+#define PLUGIN_ID ID_SKIN
+
+namespace rw {
+
+SkinGlobals skinGlobals = { 0, 0, { nil }, nil };
+
+static void*
+createSkin(void *object, int32 offset, int32)
+{
+ *PLUGINOFFSET(Skin*, object, offset) = nil;
+ return object;
+}
+
+static void*
+destroySkin(void *object, int32 offset, int32)
+{
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ if(skin){
+ rwFree(skin->data);
+ rwFree(skin->remapIndices);
+// delete[] skin->platformData;
+ }
+ rwFree(skin);
+ return object;
+}
+
+static void*
+copySkin(void *dst, void *src, int32 offset, int32)
+{
+ Skin *srcskin = *PLUGINOFFSET(Skin*, src, offset);
+ if(srcskin == nil)
+ return dst;
+ Geometry *geometry = (Geometry*)src;
+ assert(geometry->instData == nil);
+ assert(((Geometry*)src)->numVertices == ((Geometry*)dst)->numVertices);
+ Skin *dstskin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
+ *PLUGINOFFSET(Skin*, dst, offset) = dstskin;
+ dstskin->numBones = srcskin->numBones;
+ dstskin->numUsedBones = srcskin->numUsedBones;
+ dstskin->numWeights = srcskin->numWeights;
+
+ assert(0 && "can't copy skin yet");
+ dstskin->init(srcskin->numBones, srcskin->numUsedBones,
+ geometry->numVertices);
+ memcpy(dstskin->usedBones, srcskin->usedBones, srcskin->numUsedBones);
+ memcpy(dstskin->inverseMatrices, srcskin->inverseMatrices,
+ srcskin->numBones*64);
+ memcpy(dstskin->indices, srcskin->indices, geometry->numVertices*4);
+ memcpy(dstskin->weights, srcskin->weights, geometry->numVertices*16);
+ return dst;
+}
+
+Stream*
+readSkinSplitData(Stream *stream, Skin *skin)
+{
+ uint32 sz;
+ int8 *data;
+
+ skin->boneLimit = stream->readI32();
+ skin->numMeshes = stream->readI32();
+ skin->rleSize = stream->readI32();
+ if(skin->numMeshes){
+ sz = skin->numBones + 2*(skin->numMeshes+skin->rleSize);
+ data = (int8*)rwMalloc(sz, MEMDUR_EVENT | ID_SKIN);
+ stream->read8(data, sz);
+ skin->remapIndices = data;
+ skin->rleCount = (Skin::RLEcount*)(data + skin->numBones);
+ skin->rle = (Skin::RLE*)(data + skin->numBones + 2*skin->numMeshes);
+ }
+ return stream;
+}
+
+Stream*
+writeSkinSplitData(Stream *stream, Skin *skin)
+{
+ stream->writeI32(skin->boneLimit);
+ stream->writeI32(skin->numMeshes);
+ stream->writeI32(skin->rleSize);
+ if(skin->numMeshes)
+ stream->write8(skin->remapIndices,
+ skin->numBones + 2*(skin->numMeshes+skin->rleSize));
+ return stream;
+}
+
+int32
+skinSplitDataSize(Skin *skin)
+{
+ if(skin->numMeshes == 0)
+ return 12;
+ return 12 + skin->numBones + 2*(skin->numMeshes+skin->rleSize);
+}
+
+static Stream*
+readSkin(Stream *stream, int32 len, void *object, int32 offset, int32)
+{
+ uint8 header[4];
+ Geometry *geometry = (Geometry*)object;
+
+ if(geometry->instData){
+ // TODO: function pointers
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::readNativeSkin(stream, len, object, offset);
+ else if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::readNativeSkin(stream, len, object, offset);
+ else if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::readNativeSkin(stream, len, object, offset);
+ else{
+ assert(0 && "unsupported native skin platform");
+ return nil;
+ }
+ }
+
+ stream->read8(header, 4); // numBones, numUsedBones,
+ // numWeights, unused
+ Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
+ *PLUGINOFFSET(Skin*, geometry, offset) = skin;
+
+ // numUsedBones and numWeights appear in/after 34003
+ // but not in/before 33002 (probably rw::version >= 0x34000)
+ bool oldFormat = header[1] == 0;
+
+ // Use numBones for numUsedBones to allocate data,
+ // find out the correct value later
+ if(oldFormat)
+ skin->init(header[0], header[0], geometry->numVertices);
+ else
+ skin->init(header[0], header[1], geometry->numVertices);
+ skin->numWeights = header[2];
+
+ if(!oldFormat)
+ stream->read8(skin->usedBones, skin->numUsedBones);
+ if(skin->indices)
+ stream->read8(skin->indices, geometry->numVertices*4);
+ if(skin->weights)
+ stream->read32(skin->weights, geometry->numVertices*16);
+ for(int32 i = 0; i < skin->numBones; i++){
+ if(oldFormat)
+ stream->seek(4); // skip 0xdeaddead
+ stream->read32(&skin->inverseMatrices[i*16], 64);
+ }
+
+ if(oldFormat){
+ skin->findNumWeights(geometry->numVertices);
+ skin->findUsedBones(geometry->numVertices);
+ }
+
+ if(!oldFormat)
+ readSkinSplitData(stream, skin);
+
+ return stream;
+}
+
+static Stream*
+writeSkin(Stream *stream, int32 len, void *object, int32 offset, int32)
+{
+ uint8 header[4];
+ Geometry *geometry = (Geometry*)object;
+
+ if(geometry->instData){
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::writeNativeSkin(stream, len, object, offset);
+ else if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::writeNativeSkin(stream, len, object, offset);
+ else if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::writeNativeSkin(stream, len, object, offset);
+ else{
+ assert(0 && "unsupported native skin platform");
+ return nil;
+ }
+ }
+
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ // not sure which version introduced the new format
+ bool oldFormat = version < 0x34000;
+ header[0] = skin->numBones;
+ if(oldFormat){
+ header[1] = 0;
+ header[2] = 0;
+ }else{
+ header[1] = skin->numUsedBones;
+ header[2] = skin->numWeights;
+ }
+ header[3] = 0;
+ stream->write8(header, 4);
+ if(!oldFormat)
+ stream->write8(skin->usedBones, skin->numUsedBones);
+ stream->write8(skin->indices, geometry->numVertices*4);
+ stream->write32(skin->weights, geometry->numVertices*16);
+ for(int32 i = 0; i < skin->numBones; i++){
+ if(oldFormat)
+ stream->writeU32(0xdeaddead);
+ stream->write32(&skin->inverseMatrices[i*16], 64);
+ }
+
+ if(!oldFormat)
+ writeSkinSplitData(stream, skin);
+ return stream;
+}
+
+static int32
+getSizeSkin(void *object, int32 offset, int32)
+{
+ Geometry *geometry = (Geometry*)object;
+
+ if(geometry->instData){
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::getSizeNativeSkin(object, offset);
+ if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::getSizeNativeSkin(object, offset);
+ if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::getSizeNativeSkin(object, offset);
+ if(geometry->instData->platform == PLATFORM_D3D8)
+ return -1;
+ if(geometry->instData->platform == PLATFORM_D3D9)
+ return -1;
+ assert(0 && "unsupported native skin platform");
+ }
+
+ Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
+ if(skin == nil)
+ return -1;
+
+ int32 size = 4 + geometry->numVertices*(16+4) +
+ skin->numBones*64;
+ // not sure which version introduced the new format
+ if(version < 0x34000)
+ size += skin->numBones*4;
+ else
+ size += skin->numUsedBones + skinSplitDataSize(skin);
+ return size;
+}
+
+static Stream*
+readSkinLegacy(Stream *stream, int32 len, void *object, int32, int32)
+{
+ Atomic *atomic = (Atomic*)object;
+ Geometry *geometry = atomic->geometry;
+
+ int32 numBones = stream->readI32();
+ int32 numVertices = stream->readI32();
+ assert(numVertices == geometry->numVertices);
+
+ Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
+ *PLUGINOFFSET(Skin*, geometry, skinGlobals.geoOffset) = skin;
+ skin->init(numBones, numBones, numVertices);
+ skin->legacyType = 1;
+ skin->numWeights = 4;
+
+ stream->read8(skin->indices, numVertices*4);
+ stream->read32(skin->weights, numVertices*16);
+
+ HAnimHierarchy *hier = HAnimHierarchy::create(numBones, nil, nil, 0, 36);
+
+ for(int i = 0; i < numBones; i++){
+ hier->nodeInfo[i].id = stream->readI32();
+ hier->nodeInfo[i].index = stream->readI32();
+ hier->nodeInfo[i].flags = stream->readI32() & 3;
+// printf("%d %d %d %d\n", i, hier->nodeInfo[i].id, hier->nodeInfo[i].index, hier->nodeInfo[i].flags);
+ stream->read32(&skin->inverseMatrices[i*16], 64);
+
+ Matrix mat;
+ Matrix::invert(&mat, (Matrix*)&skin->inverseMatrices[i*16]);
+// printf("[ [ %8.4f, %8.4f, %8.4f, %8.4f ]\n"
+// " [ %8.4f, %8.4f, %8.4f, %8.4f ]\n"
+// " [ %8.4f, %8.4f, %8.4f, %8.4f ]\n"
+// " [ %8.4f, %8.4f, %8.4f, %8.4f ] ]\n"
+// " %08x == flags\n",
+// mat.right.x, mat.up.x, mat.at.x, mat.pos.x,
+// mat.right.y, mat.up.y, mat.at.y, mat.pos.y,
+// mat.right.z, mat.up.z, mat.at.z, mat.pos.z,
+// 0.0f, 0.0f, 0.0f, 1.0f,
+// mat.flags);
+ }
+ Frame *frame = atomic->getFrame()->child;
+ assert(frame->next == nil); // in old files atomic is above hierarchy it seems
+ assert(frame->count() == numBones); // assuming one frame per node this should also be true
+ HAnimData::get(frame)->hierarchy = hier;
+ hier->parentFrame = frame;
+
+ return stream;
+}
+
+static void
+skinRights(void *object, int32, int32, uint32)
+{
+ Skin::setPipeline((Atomic*)object, 1);
+}
+
+static void
+skinAlways(void *object, int32, int32)
+{
+ Atomic *atomic = (Atomic*)object;
+ Geometry *geo = atomic->geometry;
+ if(geo == nil) return;
+ Skin *skin = Skin::get(geo);
+ if(skin == nil) return;
+ Skin::setPipeline((Atomic*)object, 1);
+}
+
+static void*
+createSkinAtm(void *object, int32 offset, int32)
+{
+ *PLUGINOFFSET(void*, object, offset) = nil;
+ return object;
+}
+
+static void*
+destroySkinAtm(void *object, int32 offset, int32)
+{
+ return object;
+}
+
+static void*
+copySkinAtm(void *dst, void *src, int32 offset, int32)
+{
+ *PLUGINOFFSET(void*, dst, offset) = *PLUGINOFFSET(void*, src, offset);
+ return dst;
+}
+
+static void*
+skinOpen(void *o, int32, int32)
+{
+ // init dummy pipelines
+ skinGlobals.dummypipe = ObjPipeline::create();
+ skinGlobals.dummypipe->pluginID = ID_SKIN;
+ skinGlobals.dummypipe->pluginData = 1;
+ for(uint i = 0; i < nelem(skinGlobals.pipelines); i++)
+ skinGlobals.pipelines[i] = skinGlobals.dummypipe;
+ return o;
+}
+
+static void*
+skinClose(void *o, int32, int32)
+{
+ for(uint i = 0; i < nelem(skinGlobals.pipelines); i++)
+ if(skinGlobals.pipelines[i] == skinGlobals.dummypipe)
+ matFXGlobals.pipelines[i] = nil;
+ skinGlobals.dummypipe->destroy();
+ skinGlobals.dummypipe = nil;
+ return o;
+}
+
+void
+registerSkinPlugin(void)
+{
+ Driver::registerPlugin(PLATFORM_NULL, 0, ID_SKIN,
+ skinOpen, skinClose);
+ ps2::initSkin();
+ xbox::initSkin();
+ d3d8::initSkin();
+ d3d9::initSkin();
+ wdgl::initSkin();
+ gl3::initSkin();
+
+ int32 o;
+ o = Geometry::registerPlugin(sizeof(Skin*), ID_SKIN,
+ createSkin, destroySkin, copySkin);
+ Geometry::registerPluginStream(ID_SKIN,
+ readSkin, writeSkin, getSizeSkin);
+ skinGlobals.geoOffset = o;
+ o = Atomic::registerPlugin(sizeof(HAnimHierarchy*),ID_SKIN,
+ createSkinAtm, destroySkinAtm, copySkinAtm);
+ skinGlobals.atomicOffset = o;
+ Atomic::registerPluginStream(ID_SKIN, readSkinLegacy, nil, nil);
+ Atomic::setStreamRightsCallback(ID_SKIN, skinRights);
+ Atomic::setStreamAlwaysCallback(ID_SKIN, skinAlways);
+}
+
+void
+Skin::init(int32 numBones, int32 numUsedBones, int32 numVertices)
+{
+ this->numBones = numBones;
+ this->numUsedBones = numUsedBones;
+ uint32 size = this->numUsedBones +
+ this->numBones*64 +
+ numVertices*(16+4) + 0xF;
+ this->data = rwNewT(uint8, size, MEMDUR_EVENT | ID_SKIN);
+ uint8 *p = this->data;
+
+ this->usedBones = nil;
+ if(this->numUsedBones){
+ this->usedBones = p;
+ p += this->numUsedBones;
+ }
+
+ p = (uint8*)(((uintptr)p + 0xF) & ~0xF);
+ this->inverseMatrices = nil;
+ if(this->numBones){
+ this->inverseMatrices = (float*)p;
+ p += 64*this->numBones;
+ }
+
+ this->indices = nil;
+ if(numVertices){
+ this->indices = p;
+ p += 4*numVertices;
+ }
+
+ this->weights = nil;
+ if(numVertices)
+ this->weights = (float*)p;
+
+ this->boneLimit = 0;
+ this->numMeshes = 0;
+ this->rleSize = 0;
+ this->remapIndices = nil;
+ this->rleCount = nil;
+ this->rle = nil;
+
+ this->platformData = nil;
+ this->legacyType = 0;
+}
+
+
+
+//static_assert(sizeof(Skin::RLEcount) == 2, "RLEcount size");
+//static_assert(sizeof(Skin::RLE) == 2, "RLE size");
+
+void
+Skin::findNumWeights(int32 numVertices)
+{
+ this->numWeights = 1;
+ float *w = this->weights;
+ while(numVertices--){
+ while(w[this->numWeights] != 0.0f){
+ this->numWeights++;
+ if(this->numWeights == 4)
+ return;
+ }
+ w += 4;
+ }
+}
+
+void
+Skin::findUsedBones(int32 numVertices)
+{
+ uint8 usedTab[256];
+ uint8 *indices = this->indices;
+ float *weights = this->weights;
+ memset(usedTab, 0, 256);
+ while(numVertices--){
+ for(int32 i = 0; i < this->numWeights; i++){
+ if(weights[i] == 0.0f)
+ continue; // TODO: this could probably be optimized
+ if(usedTab[indices[i]] == 0)
+ usedTab[indices[i]]++;
+ }
+ indices += 4;
+ weights += 4;
+ }
+ this->numUsedBones = 0;
+ for(int32 i = 0; i < 256; i++)
+ if(usedTab[i])
+ this->usedBones[this->numUsedBones++] = i;
+}
+
+void
+Skin::setPipeline(Atomic *a, int32 type)
+{
+ (void)type;
+ a->pipeline = skinGlobals.pipelines[rw::platform];
+}
+
+}
diff --git a/src/texture.cpp b/src/texture.cpp
new file mode 100644
index 0000000..961649e
--- /dev/null
+++ b/src/texture.cpp
@@ -0,0 +1,596 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define WITH_D3D
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "ps2/rwps2.h"
+#include "d3d/rwd3d.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+#include "d3d/rwd3dimpl.h"
+#include "gl/rwgl3.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+
+int32 Texture::numAllocated;
+int32 TexDictionary::numAllocated;
+
+PluginList TexDictionary::s_plglist(sizeof(TexDictionary));
+PluginList Texture::s_plglist(sizeof(Texture));
+PluginList Raster::s_plglist(sizeof(Raster));
+
+struct TextureGlobals
+{
+ TexDictionary *initialTexDict;
+ TexDictionary *currentTexDict;
+ // load textures from files
+ bool32 loadTextures;
+ // create dummy textures to store just names
+ bool32 makeDummies;
+ bool32 mipmapping;
+ bool32 autoMipmapping;
+ LinkList texDicts;
+
+ LinkList textures;
+};
+int32 textureModuleOffset;
+
+#define TEXTUREGLOBAL(v) (PLUGINOFFSET(TextureGlobals, engine, textureModuleOffset)->v)
+
+static void*
+textureOpen(void *object, int32 offset, int32 size)
+{
+ TexDictionary *texdict;
+ textureModuleOffset = offset;
+ TEXTUREGLOBAL(texDicts).init();
+ TEXTUREGLOBAL(textures).init();
+ texdict = TexDictionary::create();
+ TEXTUREGLOBAL(initialTexDict) = texdict;
+ TexDictionary::setCurrent(texdict);
+ TEXTUREGLOBAL(loadTextures) = 1;
+ TEXTUREGLOBAL(makeDummies) = 0;
+ TEXTUREGLOBAL(mipmapping) = 0;
+ TEXTUREGLOBAL(autoMipmapping) = 0;
+ return object;
+}
+static void*
+textureClose(void *object, int32 offset, int32 size)
+{
+ FORLIST(lnk, TEXTUREGLOBAL(texDicts))
+ TexDictionary::fromLink(lnk)->destroy();
+ TEXTUREGLOBAL(initialTexDict) = nil;
+ TEXTUREGLOBAL(currentTexDict) = nil;
+
+ FORLIST(lnk, TEXTUREGLOBAL(textures)){
+ Texture *tex = LLLinkGetData(lnk, Texture, inGlobalList);
+// printf("Tex still allocated: %d %s %s\n", tex->refCount, tex->name, tex->mask);
+ assert(tex->dict == nil);
+ tex->destroy();
+ }
+ return object;
+}
+
+void
+Texture::registerModule(void)
+{
+ Engine::registerPlugin(sizeof(TextureGlobals), ID_TEXTUREMODULE, textureOpen, textureClose);
+}
+
+void
+Texture::setLoadTextures(bool32 b)
+{
+ TEXTUREGLOBAL(loadTextures) = b;
+}
+
+void
+Texture::setCreateDummies(bool32 b)
+{
+ TEXTUREGLOBAL(makeDummies) = b;
+}
+
+void Texture::setMipmapping(bool32 b) { TEXTUREGLOBAL(mipmapping) = b; }
+void Texture::setAutoMipmapping(bool32 b) { TEXTUREGLOBAL(autoMipmapping) = b; }
+bool32 Texture::getMipmapping(void) { return TEXTUREGLOBAL(mipmapping); }
+bool32 Texture::getAutoMipmapping(void) { return TEXTUREGLOBAL(autoMipmapping); }
+
+//
+// TexDictionary
+//
+
+TexDictionary*
+TexDictionary::create(void)
+{
+ TexDictionary *dict = (TexDictionary*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_TEXDICTIONARY);
+ if(dict == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ dict->object.init(TexDictionary::ID, 0);
+ dict->textures.init();
+ TEXTUREGLOBAL(texDicts).add(&dict->inGlobalList);
+ s_plglist.construct(dict);
+ return dict;
+}
+
+void
+TexDictionary::destroy(void)
+{
+ if(TEXTUREGLOBAL(currentTexDict) == this)
+ TEXTUREGLOBAL(currentTexDict) = nil;
+ FORLIST(lnk, this->textures){
+ Texture *tex = Texture::fromDict(lnk);
+ this->remove(tex);
+ tex->destroy();
+ }
+ s_plglist.destruct(this);
+ this->inGlobalList.remove();
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+TexDictionary::add(Texture *t)
+{
+ if(t->dict)
+ t->inDict.remove();
+ t->dict = this;
+ this->textures.append(&t->inDict);
+}
+
+void
+TexDictionary::remove(Texture *t)
+{
+ assert(t->dict == this);
+ t->inDict.remove();
+ t->dict = nil;
+}
+
+void
+TexDictionary::addFront(Texture *t)
+{
+ if(t->dict)
+ t->inDict.remove();
+ t->dict = this;
+ this->textures.add(&t->inDict);
+}
+
+Texture*
+TexDictionary::find(const char *name)
+{
+ FORLIST(lnk, this->textures){
+ Texture *tex = Texture::fromDict(lnk);
+ if(strncmp_ci(tex->name, name, 32) == 0)
+ return tex;
+ }
+ return nil;
+}
+
+TexDictionary*
+TexDictionary::streamRead(Stream *stream)
+{
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ int32 numTex = stream->readI16();
+ stream->readI16(); // device id (0 = unknown, 1 = d3d8, 2 = d3d9,
+ // 3 = gcn, 4 = null, 5 = opengl,
+ // 6 = ps2, 7 = softras, 8 = xbox, 9 = psp)
+ TexDictionary *txd = TexDictionary::create();
+ if(txd == nil)
+ return nil;
+ Texture *tex;
+ for(int32 i = 0; i < numTex; i++){
+ if(!findChunk(stream, ID_TEXTURENATIVE, nil, nil)){
+ RWERROR((ERR_CHUNK, "TEXTURENATIVE"));
+ goto fail;
+ }
+ tex = Texture::streamReadNative(stream);
+ if(tex == nil)
+ goto fail;
+ Texture::s_plglist.streamRead(stream, tex);
+ txd->add(tex);
+ }
+ if(s_plglist.streamRead(stream, txd))
+ return txd;
+fail:
+ txd->destroy();
+ return nil;
+}
+
+void
+TexDictionary::streamWrite(Stream *stream)
+{
+ writeChunkHeader(stream, ID_TEXDICTIONARY, this->streamGetSize());
+ writeChunkHeader(stream, ID_STRUCT, 4);
+ int32 numTex = this->count();
+ stream->writeI16(numTex);
+ stream->writeI16(0);
+ FORLIST(lnk, this->textures){
+ Texture *tex = Texture::fromDict(lnk);
+ uint32 sz = tex->streamGetSizeNative();
+ sz += 12 + Texture::s_plglist.streamGetSize(tex);
+ writeChunkHeader(stream, ID_TEXTURENATIVE, sz);
+ tex->streamWriteNative(stream);
+ Texture::s_plglist.streamWrite(stream, tex);
+ }
+ s_plglist.streamWrite(stream, this);
+}
+
+uint32
+TexDictionary::streamGetSize(void)
+{
+ uint32 size = 12 + 4;
+ FORLIST(lnk, this->textures){
+ Texture *tex = Texture::fromDict(lnk);
+ size += 12 + tex->streamGetSizeNative();
+ size += 12 + Texture::s_plglist.streamGetSize(tex);
+ }
+ size += 12 + s_plglist.streamGetSize(this);
+ return size;
+}
+
+void
+TexDictionary::setCurrent(TexDictionary *txd)
+{
+ PLUGINOFFSET(TextureGlobals, engine, textureModuleOffset)->currentTexDict = txd;
+}
+
+TexDictionary*
+TexDictionary::getCurrent(void)
+{
+ return PLUGINOFFSET(TextureGlobals, engine, textureModuleOffset)->currentTexDict;
+}
+
+//
+// Texture
+//
+
+static Texture *defaultFindCB(const char *name);
+static Texture *defaultReadCB(const char *name, const char *mask);
+
+Texture *(*Texture::findCB)(const char *name) = defaultFindCB;
+Texture *(*Texture::readCB)(const char *name, const char *mask) = defaultReadCB;
+
+Texture*
+Texture::create(Raster *raster)
+{
+ Texture *tex = (Texture*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_TEXTURE);
+ if(tex == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ tex->dict = nil;
+ tex->inDict.init();
+ memset(tex->name, 0, 32);
+ memset(tex->mask, 0, 32);
+ tex->filterAddressing = (WRAP << 12) | (WRAP << 8) | NEAREST;
+ tex->raster = raster;
+ tex->refCount = 1;
+ TEXTUREGLOBAL(textures).add(&tex->inGlobalList);
+ s_plglist.construct(tex);
+ return tex;
+}
+
+void
+Texture::destroy(void)
+{
+ this->refCount--;
+ if(this->refCount <= 0){
+ s_plglist.destruct(this);
+ if(this->dict)
+ this->inDict.remove();
+ if(this->raster)
+ this->raster->destroy();
+ this->inGlobalList.remove();
+ rwFree(this);
+ numAllocated--;
+ }
+}
+
+static Texture*
+defaultFindCB(const char *name)
+{
+ if(TEXTUREGLOBAL(currentTexDict))
+ return TEXTUREGLOBAL(currentTexDict)->find(name);
+ // TODO: RW searches *all* TXDs otherwise
+ return nil;
+}
+
+
+static Texture*
+defaultReadCB(const char *name, const char *mask)
+{
+ Texture *tex;
+ Image *img;
+
+ img = Image::readMasked(name, mask);
+ if(img){
+ tex = Texture::create(Raster::createFromImage(img));
+ strncpy(tex->name, name, 32);
+ if(mask)
+ strncpy(tex->mask, mask, 32);
+ img->destroy();
+ return tex;
+ }else
+ return nil;
+}
+
+Texture*
+Texture::read(const char *name, const char *mask)
+{
+ (void)mask;
+ Raster *raster = nil;
+ Texture *tex;
+
+ if(tex = Texture::findCB(name), tex){
+ tex->addRef();
+ return tex;
+ }
+ if(TEXTUREGLOBAL(loadTextures)){
+ tex = Texture::readCB(name, mask);
+ if(tex == nil)
+ goto dummytex;
+ }else dummytex: if(TEXTUREGLOBAL(makeDummies)){
+//printf("missing texture %s %s\n", name ? name : "", mask ? mask : "");
+ tex = Texture::create(nil);
+ if(tex == nil)
+ return nil;
+ strncpy(tex->name, name, 32);
+ if(mask)
+ strncpy(tex->mask, mask, 32);
+ raster = Raster::create(0, 0, 0, Raster::DONTALLOCATE);
+ tex->raster = raster;
+ }
+ if(tex && TEXTUREGLOBAL(currentTexDict)){
+ if(tex->dict)
+ tex->inDict.remove();
+ TEXTUREGLOBAL(currentTexDict)->add(tex);
+ }
+ return tex;
+}
+
+Texture*
+Texture::streamRead(Stream *stream)
+{
+ uint32 length;
+ char name[128], mask[128];
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ uint32 filterAddressing = stream->readU32();
+ // if V addressing is 0, copy U
+ if((filterAddressing & 0xF000) == 0)
+ filterAddressing |= (filterAddressing&0xF00) << 4;
+
+ // if using mipmap filter mode, set automipmapping,
+ // if 0x10000 is set, set mipmapping
+
+ if(!findChunk(stream, ID_STRING, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRING"));
+ return nil;
+ }
+ stream->read8(name, length);
+
+ if(!findChunk(stream, ID_STRING, &length, nil)){
+ RWERROR((ERR_CHUNK, "STRING"));
+ return nil;
+ }
+ stream->read8(mask, length);
+
+ bool32 mipState = getMipmapping();
+ bool32 autoMipState = getAutoMipmapping();
+ int32 filter = filterAddressing&0xFF;
+ if(filter == MIPNEAREST || filter == MIPLINEAR ||
+ filter == LINEARMIPNEAREST || filter == LINEARMIPLINEAR){
+ setMipmapping(1);
+ setAutoMipmapping((filterAddressing&0x10000) == 0);
+ }else{
+ setMipmapping(0);
+ setAutoMipmapping(0);
+ }
+
+ Texture *tex = Texture::read(name, mask);
+
+ setMipmapping(mipState);
+ setAutoMipmapping(autoMipState);
+
+ if(tex == nil){
+ s_plglist.streamSkip(stream);
+ return nil;
+ }
+ if(tex->refCount == 1)
+ tex->filterAddressing = filterAddressing&0xFFFF;
+
+ if(s_plglist.streamRead(stream, tex))
+ return tex;
+
+ tex->destroy();
+ return nil;
+}
+
+bool
+Texture::streamWrite(Stream *stream)
+{
+ int size;
+ char buf[36];
+ writeChunkHeader(stream, ID_TEXTURE, this->streamGetSize());
+ writeChunkHeader(stream, ID_STRUCT, 4);
+ uint32 filterAddressing = this->filterAddressing;
+ if(this->raster && (raster->format & Raster::AUTOMIPMAP) == 0)
+ filterAddressing |= 0x10000;
+ stream->writeU32(filterAddressing);
+
+ memset(buf, 0, 36);
+ strncpy(buf, this->name, 32);
+ size = strlen(buf)+4 & ~3;
+ writeChunkHeader(stream, ID_STRING, size);
+ stream->write8(buf, size);
+
+ memset(buf, 0, 36);
+ strncpy(buf, this->mask, 32);
+ size = strlen(buf)+4 & ~3;
+ writeChunkHeader(stream, ID_STRING, size);
+ stream->write8(buf, size);
+
+ s_plglist.streamWrite(stream, this);
+ return true;
+}
+
+uint32
+Texture::streamGetSize(void)
+{
+ uint32 size = 0;
+ size += 12 + 4;
+ size += 12 + 12;
+ size += strlen(this->name)+4 & ~3;
+ size += strlen(this->mask)+4 & ~3;
+ size += 12 + s_plglist.streamGetSize(this);
+ return size;
+}
+
+Texture*
+Texture::streamReadNative(Stream *stream)
+{
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ uint32 platform = stream->readU32();
+ stream->seek(-16);
+ if(platform == FOURCC_PS2)
+ return ps2::readNativeTexture(stream);
+ if(platform == PLATFORM_D3D8)
+ return d3d8::readNativeTexture(stream);
+ if(platform == PLATFORM_D3D9)
+ return d3d9::readNativeTexture(stream);
+ if(platform == PLATFORM_XBOX)
+ return xbox::readNativeTexture(stream);
+ if(platform == PLATFORM_GL3)
+ return gl3::readNativeTexture(stream);
+ assert(0 && "unsupported platform");
+ return nil;
+}
+
+void
+Texture::streamWriteNative(Stream *stream)
+{
+ if(this->raster->platform == PLATFORM_PS2)
+ ps2::writeNativeTexture(this, stream);
+ else if(this->raster->platform == PLATFORM_D3D8)
+ d3d8::writeNativeTexture(this, stream);
+ else if(this->raster->platform == PLATFORM_D3D9)
+ d3d9::writeNativeTexture(this, stream);
+ else if(this->raster->platform == PLATFORM_XBOX)
+ xbox::writeNativeTexture(this, stream);
+ else if(this->raster->platform == PLATFORM_GL3)
+ gl3::writeNativeTexture(this, stream);
+ else
+ assert(0 && "unsupported platform");
+}
+
+uint32
+Texture::streamGetSizeNative(void)
+{
+ if(this->raster->platform == PLATFORM_PS2)
+ return ps2::getSizeNativeTexture(this);
+ if(this->raster->platform == PLATFORM_D3D8)
+ return d3d8::getSizeNativeTexture(this);
+ if(this->raster->platform == PLATFORM_D3D9)
+ return d3d9::getSizeNativeTexture(this);
+ if(this->raster->platform == PLATFORM_XBOX)
+ return xbox::getSizeNativeTexture(this);
+ if(this->raster->platform == PLATFORM_GL3)
+ return gl3::getSizeNativeTexture(this);
+ assert(0 && "unsupported platform");
+ return 0;
+}
+
+
+
+int32 anisotOffset;
+
+static void*
+createAnisot(void *object, int32 offset, int32)
+{
+ *GETANISOTROPYEXT(object) = 1;
+ return object;
+}
+
+static void*
+copyAnisot(void *dst, void *src, int32 offset, int32)
+{
+ *GETANISOTROPYEXT(dst) = *GETANISOTROPYEXT(src);
+ return dst;
+}
+
+static Stream*
+readAnisot(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ *GETANISOTROPYEXT(object) = stream->readI32();
+ return stream;
+}
+
+static Stream*
+writeAnisot(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ stream->writeI32(*GETANISOTROPYEXT(object));
+ return stream;
+}
+
+static int32
+getSizeAnisot(void *object, int32 offset, int32)
+{
+ if(*GETANISOTROPYEXT(object) == 1)
+ return 0;
+ return sizeof(int32);
+}
+
+void
+registerAnisotropyPlugin(void)
+{
+ anisotOffset = Texture::registerPlugin(sizeof(int32), ID_ANISOT, createAnisot, nil, copyAnisot);
+ Texture::registerPluginStream(ID_ANISOT, readAnisot, writeAnisot, getSizeAnisot);
+}
+
+void
+Texture::setMaxAnisotropy(int32 maxaniso)
+{
+ if(anisotOffset > 0)
+ *GETANISOTROPYEXT(this) = maxaniso;
+}
+
+int32
+Texture::getMaxAnisotropy(void)
+{
+ if(anisotOffset > 0)
+ return *GETANISOTROPYEXT(this);
+ return 1;
+}
+
+int32
+getMaxSupportedMaxAnisotropy(void)
+{
+#ifdef RW_D3D9
+ return d3d::d3d9Globals.caps.MaxAnisotropy;
+#endif
+#ifdef RW_GL3
+ return (int32)gl3::gl3Caps.maxAnisotropy;
+#endif
+ return 1;
+}
+
+}
diff --git a/src/tga.cpp b/src/tga.cpp
new file mode 100644
index 0000000..cbe845f
--- /dev/null
+++ b/src/tga.cpp
@@ -0,0 +1,222 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID 0
+
+namespace rw {
+
+
+// NB: this has padding and cannot be streamed directly!
+struct TGAHeader
+{
+ int8 IDlen;
+ int8 colorMapType;
+ int8 imageType;
+ int16 colorMapOrigin;
+ int16 colorMapLength;
+ int8 colorMapDepth;
+ int16 xOrigin, yOrigin;
+ int16 width, height;
+ uint8 depth;
+ uint8 descriptor;
+
+ void read(Stream *stream);
+ void write(Stream *stream);
+};
+
+void
+TGAHeader::read(Stream *stream)
+{
+ IDlen = stream->readI8();
+ colorMapType = stream->readI8();
+ imageType = stream->readI8();
+ colorMapOrigin = stream->readI16();
+ colorMapLength = stream->readI16();
+ colorMapDepth = stream->readI8();
+ xOrigin = stream->readI16();
+ yOrigin = stream->readI16();
+ width = stream->readI16();
+ height = stream->readI16();
+ depth = stream->readU8();
+ descriptor = stream->readU8();
+}
+
+void
+TGAHeader::write(Stream *stream)
+{
+ stream->writeI8(IDlen);
+ stream->writeI8(colorMapType);
+ stream->writeI8(imageType);
+ stream->writeI16(colorMapOrigin);
+ stream->writeI16(colorMapLength);
+ stream->writeI8(colorMapDepth);
+ stream->writeI16(xOrigin);
+ stream->writeI16(yOrigin);
+ stream->writeI16(width);
+ stream->writeI16(height);
+ stream->writeU8(depth);
+ stream->writeU8(descriptor);
+}
+
+Image*
+readTGA(const char *filename)
+{
+ TGAHeader header;
+ Image *image;
+ int depth = 0, palDepth = 0;
+ uint32 length;
+ uint8 *data = getFileContents(filename, &length);
+ assert(data != nil);
+ StreamMemory file;
+ file.open(data, length);
+ header.read(&file);
+
+ assert(header.imageType == 1 || header.imageType == 2);
+ file.seek(header.IDlen);
+ if(header.colorMapType){
+ assert(header.colorMapOrigin == 0);
+ depth = (header.colorMapLength <= 16) ? 4 : 8;
+ palDepth = header.colorMapDepth;
+ assert(palDepth == 24 || palDepth == 32);
+ }else{
+ depth = header.depth;
+ assert(depth == 16 || depth == 24 || depth == 32);
+ }
+
+ image = Image::create(header.width, header.height, depth);
+ image->allocate();
+ uint8 *palette = header.colorMapType ? image->palette : nil;
+ uint8 (*color)[4] = nil;
+ if(palette){
+ int maxlen = depth == 4 ? 16 : 256;
+ color = (uint8(*)[4])palette;
+ int i;
+ for(i = 0; i < header.colorMapLength; i++){
+ color[i][2] = file.readU8();
+ color[i][1] = file.readU8();
+ color[i][0] = file.readU8();
+ color[i][3] = 0xFF;
+ if(palDepth == 32)
+ color[i][3] = file.readU8();
+ }
+ for(; i < maxlen; i++){
+ color[i][0] = color[i][1] = color[i][2] = 0;
+ color[i][3] = 0xFF;
+ }
+ }
+
+ uint8 *pixels = image->pixels;
+ if(!(header.descriptor & 0x20))
+ pixels += (image->height-1)*image->stride;
+ for(int y = 0; y < image->height; y++){
+ uint8 *line = pixels;
+ for(int x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 4:
+ case 8:
+ line[0] = file.readU8();
+ break;
+ case 16:
+ line[0] = file.readU8();
+ line[1] = file.readU8();
+ break;
+ case 24:
+ line[2] = file.readU8();
+ line[1] = file.readU8();
+ line[0] = file.readU8();
+ break;
+ case 32:
+ line[2] = file.readU8();
+ line[1] = file.readU8();
+ line[0] = file.readU8();
+ line[3] = file.readU8();
+ break;
+ }
+ line += image->bpp;
+ }
+ pixels += (header.descriptor&0x20) ?
+ image->stride : -image->stride;
+ }
+
+ file.close();
+ rwFree(data);
+ return image;
+}
+
+void
+writeTGA(Image *image, const char *filename)
+{
+ TGAHeader header;
+ StreamFile file;
+ if(!file.open(filename, "wb")){
+ RWERROR((ERR_FILE, filename));
+ return;
+ }
+ header.IDlen = 0;
+ header.imageType = image->palette != nil ? 1 : 2;
+ header.colorMapType = image->palette != nil;
+ header.colorMapOrigin = 0;
+ header.colorMapLength = image->depth == 4 ? 16 :
+ image->depth == 8 ? 256 : 0;
+ header.colorMapDepth = image->palette ? 32 : 0;
+ header.xOrigin = 0;
+ header.yOrigin = 0;
+ header.width = image->width;
+ header.height = image->height;
+ header.depth = image->depth == 4 ? 8 : image->depth;
+ header.descriptor = 0x20 | (image->depth == 32 ? 8 : 0);
+ header.write(&file);
+
+ uint8 *palette = header.colorMapType ? image->palette : nil;
+ uint8 (*color)[4] = (uint8(*)[4])palette;;
+ if(palette)
+ for(int i = 0; i < header.colorMapLength; i++){
+ file.writeU8(color[i][2]);
+ file.writeU8(color[i][1]);
+ file.writeU8(color[i][0]);
+ file.writeU8(color[i][3]);
+ }
+
+ uint8 *line = image->pixels;
+ uint8 *p;
+ for(int y = 0; y < image->height; y++){
+ p = line;
+ for(int x = 0; x < image->width; x++){
+ switch(image->depth){
+ case 4:
+ case 8:
+ file.writeU8(p[0]);
+ break;
+ case 16:
+ file.writeU8(p[0]);
+ file.writeU8(p[1]);
+ break;
+ case 24:
+ file.writeU8(p[2]);
+ file.writeU8(p[1]);
+ file.writeU8(p[0]);
+ break;
+ case 32:
+ file.writeU8(p[2]);
+ file.writeU8(p[1]);
+ file.writeU8(p[0]);
+ file.writeU8(p[3]);
+ break;
+ }
+ p += image->bpp;
+ }
+ line += image->stride;
+ }
+ file.close();
+}
+
+}
diff --git a/src/tristrip.cpp b/src/tristrip.cpp
new file mode 100644
index 0000000..bd9123f
--- /dev/null
+++ b/src/tristrip.cpp
@@ -0,0 +1,682 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+
+struct GraphEdge
+{
+ int32 node; /* index of the connected node */
+ uint32 isConnected : 1; /* is connected to other node */
+ uint32 otherEdge : 2; /* edge number on connected node */
+ uint32 isStrip : 1; /* is strip edge */
+};
+
+struct StripNode
+{
+ uint16 v[3]; /* vertex indices */
+ uint8 parent : 2; /* tunnel parent node (edge index) */
+ uint8 visited : 1; /* visited in breadth first search */
+ uint8 stripVisited : 1; /* strip starting at this node was visited during search */
+ uint8 isEnd : 1; /* is in end list */
+ GraphEdge e[3];
+ int32 stripId; /* index of start node */
+ LLLink inlist;
+};
+
+struct StripMesh
+{
+ int32 numNodes;
+ StripNode *nodes;
+ LinkList loneNodes; /* nodes not connected to any others */
+ LinkList endNodes; /* strip start/end nodes */
+};
+
+//#define trace(...) printf(__VA_ARGS__)
+#define trace(...)
+
+static void
+printNode(StripMesh *sm, StripNode *n)
+{
+ trace("%3ld: %3d %3d.%d %3d.%d %3d.%d || %3d %3d %3d\n",
+ n - sm->nodes,
+ n->stripId,
+ n->e[0].node,
+ n->e[0].isStrip,
+ n->e[1].node,
+ n->e[1].isStrip,
+ n->e[2].node,
+ n->e[2].isStrip,
+ n->v[0],
+ n->v[1],
+ n->v[2]);
+}
+
+static void
+printLone(StripMesh *sm)
+{
+ FORLIST(lnk, sm->loneNodes)
+ printNode(sm, LLLinkGetData(lnk, StripNode, inlist));
+}
+
+static void
+printEnds(StripMesh *sm)
+{
+ FORLIST(lnk, sm->endNodes)
+ printNode(sm, LLLinkGetData(lnk, StripNode, inlist));
+}
+
+static void
+printSmesh(StripMesh *sm)
+{
+ for(int32 i = 0; i < sm->numNodes; i++)
+ printNode(sm, &sm->nodes[i]);
+}
+
+static void
+collectFaces(Geometry *geo, StripMesh *sm, uint16 m)
+{
+ StripNode *n;
+ Triangle *t;
+ sm->numNodes = 0;
+ for(int32 i = 0; i < geo->numTriangles; i++){
+ t = &geo->triangles[i];
+ if(t->matId == m){
+ n = &sm->nodes[sm->numNodes++];
+ n->v[0] = t->v[0];
+ n->v[1] = t->v[1];
+ n->v[2] = t->v[2];
+ assert(t->v[0] < geo->numVertices);
+ assert(t->v[1] < geo->numVertices);
+ assert(t->v[2] < geo->numVertices);
+ n->e[0].node = 0;
+ n->e[1].node = 0;
+ n->e[2].node = 0;
+ n->e[0].isConnected = 0;
+ n->e[1].isConnected = 0;
+ n->e[2].isConnected = 0;
+ n->e[0].isStrip = 0;
+ n->e[1].isStrip = 0;
+ n->e[2].isStrip = 0;
+ n->parent = 0;
+ n->visited = 0;
+ n->stripVisited = 0;
+ n->isEnd = 0;
+ n->stripId = -1;
+ n->inlist.init();
+ }
+ }
+}
+
+/* Find Triangle that has edge e that is not connected yet. */
+static GraphEdge
+findEdge(StripMesh *sm, int32 e[2])
+{
+ StripNode *n;
+ GraphEdge ge = { 0, 0, 0, 0 };
+ for(int32 i = 0; i < sm->numNodes; i++){
+ n = &sm->nodes[i];
+ for(int32 j = 0; j < 3; j++){
+ if(n->e[j].isConnected)
+ continue;
+ if(e[0] == n->v[j] &&
+ e[1] == n->v[(j+1) % 3]){
+ ge.node = i;
+ // signal success
+ ge.isConnected = 1;
+ ge.otherEdge = j;
+ return ge;
+ }
+ }
+ }
+ return ge;
+}
+
+/* Connect nodes sharing an edge, preserving winding */
+static void
+connectNodesPreserve(StripMesh *sm)
+{
+ StripNode *n, *nn;
+ int32 e[2];
+ GraphEdge ge;
+ for(int32 i = 0; i < sm->numNodes; i++){
+ n = &sm->nodes[i];
+ for(int32 j = 0; j < 3; j++){
+ if(n->e[j].isConnected)
+ continue;
+
+ /* flip edge and search for node */
+ e[1] = n->v[j];
+ e[0] = n->v[(j+1) % 3];
+ ge = findEdge(sm, e);
+ if(ge.isConnected){
+ /* found node, now connect */
+ n->e[j].node = ge.node;
+ n->e[j].isConnected = 1;
+ n->e[j].otherEdge = ge.otherEdge;
+ n->e[j].isStrip = 0;
+ nn = &sm->nodes[ge.node];
+ nn->e[ge.otherEdge].node = i;
+ nn->e[ge.otherEdge].isConnected = 1;
+ nn->e[ge.otherEdge].otherEdge = j;
+ nn->e[ge.otherEdge].isStrip = 0;
+ }
+ }
+ }
+}
+
+static int32
+numConnections(StripNode *n)
+{
+ return n->e[0].isConnected +
+ n->e[1].isConnected +
+ n->e[2].isConnected;
+}
+
+static int32
+numStripEdges(StripNode *n)
+{
+ return n->e[0].isStrip +
+ n->e[1].isStrip +
+ n->e[2].isStrip;
+}
+
+#define IsEnd(n) (numConnections(n) > 0 && numStripEdges(n) < 2)
+
+/* Complement the strip-ness of an edge */
+static void
+complementEdge(StripMesh *sm, GraphEdge *e)
+{
+ e->isStrip = !e->isStrip;
+ e = &sm->nodes[e->node].e[e->otherEdge];
+ e->isStrip = !e->isStrip;
+}
+
+/* While possible extend a strip from a starting node until
+ * we find a node already in a strip. N.B. this function
+ * makes no attempts to connect to an already existing strip.
+ * It also doesn't try to alternate between left and right. */
+static void
+extendStrip(StripMesh *sm, StripNode *start)
+{
+ StripNode *n, *nn;
+ n = start;
+ if(numConnections(n) == 0){
+ sm->loneNodes.append(&n->inlist);
+ return;
+ }
+ sm->endNodes.append(&n->inlist);
+ n->isEnd = 1;
+loop:
+ /* Find the next node to connect to on any of the three edges */
+ for(int32 i = 0; i < 3; i++){
+ if(!n->e[i].isConnected)
+ continue;
+ nn = &sm->nodes[n->e[i].node];
+ if(nn->stripId >= 0)
+ continue;
+
+ /* found one */
+ nn->stripId = n->stripId;
+ /* We know it's not a strip edge yet,
+ * so complementing it will make it one. */
+ complementEdge(sm, &n->e[i]);
+ n = nn;
+ goto loop;
+ }
+ if(n != start){
+ sm->endNodes.append(&n->inlist);
+ n->isEnd = 1;
+ }
+}
+
+static void
+buildStrips(StripMesh *sm)
+{
+ StripNode *n;
+ for(int32 i = 0; i < sm->numNodes; i++){
+ n = &sm->nodes[i];
+ if(n->stripId >= 0)
+ continue;
+ n->stripId = i;
+ extendStrip(sm, n);
+ }
+}
+
+static StripNode*
+findTunnel(StripMesh *sm, StripNode *n)
+{
+ LinkList searchNodes;
+ StripNode *nn;
+ int edgetype;
+ int isEnd;
+
+ searchNodes.init();
+ edgetype = 0;
+ for(;;){
+ for(int32 i = 0; i < 3; i++){
+ /* Find a node connected by the right edgetype */
+ if(!n->e[i].isConnected ||
+ n->e[i].isStrip != edgetype)
+ continue;
+ nn = &sm->nodes[n->e[i].node];
+
+ /* If the node has been visited already,
+ * there's a shorter path. */
+ if(nn->visited)
+ continue;
+
+ /* Don't allow non-strip edge between nodes of the same
+ * strip to prevent loops.
+ * Actually these edges are allowed under certain
+ * circumstances, but they require complex checks. */
+ if(edgetype == 0 &&
+ n->stripId == nn->stripId)
+ continue;
+
+ isEnd = IsEnd(nn);
+
+ /* Can't add end nodes to two lists, so skip. */
+ if(isEnd && edgetype == 1)
+ continue;
+
+ nn->parent = n->e[i].otherEdge;
+ nn->visited = 1;
+ sm->nodes[nn->stripId].stripVisited = 1;
+
+ /* Search complete. */
+ if(isEnd && edgetype == 0)
+ return nn;
+
+ /* Found a valid node. */
+ searchNodes.append(&nn->inlist);
+ }
+ if(searchNodes.isEmpty())
+ return nil;
+ n = LLLinkGetData(searchNodes.link.next, StripNode, inlist);
+ n->inlist.remove();
+ edgetype = !edgetype;
+ }
+}
+
+static void
+resetGraph(StripMesh *sm)
+{
+ StripNode *n;
+ for(int32 i = 0; i < sm->numNodes; i++){
+ n = &sm->nodes[i];
+ n->visited = 0;
+ n->stripVisited = 0;
+ }
+}
+
+static StripNode*
+walkStrip(StripMesh *sm, StripNode *start)
+{
+ StripNode *n, *nn;
+ int32 last;
+
+//trace("stripend: ");
+//printNode(sm, start);
+
+ n = start;
+ last = -1;
+ for(;;n = nn){
+ n->visited = 0;
+ n->stripVisited = 0;
+ if(n->isEnd)
+ n->inlist.remove();
+ n->isEnd = 0;
+
+ if(IsEnd(n) && n != start)
+ return n;
+
+ /* find next node */
+ nn = nil;
+ for(int32 i = 0; i < 3; i++){
+ if(!n->e[i].isStrip || i == last)
+ continue;
+ nn = &sm->nodes[n->e[i].node];
+ last = n->e[i].otherEdge;
+ nn->stripId = n->stripId;
+ break;
+ }
+//trace(" next: ");
+//printNode(sm, nn);
+ if(nn == nil)
+ return nil;
+ }
+}
+
+static void
+applyTunnel(StripMesh *sm, StripNode *end, StripNode *start)
+{
+ LinkList tmplist;
+ StripNode *n, *nn;
+
+ for(n = end; n != start; n = &sm->nodes[n->e[n->parent].node]){
+//trace(" ");
+//printNode(sm, n);
+ complementEdge(sm, &n->e[n->parent]);
+ }
+//trace(" ");
+//printNode(sm, start);
+
+//printSmesh(sm);
+//trace("-------\n");
+ tmplist.init();
+ while(!sm->endNodes.isEmpty()){
+ n = LLLinkGetData(sm->endNodes.link.next, StripNode, inlist);
+ /* take out of end list */
+ n->inlist.remove();
+ n->isEnd = 0;
+ /* no longer an end node */
+ if(!IsEnd(n))
+ continue;
+ // TODO: only walk strip if it was touched
+ /* set new id, walk strip and find other end */
+ n->stripId = n - sm->nodes;
+ nn = walkStrip(sm, n);
+ tmplist.append(&n->inlist);
+ n->isEnd = 1;
+ if(nn && n != nn){
+ tmplist.append(&nn->inlist);
+ nn->isEnd = 1;
+ }
+ }
+ /* Move new end nodes to the real list. */
+ sm->endNodes = tmplist;
+ sm->endNodes.link.next->prev = &sm->endNodes.link;
+ sm->endNodes.link.prev->next = &sm->endNodes.link;
+}
+
+static void
+tunnel(StripMesh *sm)
+{
+ StripNode *n, *nn;
+
+again:
+ FORLIST(lnk, sm->endNodes){
+ n = LLLinkGetData(lnk, StripNode, inlist);
+// trace("searching %p %d\n", n, numStripEdges(n));
+ nn = findTunnel(sm, n);
+// trace(" %p %p\n", n, nn);
+
+ if(nn){
+ applyTunnel(sm, nn, n);
+ resetGraph(sm);
+ /* applyTunnel changes sm->endNodes, so we have to
+ * jump out of the loop. */
+ goto again;
+ }
+ resetGraph(sm);
+ }
+ trace("tunneling done!\n");
+}
+
+/* Get next edge in strip.
+ * Last is the edge index whence we came lest we go back. */
+static int
+getNextEdge(StripNode *n, int32 last)
+{
+ int32 i;
+ for(i = 0; i < 3; i++)
+ if(n->e[i].isStrip && i != last)
+ return i;
+ return -1;
+}
+
+#define NEXT(x) (((x)+1) % 3)
+#define PREV(x) (((x)+2) % 3)
+#define RIGHT(x) NEXT(x)
+#define LEFT(x) PREV(x)
+
+/* Generate mesh indices for all strips in a StripMesh */
+static void
+makeMesh(StripMesh *sm, Mesh *m)
+{
+ int32 i, j;
+ int32 rightturn, lastrightturn;
+ int32 seam;
+ int32 even;
+ StripNode *n;
+
+ /* three indices + two for stitch per triangle must be enough */
+ m->indices = rwNewT(uint16, sm->numNodes*5, MEMDUR_FUNCTION | ID_GEOMETRY);
+ memset(m->indices, 0xFF, sm->numNodes*5*sizeof(uint16));
+
+ even = 1;
+ FORLIST(lnk, sm->endNodes){
+ n = LLLinkGetData(lnk, StripNode, inlist);
+ /* only interested in start nodes, not the ends */
+ if(n->stripId != (n - sm->nodes))
+ continue;
+
+ /* i is the edge we enter this triangle from.
+ * j is the edge we exit. */
+ j = getNextEdge(n, -1);
+ /* starting triangle must have connection */
+ if(j < 0)
+ continue;
+ /* Space to stitch together strips */
+ seam = m->numIndices;
+ if(seam)
+ m->numIndices += 2;
+ /* Start ccw for even tris */
+ if(even){
+ /* Start with a right turn */
+ i = LEFT(j);
+ m->indices[m->numIndices++] = n->v[i];
+ m->indices[m->numIndices++] = n->v[NEXT(i)];
+ }else{
+ /* Start with a left turn */
+ i = RIGHT(j);
+ m->indices[m->numIndices++] = n->v[NEXT(i)];
+ m->indices[m->numIndices++] = n->v[i];
+ }
+trace("\nstart %d %d\n", numStripEdges(n), m->numIndices-2);
+ lastrightturn = -1;
+
+ while(j >= 0){
+ rightturn = RIGHT(i) == j;
+ if(rightturn == lastrightturn){
+ // insert a swap if we're not alternating
+ m->indices[m->numIndices] = m->indices[m->numIndices-2];
+trace("SWAP\n");
+ m->numIndices++;
+ even = !even;
+ }
+trace("%d:%d%c %d %d %d\n", n-sm->nodes, m->numIndices, even ? ' ' : '.', n->v[0], n->v[1], n->v[2]);
+ lastrightturn = rightturn;
+ if(rightturn)
+ m->indices[m->numIndices++] = n->v[NEXT(j)];
+ else
+ m->indices[m->numIndices++] = n->v[j];
+ even = !even;
+
+ /* go to next triangle */
+ i = n->e[j].otherEdge;
+ n = &sm->nodes[n->e[j].node];
+ j = getNextEdge(n, i);
+ }
+
+ /* finish strip */
+trace("%d:%d%c %d %d %d\nend\n", n-sm->nodes, m->numIndices, even ? ' ' : '.', n->v[0], n->v[1], n->v[2]);
+ m->indices[m->numIndices++] = n->v[LEFT(i)];
+ even = !even;
+ if(seam){
+ m->indices[seam] = m->indices[seam-1];
+ m->indices[seam+1] = m->indices[seam+2];
+trace("STITCH %d: %d %d\n", seam, m->indices[seam], m->indices[seam+1]);
+ }
+ }
+
+ /* Add all unconnected and lonely triangles */
+ FORLIST(lnk, sm->endNodes){
+ n = LLLinkGetData(lnk, StripNode, inlist);
+ if(numStripEdges(n) != 0)
+ continue;
+ if(m->numIndices != 0){
+ m->indices[m->numIndices] = m->indices[m->numIndices-1];
+ m->numIndices++;
+ m->indices[m->numIndices++] = n->v[!even];
+ }
+ m->indices[m->numIndices++] = n->v[!even];
+ m->indices[m->numIndices++] = n->v[even];
+ m->indices[m->numIndices++] = n->v[2];
+ even = !even;
+ }
+ FORLIST(lnk, sm->loneNodes){
+ n = LLLinkGetData(lnk, StripNode, inlist);
+ if(m->numIndices != 0){
+ m->indices[m->numIndices] = m->indices[m->numIndices-1];
+ m->numIndices++;
+ m->indices[m->numIndices++] = n->v[!even];
+ }
+ m->indices[m->numIndices++] = n->v[!even];
+ m->indices[m->numIndices++] = n->v[even];
+ m->indices[m->numIndices++] = n->v[2];
+ even = !even;
+ }
+}
+
+static void verifyMesh(Geometry *geo);
+
+/*
+ * For each material:
+ * 1. build dual graph (collectFaces, connectNodes)
+ * 2. make some simple strip (buildStrips)
+ * 3. apply tunnel operator (tunnel)
+ */
+void
+Geometry::buildTristrips(void)
+{
+ int32 i;
+ uint16 *indices;
+ MeshHeader *header;
+ Mesh *ms, *md;
+ StripMesh smesh;
+
+// trace("%ld\n", sizeof(StripNode));
+
+ this->allocateMeshes(matList.numMaterials, 0, 1);
+
+ smesh.nodes = rwNewT(StripNode, this->numTriangles, MEMDUR_FUNCTION | ID_GEOMETRY);
+ ms = this->meshHeader->getMeshes();
+ for(int32 i = 0; i < this->matList.numMaterials; i++){
+ smesh.loneNodes.init();
+ smesh.endNodes.init();
+ collectFaces(this, &smesh, i);
+ connectNodesPreserve(&smesh);
+ buildStrips(&smesh);
+printSmesh(&smesh);
+//trace("-------\n");
+//printLone(&smesh);
+//trace("-------\n");
+//printEnds(&smesh);
+//trace("-------\n");
+ // TODO: make this work
+// tunnel(&smesh);
+//trace("-------\n");
+//printEnds(&smesh);
+
+ ms[i].material = this->matList.materials[i];
+ makeMesh(&smesh, &ms[i]);
+ this->meshHeader->totalIndices += ms[i].numIndices;
+ }
+ rwFree(smesh.nodes);
+
+ /* Now re-allocate and copy data */
+ header = this->meshHeader;
+ this->meshHeader = nil;
+ this->allocateMeshes(header->numMeshes, header->totalIndices, 0);
+ this->meshHeader->flags = MeshHeader::TRISTRIP;
+ md = this->meshHeader->getMeshes();
+ indices = md->indices;
+ for(i = 0; i < header->numMeshes; i++){
+ md[i].material = ms[i].material;
+ md[i].numIndices = ms[i].numIndices;
+ md[i].indices = indices;
+ indices += md[i].numIndices;
+ memcpy(md[i].indices, ms[i].indices, md[i].numIndices*sizeof(uint16));
+ rwFree(ms[i].indices);
+ }
+ rwFree(header);
+
+ verifyMesh(this);
+}
+
+/* Check that tristripped mesh and geometry triangles are actually the same. */
+static void
+verifyMesh(Geometry *geo)
+{
+ int32 i, k;
+ uint32 j;
+ int32 x;
+ int32 a, b, c, m;
+ Mesh *mesh;
+ Triangle *t;
+ uint8 *seen;
+
+ seen = rwNewT(uint8, geo->numTriangles, MEMDUR_FUNCTION | ID_GEOMETRY);
+ memset(seen, 0, geo->numTriangles);
+
+ mesh = geo->meshHeader->getMeshes();
+ for(i = 0; i < geo->meshHeader->numMeshes; i++){
+ m = geo->matList.findIndex(mesh->material);
+ x = 0;
+ for(j = 0; j < mesh->numIndices-2; j++){
+ a = mesh->indices[j+x];
+ x = !x;
+ b = mesh->indices[j+x];
+ c = mesh->indices[j+2];
+ if(a >= geo->numVertices ||
+ b >= geo->numVertices ||
+ c >= geo->numVertices){
+ fprintf(stderr, "triangle %d %d %d out of range (%d)\n", a, b, c, geo->numVertices);
+ goto loss;
+ }
+ if(a == b || a == c || b == c)
+ continue;
+trace("%d %d %d\n", a, b, c);
+
+ /* now that we have a triangle, try to find it */
+ for(k = 0; k < geo->numTriangles; k++){
+ t = &geo->triangles[k];
+ if(seen[k] || t->matId != m) continue;
+ if((t->v[0] == a && t->v[1] == b && t->v[2] == c) ||
+ (t->v[1] == a && t->v[2] == b && t->v[0] == c) ||
+ (t->v[2] == a && t->v[0] == b && t->v[1] == c)){
+ seen[k] = 1;
+ goto found;
+ }
+ }
+ goto loss;
+ found: ;
+ }
+ mesh++;
+ }
+
+ /* Also check that all triangles are in the mesh */
+ for(i = 0; i < geo->numTriangles; i++)
+ if(!seen[i]){
+ loss:
+ fprintf(stderr, "TRISTRIP verify failed\n");
+ exit(1);
+ }
+
+ rwFree(seen);
+}
+
+}
diff --git a/src/userdata.cpp b/src/userdata.cpp
new file mode 100644
index 0000000..e17a3d5
--- /dev/null
+++ b/src/userdata.cpp
@@ -0,0 +1,364 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwuserdata.h"
+
+#define PLUGIN_ID ID_USERDATA
+
+namespace rw {
+
+UserDataGlobals userDataGlobals;
+
+#define udMalloc(sz) rwMalloc(sz, MEMDUR_EVENT | ID_USERDATA)
+
+void
+UserDataArray::setString(int32 n, const char *s)
+{
+ int32 len;
+ char **sp = &((char**)this->data)[n];
+ rwFree(*sp);
+ len = (int32)strlen(s)+1;
+ *sp = (char*)udMalloc(len);
+ if(*sp)
+ strncpy(*sp, s, len);
+}
+
+static void*
+createUserData(void *object, int32 offset, int32)
+{
+ UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset);
+ ext->numArrays = 0;
+ ext->arrays = nil;
+ return object;
+}
+
+static void*
+destroyUserData(void *object, int32 offset, int32)
+{
+ int32 i, j;
+ char **strar;
+ UserDataArray *a;
+ UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset);
+ a = ext->arrays;
+ for(i = 0; i < ext->numArrays; i++){
+ rwFree(a->name);
+ switch(a->datatype){
+ case USERDATASTRING:
+ strar = (char**)a->data;
+ for(j = 0; j < a->numElements; j++)
+ rwFree(strar[j]);
+ /* fallthrough */
+ case USERDATAINT:
+ case USERDATAFLOAT:
+ rwFree(a->data);
+ break;
+ }
+ a++;
+ }
+ rwFree(ext->arrays);
+ ext->numArrays = 0;
+ ext->arrays = nil;
+ return object;
+}
+
+static void*
+copyUserData(void *dst, void *src, int32 offset, int32)
+{
+ int32 i, j;
+ char **srcstrar, **dststrar;
+ UserDataArray *srca, *dsta;
+ UserDataExtension *srcext = PLUGINOFFSET(UserDataExtension, src, offset);
+ UserDataExtension *dstext = PLUGINOFFSET(UserDataExtension, dst, offset);
+ dstext->numArrays = srcext->numArrays;
+ dstext->arrays = (UserDataArray*)udMalloc(dstext->numArrays*sizeof(UserDataArray));
+ srca = srcext->arrays;
+ dsta = srcext->arrays;
+ for(i = 0; i < srcext->numArrays; i++){
+ int32 len = (int32)strlen(srca->name)+1;
+ dsta->name = (char*)udMalloc(len);
+ strncpy(dsta->name, srca->name, len);
+ dsta->datatype = srca->datatype;
+ dsta->numElements = srca->numElements;
+ switch(srca->datatype){
+ case USERDATAINT:
+ dsta->data = (int32*)udMalloc(sizeof(int32)*dsta->numElements);
+ memcpy(dsta->data, srca->data, sizeof(int32)*dsta->numElements);
+ break;
+ case USERDATAFLOAT:
+ dsta->data = (float32*)udMalloc(sizeof(float32)*dsta->numElements);
+ memcpy(dsta->data, srca->data, sizeof(float32)*dsta->numElements);
+ break;
+ case USERDATASTRING:
+ dststrar = (char**)udMalloc(sizeof(char*)*dsta->numElements);
+ dsta->data = dststrar;
+ srcstrar = (char**)srca->data;
+ for(j = 0; j < dsta->numElements; j++){
+ len = (int32)strlen(srcstrar[j])+1;
+ dststrar[j] = (char*)udMalloc(len);
+ strncpy(dststrar[j], srcstrar[j], len);
+ }
+ break;
+ }
+ srca++;
+ dsta++;
+ }
+ return dst;
+}
+
+static Stream*
+readUserData(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ int32 i, j;
+ char **strar;
+ UserDataArray *a;
+ UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset);
+ ext->numArrays = stream->readI32();
+ ext->arrays = (UserDataArray*)udMalloc(ext->numArrays*sizeof(UserDataArray));
+ a = ext->arrays;
+ for(i = 0; i < ext->numArrays; i++){
+ int32 len = stream->readI32();
+ a->name = (char*)udMalloc(len);
+ stream->read8(a->name, len);
+ a->datatype = stream->readU32();
+ a->numElements = stream->readI32();
+ switch(a->datatype){
+ case USERDATAINT:
+ a->data = (int32*)udMalloc(sizeof(int32)*a->numElements);
+ stream->read32(a->data, sizeof(int32)*a->numElements);
+ break;
+ case USERDATAFLOAT:
+ a->data = (float32*)udMalloc(sizeof(float32)*a->numElements);
+ stream->read32(a->data, sizeof(float32)*a->numElements);
+ break;
+ case USERDATASTRING:
+ strar = (char**)udMalloc(sizeof(char*)*a->numElements);
+ a->data = strar;
+ for(j = 0; j < a->numElements; j++){
+ len = stream->readI32();
+ strar[j] = (char*)udMalloc(len);
+ stream->read8(strar[j], len);
+ }
+ break;
+ }
+ a++;
+ }
+ return stream;
+}
+
+static Stream*
+writeUserData(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ int32 len;
+ int32 i, j;
+ char **strar;
+ UserDataArray *a;
+ UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset);
+ stream->writeI32(ext->numArrays);
+ a = ext->arrays;
+ for(i = 0; i < ext->numArrays; i++){
+ len = (int32)strlen(a->name)+1;
+ stream->writeI32(len);
+ stream->write8(a->name, len);
+ stream->writeU32(a->datatype);
+ stream->writeI32(a->numElements);
+ switch(a->datatype){
+ case USERDATAINT:
+ stream->write32(a->data, sizeof(int32)*a->numElements);
+ break;
+ case USERDATAFLOAT:
+ stream->write32(a->data, sizeof(float32)*a->numElements);
+ break;
+ case USERDATASTRING:
+ strar = (char**)a->data;
+ for(j = 0; j < a->numElements; j++){
+ len = (int32)strlen(strar[j])+1;
+ stream->writeI32(len);
+ stream->write8(strar[j], len);
+ }
+ break;
+ }
+ a++;
+ }
+ return stream;
+}
+
+static int32
+getSizeUserData(void *object, int32 offset, int32)
+{
+ int32 len;
+ int32 i, j;
+ char **strar;
+ int32 size;
+ UserDataArray *a;
+ UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset);
+ if(ext->numArrays == 0)
+ return 0;
+ size = 4; // numArrays
+ a = ext->arrays;
+ for(i = 0; i < ext->numArrays; i++){
+ len = (int32)strlen(a->name)+1;
+ size += 4 + len + 4 + 4; // name len, name, type, numElements
+ switch(a->datatype){
+ case USERDATAINT:
+ size += sizeof(int32)*a->numElements;
+ break;
+ case USERDATAFLOAT:
+ size += sizeof(float32)*a->numElements;
+ break;
+ case USERDATASTRING:
+ strar = (char**)a->data;
+ for(j = 0; j < a->numElements; j++){
+ len = (int32)strlen(strar[j])+1;
+ size += 4 + len; // len and string
+ }
+ break;
+ }
+ a++;
+ }
+ return size;
+}
+
+int32
+UserDataExtension::add(const char *name, int32 datatype, int32 numElements)
+{
+ int32 i;
+ int32 len;
+ int32 typesz;
+ UserDataArray *a;
+ // try to find empty slot
+ for(i = 0; i < this->numArrays; i++)
+ if(this->arrays[i].datatype == USERDATANA)
+ goto alloc;
+ // have to realloc
+ a = (UserDataArray*)udMalloc((this->numArrays+1)*sizeof(UserDataArray));
+ if(a == nil)
+ return -1;
+ memcpy(a, this->arrays, this->numArrays*sizeof(UserDataArray));
+ rwFree(this->arrays);
+ this->arrays = a;
+ i = this->numArrays++;
+alloc:
+ a = &this->arrays[i];
+ len = (int32)strlen(name)+1;
+ a->name = (char*)udMalloc(len+1);
+ assert(a->name);
+ strncpy(a->name, name, len);
+ a->datatype = datatype;
+ a->numElements = numElements;
+ typesz = datatype == USERDATAINT ? sizeof(int32) :
+ datatype == USERDATAFLOAT ? sizeof(float32) :
+ datatype == USERDATASTRING ? sizeof(char*) : 0;
+ a->data = udMalloc(typesz*numElements);
+ assert(a->data);
+ memset(a->data, 0, typesz*numElements);
+ return i;
+}
+
+void
+UserDataExtension::remove(int32 n)
+{
+ int32 i;
+ UserDataArray *a = &this->arrays[n];
+ if(a->name){
+ rwFree(a->name);
+ a->name = nil;
+ }
+ if(a->datatype == USERDATASTRING)
+ for(i = 0; i < a->numElements; i++)
+ rwFree(((char**)a->data)[i]);
+ if(a->data){
+ rwFree(a->data);
+ a->data = nil;
+ }
+ a->datatype = USERDATANA;
+ a->numElements = 0;
+}
+
+int32
+UserDataExtension::findIndex(const char *name) {
+ for(int32 i = 0; i < this->numArrays; i++)
+ if(strcmp(this->arrays[i].name, name) == 0)
+ return i;
+ return -1;
+}
+
+#define ACCESSOR(TYPE, NAME) \
+int32 \
+UserDataArray::NAME##Add(TYPE *t, const char *name, int32 datatype, int32 numElements) \
+{ \
+ return PLUGINOFFSET(UserDataExtension, t, userDataGlobals.NAME##Offset)->add(name, datatype, numElements); \
+} \
+void \
+UserDataArray::NAME##Remove(TYPE *t, int32 n) \
+{ \
+ PLUGINOFFSET(UserDataExtension, t, userDataGlobals.NAME##Offset)->remove(n); \
+} \
+int32 \
+UserDataArray::NAME##GetCount(TYPE *t) \
+{ \
+ return PLUGINOFFSET(UserDataExtension, t, userDataGlobals.NAME##Offset)->getCount(); \
+} \
+UserDataArray* \
+UserDataArray::NAME##Get(TYPE *t, int32 n) \
+{ \
+ return PLUGINOFFSET(UserDataExtension, t, userDataGlobals.NAME##Offset)->get(n); \
+} \
+int32 \
+UserDataArray::NAME##FindIndex(TYPE *t, const char *name) \
+{ \
+ return PLUGINOFFSET(UserDataExtension, t, userDataGlobals.NAME##Offset)->findIndex(name); \
+}
+
+ACCESSOR(Geometry, geometry)
+ACCESSOR(Frame, frame)
+ACCESSOR(Camera, camera)
+ACCESSOR(Light, light)
+ACCESSOR(Material, material)
+ACCESSOR(Texture, texture)
+
+UserDataExtension *UserDataExtension::get(Geometry *geo) { return PLUGINOFFSET(UserDataExtension, geo, userDataGlobals.geometryOffset); }
+UserDataExtension *UserDataExtension::get(Frame *frame) { return PLUGINOFFSET(UserDataExtension, frame, userDataGlobals.frameOffset); }
+UserDataExtension *UserDataExtension::get(Camera *cam) { return PLUGINOFFSET(UserDataExtension, cam, userDataGlobals.cameraOffset); }
+UserDataExtension *UserDataExtension::get(Light *light) { return PLUGINOFFSET(UserDataExtension, light, userDataGlobals.lightOffset); }
+UserDataExtension *UserDataExtension::get(Material *mat) { return PLUGINOFFSET(UserDataExtension, mat, userDataGlobals.materialOffset); }
+UserDataExtension *UserDataExtension::get(Texture *tex) { return PLUGINOFFSET(UserDataExtension, tex, userDataGlobals.textureOffset); }
+
+void
+registerUserDataPlugin(void)
+{
+ // TODO: World Sector
+
+ userDataGlobals.geometryOffset = Geometry::registerPlugin(sizeof(UserDataExtension),
+ ID_USERDATA, createUserData, destroyUserData, copyUserData);
+ Geometry::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData);
+
+ userDataGlobals.frameOffset = Frame::registerPlugin(sizeof(UserDataExtension),
+ ID_USERDATA, createUserData, destroyUserData, copyUserData);
+ Frame::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData);
+
+ userDataGlobals.cameraOffset = Camera::registerPlugin(sizeof(UserDataExtension),
+ ID_USERDATA, createUserData, destroyUserData, copyUserData);
+ Camera::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData);
+
+ userDataGlobals.lightOffset = Light::registerPlugin(sizeof(UserDataExtension),
+ ID_USERDATA, createUserData, destroyUserData, copyUserData);
+ Light::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData);
+
+ userDataGlobals.materialOffset = Material::registerPlugin(sizeof(UserDataExtension),
+ ID_USERDATA, createUserData, destroyUserData, copyUserData);
+ Material::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData);
+
+ userDataGlobals.textureOffset = Texture::registerPlugin(sizeof(UserDataExtension),
+ ID_USERDATA, createUserData, destroyUserData, copyUserData);
+ Texture::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData);
+}
+
+}
diff --git a/src/uvanim.cpp b/src/uvanim.cpp
new file mode 100644
index 0000000..f197b28
--- /dev/null
+++ b/src/uvanim.cpp
@@ -0,0 +1,502 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwanim.h"
+#include "rwplugins.h"
+
+#define PLUGIN_ID ID_UVANIMATION
+
+namespace rw {
+
+//
+// UVAnim
+//
+
+void
+UVAnimCustomData::destroy(Animation *anim)
+{
+ this->refCount--;
+ if(this->refCount <= 0)
+ anim->destroy();
+}
+
+UVAnimDictionary *currentUVAnimDictionary;
+
+UVAnimDictionary*
+UVAnimDictionary::create(void)
+{
+ UVAnimDictionary *dict = (UVAnimDictionary*)rwMalloc(sizeof(UVAnimDictionary), MEMDUR_EVENT | ID_UVANIMATION);
+ if(dict == nil){
+ RWERROR((ERR_ALLOC, sizeof(UVAnimDictionary)));
+ return nil;
+ }
+ dict->animations.init();
+ return dict;
+}
+
+void
+UVAnimDictionary::destroy(void)
+{
+ FORLIST(lnk, this->animations){
+ UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk);
+ UVAnimCustomData *cust = UVAnimCustomData::get(de->anim);
+ cust->destroy(de->anim);
+ rwFree(de);
+ }
+ rwFree(this);
+}
+
+void
+UVAnimDictionary::add(Animation *anim)
+{
+ UVAnimDictEntry *de = rwNewT(UVAnimDictEntry, 1, MEMDUR_EVENT | ID_UVANIMDICT);
+ de->anim = anim;
+ this->animations.append(&de->inDict);
+}
+
+UVAnimDictionary*
+UVAnimDictionary::streamRead(Stream *stream)
+{
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ UVAnimDictionary *dict = UVAnimDictionary::create();
+ if(dict == nil)
+ return nil;
+ int32 numAnims = stream->readI32();
+ Animation *anim;
+ for(int32 i = 0; i < numAnims; i++){
+ if(!findChunk(stream, ID_ANIMANIMATION, nil, nil)){
+ RWERROR((ERR_CHUNK, "ANIMANIMATION"));
+ goto fail;
+ }
+ anim = Animation::streamRead(stream);
+ if(anim == nil)
+ goto fail;
+ dict->add(anim);
+ }
+ return dict;
+fail:
+ dict->destroy();
+ return nil;
+}
+
+bool
+UVAnimDictionary::streamWrite(Stream *stream)
+{
+ uint32 size = this->streamGetSize();
+ writeChunkHeader(stream, ID_UVANIMDICT, size);
+ writeChunkHeader(stream, ID_STRUCT, 4);
+ int32 numAnims = this->count();
+ stream->writeI32(numAnims);
+ FORLIST(lnk, this->animations){
+ UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk);
+ de->anim->streamWrite(stream);
+ }
+ return true;
+}
+
+uint32
+UVAnimDictionary::streamGetSize(void)
+{
+ uint32 size = 12 + 4;
+ FORLIST(lnk, this->animations){
+ UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk);
+ size += 12 + de->anim->streamGetSize();
+ }
+ return size;
+}
+
+Animation*
+UVAnimDictionary::find(const char *name)
+{
+ FORLIST(lnk, this->animations){
+ Animation *anim = UVAnimDictEntry::fromDict(lnk)->anim;
+ UVAnimCustomData *custom = UVAnimCustomData::get(anim);
+ if(strncmp_ci(custom->name, name, 32) == 0)
+ return anim;
+ }
+ return nil;
+}
+
+static void
+uvAnimStreamRead(Stream *stream, Animation *anim)
+{
+ UVAnimCustomData *custom = UVAnimCustomData::get(anim);
+ UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes;
+ stream->readI32();
+ stream->read8(custom->name, 32);
+ stream->read32(custom->nodeToUVChannel, 8*4);
+ custom->refCount = 1;
+
+ for(int32 i = 0; i < anim->numFrames; i++){
+ frames[i].time = stream->readF32();
+ stream->read32(frames[i].uv, 6*4);
+ int32 prev = stream->readI32();
+ frames[i].prev = &frames[prev];
+ }
+}
+
+static void
+uvAnimStreamWrite(Stream *stream, Animation *anim)
+{
+ UVAnimCustomData *custom = UVAnimCustomData::get(anim);
+ UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes;
+ stream->writeI32(0);
+ stream->write8(custom->name, 32);
+ stream->write32(custom->nodeToUVChannel, 8*4);
+
+ for(int32 i = 0; i < anim->numFrames; i++){
+ stream->writeF32(frames[i].time);
+ stream->write32(frames[i].uv, 6*4);
+ stream->writeI32(frames[i].prev - frames);
+ }
+}
+
+static uint32
+uvAnimStreamGetSize(Animation *anim)
+{
+ return 4 + 32 + 8*4 + anim->numFrames*(4 + 6*4 + 4);
+}
+
+static void
+uvAnimLinearApplyCB(void *result, void *frame)
+{
+ Matrix *m = (Matrix*)result;
+ UVAnimInterpFrame *f = (UVAnimInterpFrame*)frame;
+ m->right.x = f->uv[0];
+ m->right.y = f->uv[1];
+ m->right.z = 0.0f;
+ m->up.x = f->uv[2];
+ m->up.y = f->uv[3];
+ m->up.z = 0.0f;
+ m->at.x = 0.0f;
+ m->at.y = 0.0f;
+ m->at.z = 0.0f;
+ m->pos.x = f->uv[4];
+ m->pos.y = f->uv[5];
+ m->pos.z = 0.0f;
+ m->update();
+}
+
+static void
+uvAnimLinearInterpCB(void *out, void *in1, void *in2, float32 t, void *custom)
+{
+ UVAnimInterpFrame *intf = (UVAnimInterpFrame*)out;
+ UVAnimKeyFrame *kf1 = (UVAnimKeyFrame*)in1;
+ UVAnimKeyFrame *kf2 = (UVAnimKeyFrame*)in2;
+ float32 f = (t - kf1->time) / (kf2->time - kf1->time);
+ intf->uv[0] = (kf2->uv[0] - kf1->uv[0])*f + kf1->uv[0];
+ intf->uv[1] = (kf2->uv[1] - kf1->uv[1])*f + kf1->uv[1];
+ intf->uv[2] = (kf2->uv[2] - kf1->uv[2])*f + kf1->uv[2];
+ intf->uv[3] = (kf2->uv[3] - kf1->uv[3])*f + kf1->uv[3];
+ intf->uv[4] = (kf2->uv[4] - kf1->uv[4])*f + kf1->uv[4];
+ intf->uv[5] = (kf2->uv[5] - kf1->uv[5])*f + kf1->uv[5];
+}
+
+static void
+uvAnimParamApplyCB(void *result, void *frame)
+{
+ Matrix *m = (Matrix*)result;
+ UVAnimInterpFrame *f = (UVAnimInterpFrame*)frame;
+ UVAnimParamData *p = (UVAnimParamData*)f->uv;
+
+ m->right.x = p->s0;
+ m->right.y = p->skew;
+ m->right.z = 0.0f;
+ m->up.x = 0.0f;
+ m->up.y = p->s1;
+ m->up.z = 0.0f;
+ m->at.x = 0.0f;
+ m->at.y = 0.0f;
+ m->at.z = 0.0f;
+ m->pos.x = p->x;
+ m->pos.y = p->y;
+ m->pos.z = 0.0f;
+ m->update();
+ static V3d xlat1 = { -0.5f, -0.5f, 0.0f };
+ static V3d xlat2 = { 0.5f, 0.5f, 0.0f };
+ static V3d axis = { 0.0f, 0.0f, 1.0f };
+ m->translate(&xlat1, COMBINEPOSTCONCAT);
+ m->rotate(&axis, p->theta*180.0f/(float)M_PI, COMBINEPOSTCONCAT);
+ m->translate(&xlat2, COMBINEPOSTCONCAT);
+}
+
+static void
+uvAnimParamInterpCB(void *out, void *in1, void *in2, float32 t, void *custom)
+{
+ UVAnimInterpFrame *intf = (UVAnimInterpFrame*)out;
+ UVAnimKeyFrame *kf1 = (UVAnimKeyFrame*)in1;
+ UVAnimKeyFrame *kf2 = (UVAnimKeyFrame*)in2;
+ float32 f = (t - kf1->time) / (kf2->time - kf1->time);
+
+ float32 a = kf2->uv[0] - kf1->uv[0];
+ while(a < (float)M_PI) a += 2 * (float)M_PI;
+ while(a > (float)M_PI) a -= 2 * (float)M_PI;
+ intf->uv[0] = a*f + kf1->uv[0];
+ intf->uv[1] = (kf2->uv[1] - kf1->uv[1])*f + kf1->uv[1];
+ intf->uv[2] = (kf2->uv[2] - kf1->uv[2])*f + kf1->uv[2];
+ intf->uv[3] = (kf2->uv[3] - kf1->uv[3])*f + kf1->uv[3];
+ intf->uv[4] = (kf2->uv[4] - kf1->uv[4])*f + kf1->uv[4];
+ intf->uv[5] = (kf2->uv[5] - kf1->uv[5])*f + kf1->uv[5];
+}
+
+
+static void
+registerUVAnimInterpolator(void)
+{
+ // TODO: implement this fully
+
+ // Linear
+ AnimInterpolatorInfo *info = rwNewT(AnimInterpolatorInfo, 1, MEMDUR_GLOBAL | ID_UVANIMATION);
+ info->id = 0x1C0;
+ info->interpKeyFrameSize = sizeof(UVAnimInterpFrame);
+ info->animKeyFrameSize = sizeof(UVAnimKeyFrame);
+ info->customDataSize = sizeof(UVAnimCustomData);
+ info->applyCB = uvAnimLinearApplyCB;
+ info->blendCB = nil;
+ info->interpCB = uvAnimLinearInterpCB;
+ info->addCB = nil;
+ info->mulRecipCB = nil;
+ info->streamRead = uvAnimStreamRead;
+ info->streamWrite = uvAnimStreamWrite;
+ info->streamGetSize = uvAnimStreamGetSize;
+ AnimInterpolatorInfo::registerInterp(info);
+
+ // Param
+ info = rwNewT(AnimInterpolatorInfo, 1, MEMDUR_GLOBAL | ID_UVANIMATION);
+ info->id = 0x1C1;
+ info->interpKeyFrameSize = sizeof(UVAnimInterpFrame);
+ info->animKeyFrameSize = sizeof(UVAnimKeyFrame);
+ info->customDataSize = sizeof(UVAnimCustomData);
+ info->applyCB = uvAnimParamApplyCB;
+ info->blendCB = nil;
+ info->interpCB = uvAnimParamInterpCB;
+ info->addCB = nil;
+ info->mulRecipCB = nil;
+ info->streamRead = uvAnimStreamRead;
+ info->streamWrite = uvAnimStreamWrite;
+ info->streamGetSize = uvAnimStreamGetSize;
+ AnimInterpolatorInfo::registerInterp(info);
+}
+
+int32 uvAnimOffset;
+
+static void*
+createUVAnim(void *object, int32 offset, int32)
+{
+ UVAnim *uvanim;
+ uvanim = PLUGINOFFSET(UVAnim, object, offset);
+ memset(uvanim, 0, sizeof(*uvanim));
+ return object;
+}
+
+static void*
+destroyUVAnim(void *object, int32 offset, int32)
+{
+ UVAnim *uvanim;
+ uvanim = PLUGINOFFSET(UVAnim, object, offset);
+ for(int32 i = 0; i < 8; i++){
+ AnimInterpolator *ip = uvanim->interp[i];
+ if(ip){
+ UVAnimCustomData *custom =
+ UVAnimCustomData::get(ip->currentAnim);
+ custom->destroy(ip->currentAnim);
+ rwFree(ip);
+ }
+ }
+ return object;
+}
+
+static void*
+copyUVAnim(void *dst, void *src, int32 offset, int32)
+{
+ UVAnim *srcuvanim, *dstuvanim;
+ dstuvanim = PLUGINOFFSET(UVAnim, dst, offset);
+ srcuvanim = PLUGINOFFSET(UVAnim, src, offset);
+ for(int32 i = 0; i < 8; i++){
+ AnimInterpolator *srcip = srcuvanim->interp[i];
+ AnimInterpolator *dstip;
+ if(srcip){
+ Animation *anim = srcip->currentAnim;
+ UVAnimCustomData *custom = UVAnimCustomData::get(anim);
+ dstip = AnimInterpolator::create(anim->getNumNodes(),
+ anim->interpInfo->interpKeyFrameSize);
+ dstip->setCurrentAnim(anim);
+ custom->refCount++;
+ dstuvanim->interp[i] = dstip;
+ }
+ }
+ return dst;
+}
+
+Animation*
+makeDummyAnimation(const char *name)
+{
+ AnimInterpolatorInfo *interpInfo = AnimInterpolatorInfo::find(0x1C0);
+ Animation *anim = Animation::create(interpInfo, 2, 0, 1.0f);
+ UVAnimCustomData *custom = UVAnimCustomData::get(anim);
+ strncpy(custom->name, name, 32);
+ memset(custom->nodeToUVChannel, 0, sizeof(custom->nodeToUVChannel));
+ custom->refCount = 1;
+ UVAnimKeyFrame *frames = (UVAnimKeyFrame*)anim->keyframes;
+ frames[0].time = 0.0;
+ frames[0].prev = nil;
+ frames[1].time = 1.0;
+ frames[1].prev = &frames[0];
+ return anim;
+}
+
+static Stream*
+readUVAnim(Stream *stream, int32, void *object, int32 offset, int32)
+{
+ UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset);
+ if(!findChunk(stream, ID_STRUCT, nil, nil)){
+ RWERROR((ERR_CHUNK, "STRUCT"));
+ return nil;
+ }
+ char name[32];
+ uint32 mask = stream->readI32();
+ uint32 bit = 1;
+ for(int32 i = 0; i < 8; i++){
+ if(mask & bit){
+ stream->read8(name, 32);
+ Animation *anim = nil;
+ if(currentUVAnimDictionary)
+ anim = currentUVAnimDictionary->find(name);
+ if(anim == nil){
+ anim = makeDummyAnimation(name);
+ if(currentUVAnimDictionary)
+ currentUVAnimDictionary->add(anim);
+ }
+ UVAnimCustomData *custom = UVAnimCustomData::get(anim);
+ AnimInterpolator *interp;
+ interp = AnimInterpolator::create(anim->getNumNodes(),
+ anim->interpInfo->interpKeyFrameSize);
+ interp->setCurrentAnim(anim);
+ custom->refCount++;
+ uvanim->interp[i] = interp;
+ }
+ bit <<= 1;
+ }
+ // TEMP
+ if(uvanim->uv[0] == nil)
+ uvanim->uv[0] = Matrix::create();
+ if(uvanim->uv[1] == nil)
+ uvanim->uv[1] = Matrix::create();
+ return stream;
+}
+
+static Stream*
+writeUVAnim(Stream *stream, int32 size, void *object, int32 offset, int32)
+{
+ UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset);
+ writeChunkHeader(stream, ID_STRUCT, size-12);
+ uint32 mask = 0;
+ uint32 bit = 1;
+ for(int32 i = 0; i < 8; i++){
+ if(uvanim->interp[i])
+ mask |= bit;
+ bit <<= 1;
+ }
+ stream->writeI32(mask);
+ for(int32 i = 0; i < 8; i++){
+ if(uvanim->interp[i]){
+ UVAnimCustomData *custom =
+ UVAnimCustomData::get(uvanim->interp[i]->currentAnim);
+ stream->write8(custom->name, 32);
+ }
+ }
+ return stream;
+}
+
+static int32
+getSizeUVAnim(void *object, int32 offset, int32)
+{
+ UVAnim *uvanim = PLUGINOFFSET(UVAnim, object, offset);
+ int32 size = 0;
+ for(int32 i = 0; i < 8; i++)
+ if(uvanim->interp[i])
+ size += 32;
+ return size ? size + 12 + 4 : 0;
+}
+
+static void*
+uvanimOpen(void *object, int32 offset, int32 size)
+{
+ registerUVAnimInterpolator();
+ return object;
+}
+static void *uvanimClose(void *object, int32 offset, int32 size) { return object; }
+
+void
+registerUVAnimPlugin(void)
+{
+ Engine::registerPlugin(0, ID_UVANIMATION, uvanimOpen, uvanimClose);
+ uvAnimOffset = Material::registerPlugin(sizeof(UVAnim), ID_UVANIMATION,
+ createUVAnim, destroyUVAnim, copyUVAnim);
+ Material::registerPluginStream(ID_UVANIMATION,
+ readUVAnim, writeUVAnim, getSizeUVAnim);
+}
+
+bool32
+UVAnim::exists(Material *mat)
+{
+ int32 i;
+ UVAnim *uvanim = PLUGINOFFSET(UVAnim, mat, uvAnimOffset);
+ for(i = 0; i < 8; i++)
+ if(uvanim->interp[i])
+ return 1;
+ return 0;
+}
+
+void
+UVAnim::addTime(Material *mat, float32 t)
+{
+ int32 i;
+ UVAnim *uvanim = PLUGINOFFSET(UVAnim, mat, uvAnimOffset);
+ for(i = 0; i < 8; i++)
+ if(uvanim->interp[i])
+ uvanim->interp[i]->addTime(t);
+}
+
+void
+UVAnim::applyUpdate(Material *mat)
+{
+ int32 i, j;
+ int32 uv;
+ Matrix m;
+ UVAnim *uvanim = PLUGINOFFSET(UVAnim, mat, uvAnimOffset);
+ for(i = 0; i < 2; i++)
+ if(uvanim->uv[i])
+ uvanim->uv[i]->setIdentity();
+ m.setIdentity();
+
+ for(i = 0; i < 8; i++){
+ AnimInterpolator *ip = uvanim->interp[i];
+ if(ip == nil)
+ continue;
+ UVAnimCustomData *custom = UVAnimCustomData::get(ip->currentAnim);
+ for(j = 0; j < ip->numNodes; j++){
+ InterpFrameHeader *f = ip->getInterpFrame(j);
+ uv = custom->nodeToUVChannel[j];
+ if(uv < 2 && uvanim->uv[uv]){
+ ip->applyCB(&m, f);
+ uvanim->uv[uv]->transform(&m, COMBINEPRECONCAT);
+ }
+ }
+ }
+ MatFX *mfx = MatFX::get(mat);
+ if(mfx) mfx->setUVTransformMatrices(uvanim->uv[0], uvanim->uv[1]);
+}
+
+}
diff --git a/src/vgafont.inc b/src/vgafont.inc
new file mode 100644
index 0000000..110d824
--- /dev/null
+++ b/src/vgafont.inc
@@ -0,0 +1,256 @@
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
diff --git a/src/world.cpp b/src/world.cpp
new file mode 100644
index 0000000..e80b8e3
--- /dev/null
+++ b/src/world.cpp
@@ -0,0 +1,238 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+
+#define PLUGIN_ID ID_WORLD
+
+namespace rw {
+
+int32 World::numAllocated = 0;
+
+PluginList World::s_plglist(sizeof(World));
+
+World*
+World::create(BBox *bbox)
+{
+ World *world = (World*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_WORLD);
+ if(world == nil){
+ RWERROR((ERR_ALLOC, s_plglist.size));
+ return nil;
+ }
+ numAllocated++;
+ world->object.init(World::ID, 0);
+ world->localLights.init();
+ world->globalLights.init();
+ world->clumps.init();
+ s_plglist.construct(world);
+ return world;
+}
+
+void
+World::destroy(void)
+{
+ s_plglist.destruct(this);
+ rwFree(this);
+ numAllocated--;
+}
+
+void
+World::addLight(Light *light)
+{
+ assert(light->world == nil);
+ light->world = this;
+ if(light->getType() < Light::POINT){
+ this->globalLights.append(&light->inWorld);
+ }else{
+ this->localLights.append(&light->inWorld);
+ if(light->getFrame())
+ light->getFrame()->updateObjects();
+ }
+}
+
+void
+World::removeLight(Light *light)
+{
+ assert(light->world == this);
+ light->inWorld.remove();
+ light->world = nil;
+}
+
+void
+World::addCamera(Camera *cam)
+{
+ assert(cam->world == nil);
+ cam->world = this;
+ if(cam->getFrame())
+ cam->getFrame()->updateObjects();
+}
+
+void
+World::removeCamera(Camera *cam)
+{
+ assert(cam->world == this);
+ cam->world = nil;
+}
+
+void
+World::addAtomic(Atomic *atomic)
+{
+ assert(atomic->world == nil);
+ atomic->world = this;
+ if(atomic->getFrame())
+ atomic->getFrame()->updateObjects();
+}
+
+void
+World::removeAtomic(Atomic *atomic)
+{
+ assert(atomic->world == this);
+ atomic->world = nil;
+}
+
+void
+World::addClump(Clump *clump)
+{
+ assert(clump->world == nil);
+ clump->world = this;
+ this->clumps.add(&clump->inWorld);
+ FORLIST(lnk, clump->atomics)
+ this->addAtomic(Atomic::fromClump(lnk));
+ FORLIST(lnk, clump->lights)
+ this->addLight(Light::fromClump(lnk));
+ FORLIST(lnk, clump->cameras)
+ this->addCamera(Camera::fromClump(lnk));
+
+ if(clump->getFrame()){
+ clump->getFrame()->matrix.optimize();
+ clump->getFrame()->updateObjects();
+ }
+}
+
+void
+World::removeClump(Clump *clump)
+{
+ assert(clump->world == this);
+ clump->inWorld.remove();
+ FORLIST(lnk, clump->atomics)
+ this->removeAtomic(Atomic::fromClump(lnk));
+ FORLIST(lnk, clump->lights)
+ this->removeLight(Light::fromClump(lnk));
+ FORLIST(lnk, clump->cameras)
+ this->removeCamera(Camera::fromClump(lnk));
+ clump->world = nil;
+}
+
+void
+World::render(void)
+{
+ // this is very wrong, we really want world sectors
+ FORLIST(lnk, this->clumps)
+ Clump::fromWorld(lnk)->render();
+}
+
+// Find lights that illuminate an atomic
+void
+World::enumerateLights(Atomic *atomic, WorldLights *lightData)
+{
+ int32 maxDirectionals, maxLocals;
+
+ maxDirectionals = lightData->numDirectionals;
+ maxLocals = lightData->numLocals;
+
+ lightData->numDirectionals = 0;
+ lightData->numLocals = 0;
+ lightData->numAmbients = 0;
+ lightData->ambient.red = 0.0f;
+ lightData->ambient.green = 0.0f;
+ lightData->ambient.blue = 0.0f;
+ lightData->ambient.alpha = 1.0f;
+
+ bool32 normals = atomic->geometry->flags & Geometry::NORMALS;
+
+ FORLIST(lnk, this->globalLights){
+ Light *l = Light::fromWorld(lnk);
+ if((l->getFlags() & Light::LIGHTATOMICS) == 0)
+ continue;
+ if(l->getType() == Light::AMBIENT){
+ lightData->ambient.red += l->color.red;
+ lightData->ambient.green += l->color.green;
+ lightData->ambient.blue += l->color.blue;
+ lightData->numAmbients++;
+ }else if(normals && l->getType() == Light::DIRECTIONAL){
+ if(lightData->numDirectionals < maxDirectionals)
+ lightData->directionals[lightData->numDirectionals++] = l;
+ }
+ }
+
+ if(atomic->world != this)
+ return;
+
+ if(!normals)
+ return;
+
+ // TODO: for this we would use an atomic's world sectors, but we don't have those yet
+ FORLIST(lnk, this->localLights){
+ if(lightData->numLocals >= maxLocals)
+ return;
+
+ Light *l = Light::fromWorld(lnk);
+ if((l->getFlags() & Light::LIGHTATOMICS) == 0)
+ continue;
+
+ // check if spheres are intersecting
+ Sphere *atomsphere = atomic->getWorldBoundingSphere();
+ V3d dist = sub(l->getFrame()->getLTM()->pos, atomsphere->center);
+ if(length(dist) < atomsphere->radius + l->radius)
+ lightData->locals[lightData->numLocals++] = l;
+ }
+}
+
+// Find all lights, for im3d lighting extension
+// missing flags and bounding spheres so more primitive than above
+void
+World::enumerateLights(WorldLights *lightData)
+{
+ int32 maxDirectionals, maxLocals;
+
+ maxDirectionals = lightData->numDirectionals;
+ maxLocals = lightData->numLocals;
+
+ lightData->numDirectionals = 0;
+ lightData->numLocals = 0;
+ lightData->numAmbients = 0;
+ lightData->ambient.red = 0.0f;
+ lightData->ambient.green = 0.0f;
+ lightData->ambient.blue = 0.0f;
+ lightData->ambient.alpha = 1.0f;
+
+ FORLIST(lnk, this->globalLights){
+ Light *l = Light::fromWorld(lnk);
+ if(l->getType() == Light::AMBIENT){
+ lightData->ambient.red += l->color.red;
+ lightData->ambient.green += l->color.green;
+ lightData->ambient.blue += l->color.blue;
+ lightData->numAmbients++;
+ }else if(l->getType() == Light::DIRECTIONAL){
+ if(lightData->numDirectionals < maxDirectionals)
+ lightData->directionals[lightData->numDirectionals++] = l;
+ }
+ }
+
+ FORLIST(lnk, this->localLights){
+ if(lightData->numLocals >= maxLocals)
+ return;
+
+ Light *l = Light::fromWorld(lnk);
+ lightData->locals[lightData->numLocals++] = l;
+ }
+}
+
+}