diff --git a/code/rd-rend2/CMakeLists.txt b/code/rd-rend2/CMakeLists.txt new file mode 100644 index 0000000000..09a12e2636 --- /dev/null +++ b/code/rd-rend2/CMakeLists.txt @@ -0,0 +1,235 @@ +# Make sure the user is not executing this script directly +if(NOT InOpenJK) + message(FATAL_ERROR "Use the top-level cmake script!") +endif(NOT InOpenJK) + +set(SPRend2IncludeDirectories + "${SharedDir}" + "${SharedDir}/rd-rend2" + "${SPDir}" + "${SPDir}/rd-rend2" + "${GSLIncludeDirectory}" + ) + +set(SPRend2Files + "${SharedDir}/rd-rend2/iqm.h" + "${SharedDir}/rd-rend2/glext.h" + "${SharedDir}/rd-rend2/qgl.h" + "${SharedDir}/rd-rend2/stb_image.h" + "${SPDir}/rd-rend2/G2_API.cpp" + "${SPDir}/rd-rend2/G2_bolts.cpp" + "${SPDir}/rd-rend2/G2_bones.cpp" + "${SharedDir}/rd-rend2/G2_gore_r2.cpp" + "${SharedDir}/rd-rend2/G2_gore_r2.h" + "${SPDir}/rd-rend2/G2_misc.cpp" + "${SPDir}/rd-rend2/G2_surfaces.cpp" + "${SharedDir}/rd-rend2/tr_allocator.cpp" + "${SharedDir}/rd-rend2/tr_allocator.h" + "${SharedDir}/rd-rend2/tr_animation.cpp" + "${SharedDir}/rd-rend2/tr_backend.cpp" + "${SharedDir}/rd-rend2/tr_bsp.cpp" + "${SharedDir}/rd-rend2/tr_cache.cpp" + "${SharedDir}/rd-rend2/tr_cache.h" + "${SharedDir}/rd-rend2/tr_cmds.cpp" + "${SharedDir}/rd-rend2/tr_curve.cpp" + "${SharedDir}/rd-rend2/tr_decals.cpp" + "${SPDir}/rd-rend2/tr_draw.cpp" + "${SharedDir}/rd-rend2/tr_extensions.cpp" + "${SharedDir}/rd-rend2/tr_extramath.cpp" + "${SharedDir}/rd-rend2/tr_extramath.h" + "${SharedDir}/rd-rend2/tr_extratypes.h" + "${SharedDir}/rd-rend2/tr_fbo.cpp" + "${SharedDir}/rd-rend2/tr_fbo.h" + "${SharedDir}/rd-rend2/tr_flares.cpp" + "${SharedDir}/rd-rend2/tr_ghoul2.cpp" + "${SharedDir}/rd-rend2/tr_glsl.cpp" + "${SharedDir}/rd-rend2/tr_glsl_parse.cpp" + "${SharedDir}/rd-rend2/tr_image.cpp" + "${SharedDir}/rd-rend2/tr_image_stb.cpp" + "${SPDir}/rd-rend2/tr_init.cpp" + "${SharedDir}/rd-rend2/tr_light.cpp" + "${SharedDir}/rd-rend2/tr_local.h" + "${SharedDir}/rd-rend2/tr_main.cpp" + "${SharedDir}/rd-rend2/tr_marks.cpp" + "${SharedDir}/rd-rend2/tr_mesh.cpp" + "${SharedDir}/rd-rend2/tr_model.cpp" + "${SharedDir}/rd-rend2/tr_model_iqm.cpp" + "${SharedDir}/rd-rend2/tr_postprocess.cpp" + "${SharedDir}/rd-rend2/tr_postprocess.h" + "${SharedDir}/rd-rend2/tr_scene.cpp" + "${SharedDir}/rd-rend2/tr_shade.cpp" + "${SharedDir}/rd-rend2/tr_shade_calc.cpp" + "${SharedDir}/rd-rend2/tr_shader.cpp" + "${SharedDir}/rd-rend2/tr_shadows.cpp" + "${SharedDir}/rd-rend2/tr_skin.cpp" + "${SharedDir}/rd-rend2/tr_sky.cpp" + "${SharedDir}/rd-rend2/tr_subs.cpp" + "${SharedDir}/rd-rend2/tr_surface.cpp" + "${SharedDir}/rd-rend2/tr_tangentspace.cpp" + "${SharedDir}/rd-rend2/tr_vbo.cpp" + "${SharedDir}/rd-rend2/tr_world.cpp" + "${SharedDir}/rd-rend2/tr_weather.cpp" + "${SharedDir}/rd-rend2/tr_weather.h") +source_group("renderer" FILES ${SPRend2Files}) + +file(GLOB SPRend2GLSLFiles "${SharedDir}/rd-rend2/glsl/*.glsl") +source_group("renderer\\glsl" FILES ${SPRend2GLSLFiles}) +set(SPRend2Files ${SPRend2Files} ${SPRend2GLSLFiles}) + +set(SPRend2MikktFiles + "${SharedDir}/rd-rend2/MikkTSpace/mikktspace.h" + "${SharedDir}/rd-rend2/MikkTSpace/mikktspace.c") +source_group("MikktSpace" FILES ${SPRend2MikktFiles}) +set(SPRend2Files ${SPRend2Files} ${SPRend2MikktFiles}) + +set(SPRend2Ghoul2Files + "${SPDir}/ghoul2/G2.h" + "${SPDir}/ghoul2/ghoul2_gore.h") +source_group("ghoul2" FILES ${SPRend2Ghoul2Files}) +set(SPRend2Files ${SPRend2Files} ${SPRend2Ghoul2Files}) + +set(SPRend2RdCommonFiles + "${SPDir}/rd-common/mdx_format.h" + "${SPDir}/rd-common/tr_common.h" + "${SPDir}/rd-common/tr_font.cpp" + "${SPDir}/rd-common/tr_font.h" + "${SPDir}/rd-common/tr_image_load.cpp" + "${SPDir}/rd-common/tr_image_jpg.cpp" + "${SPDir}/rd-common/tr_image_tga.cpp" + "${SPDir}/rd-common/tr_image_png.cpp" + "${SPDir}/rd-common/tr_noise.cpp" + "${SPDir}/rd-common/tr_public.h" + "${SPDir}/rd-common/tr_types.h") +source_group("rd-common" FILES ${SPRend2RdCommonFiles}) +set(SPRend2Files ${SPRend2Files} ${SPRend2RdCommonFiles}) + +set(SPRend2CommonFiles + "${SPDir}/qcommon/matcomp.cpp" + "${SPDir}/qcommon/q_shared.cpp" + "${SharedCommonFiles}") +source_group("common" FILES ${SPRend2CommonFiles}) +set(SPRend2Files ${SPRend2Files} ${SPRend2CommonFiles}) + +set(SPRend2CommonSafeFiles + ${SharedCommonSafeFiles} + ) +source_group("common/safe" FILES ${SPRend2CommonSafeFiles}) +set(SPRend2Files ${SPRend2Files} ${SPRend2CommonSafeFiles}) + +# Transparently use either bundled or system libjpeg. +list(APPEND SPRend2IncludeDirectories ${JPEG_INCLUDE_DIR}) +list(APPEND SPRend2Libraries ${JPEG_LIBRARIES}) + +# Transparently use either bundled or system libpng. Order is important -- +# libpng used zlib, so it must come before it on the linker command line. +list(APPEND SPRend2IncludeDirectories ${PNG_INCLUDE_DIRS}) +list(APPEND SPRend2Libraries ${PNG_LIBRARIES}) + +# Transparently use either bundled or system zlib. +list(APPEND SPRend2IncludeDirectories ${ZLIB_INCLUDE_DIR}) +list(APPEND SPRend2Libraries ${ZLIB_LIBRARIES}) + +# Transparently use our bundled minizip. +list(APPEND SPRend2IncludeDirectories ${MINIZIP_INCLUDE_DIRS}) +list(APPEND SPRend2Libraries ${MINIZIP_LIBRARIES}) + +find_package(OpenGL REQUIRED) +set(SPRend2IncludeDirectories ${SPRend2IncludeDirectories} ${OPENGL_INCLUDE_DIR}) +set(SPRend2Libraries ${SPRend2Libraries} ${OPENGL_LIBRARIES}) + +source_group("renderer" + FILES + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.h + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.cpp) +set(SPRend2Files + ${SPRend2Files} + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.h + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.cpp) + +set(SPRend2IncludeDirectories ${SPRend2IncludeDirectories} ${OpenJKLibDir}) + +function(add_sp_renderer_project ProjectName Label EngineName InstallDir Component) + add_library(${ProjectName} SHARED ${SPRend2Files}) + if(NOT MSVC) + # remove "lib" prefix for .so/.dylib files + set_target_properties(${ProjectName} PROPERTIES PREFIX "") + endif() + + if(WIN32) + install(TARGETS ${ProjectName} + RUNTIME + DESTINATION ${InstallDir} + COMPONENT ${Component}) + else(WIN32) + if(MakeApplicationBundles AND (BuildSPEngine OR BuildJK2SPEngine)) + install(TARGETS ${ProjectName} + LIBRARY + DESTINATION "${InstallDir}/${EngineName}.app/Contents/MacOS/" + COMPONENT ${Component}) + else() + install(TARGETS ${ProjectName} + LIBRARY + DESTINATION ${InstallDir} + COMPONENT ${Component}) + endif() + endif() + + set_target_properties(${ProjectName} PROPERTIES COMPILE_DEFINITIONS "${SPRend2Defines}") + + # Hide symbols not explicitly marked public. + set_property(TARGET ${ProjectName} APPEND PROPERTY COMPILE_OPTIONS ${OPENJK_VISIBILITY_FLAGS}) + + set_target_properties(${ProjectName} PROPERTIES INCLUDE_DIRECTORIES "${SPRend2IncludeDirectories}") + set_target_properties(${ProjectName} PROPERTIES PROJECT_LABEL ${Label}) + + target_link_libraries(${ProjectName} ${SPRend2Libraries}) + target_include_directories(${ProjectName} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +endfunction(add_sp_renderer_project) + +# Defines +set(SPRend2Defines ${SharedDefines} "RENDERER" "REND2_SP" "REND2_SP_GORE") + +if(BuildSPRend2) + add_sp_renderer_project(${SPRend2} "SP Rend2 Renderer" ${SPEngine} ${JKAInstallDir} ${JKASPClientComponent}) +endif(BuildSPRend2) +if(BuildJK2SPRdRend2) + set(SPRend2Defines ${SPRend2Defines} "JK2_MODE") + add_sp_renderer_project(${JK2SPRend2Renderer} "JK2 SP Rend2 Renderer" ${JK2SPEngine} ${JK2InstallDir} ${JK2SPClientComponent}) +endif(BuildJK2SPRdRend2) + +# GLSL shader file generator +add_executable(compact_glsl_sp + ${SharedDir}/rd-rend2/glsl/compact.cpp + ${SharedDir}/rd-rend2/tr_allocator.cpp + ${SharedDir}/rd-rend2/tr_allocator.h + ${SharedDir}/rd-rend2/tr_glsl_parse.cpp) +target_compile_definitions(compact_glsl_sp PRIVATE "GLSL_BUILDTOOL" "NOMINMAX" "REND2_SP") +if (NOT WIN32 AND NOT APPLE) + target_compile_definitions(compact_glsl_sp PRIVATE "ARCH_STRING=\"${Architecture}\"") +endif() +target_include_directories(compact_glsl_sp PRIVATE "${SPRend2IncludeDirectories}") +if (WIN32 OR APPLE) +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.cpp + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.h + COMMAND + compact_glsl_sp + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.cpp + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.h + ${SPRend2GLSLFiles} + DEPENDS + compact_glsl_sp ${SPRend2GLSLFiles}) +else() +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.cpp + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.h + COMMAND + ${CMAKE_BINARY_DIR}/compact_glsl_sp + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.cpp + ${CMAKE_CURRENT_BINARY_DIR}/glsl_shaders.h + ${SPRend2GLSLFiles} + DEPENDS + compact_glsl_sp ${SPRend2GLSLFiles}) +endif() diff --git a/code/rd-rend2/G2_API.cpp b/code/rd-rend2/G2_API.cpp new file mode 100644 index 0000000000..9f4d7bd3fa --- /dev/null +++ b/code/rd-rend2/G2_API.cpp @@ -0,0 +1,2546 @@ +#include "server/exe_headers.h" + +#include "ghoul2/G2.h" +#include "ghoul2/ghoul2_gore.h" // ragdoll +#include "qcommon/q_shared.h" +#include "qcommon/MiniHeap.h" +#include "rd-common/tr_common.h" +#include "tr_local.h" +#include + +#ifdef _MSC_VER + #pragma warning (push, 3) //go back down to 3 for the stl include +#endif +#include +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +extern mdxaBone_t worldMatrix; +extern mdxaBone_t worldMatrixInv; + +#define NUM_G2T_TIME (2) +#ifndef REND2_SP +// must be a power of two +#define MAX_G2_MODELS (1024) +#define G2_MODEL_BITS (10) +#else +#define MAX_G2_MODELS (512) +#define G2_MODEL_BITS (9) +#endif +#define G2_INDEX_MASK (MAX_G2_MODELS-1) +// rww - RAGDOLL_BEGIN +#define GHOUL2_RAG_STARTED 0x0010 +#define GHOUL2_RAG_FORCESOLVE 0x1000 //api-override, determine if ragdoll should be forced to continue solving even if it thinks it is settled +// rww - RAGDOLL_END + +#if G2API_DEBUG + +#define G2ERROR(exp,m) (void)( (exp) || (G2APIError().Error(m,0,__FILE__,__LINE__), 0) ) +#define G2WARNING(exp,m) (void)( (exp) || (G2APIError().Error(m,1,__FILE__,__LINE__), 0) ) +#define G2NOTE(exp,m) (void)( (exp) || (G2APIError().Error(m,2,__FILE__,__LINE__), 0) ) +#define G2ANIM(ghlInfo,m) (void)((G2APIError().AnimTest(ghlInfo,m,__FILE__,__LINE__), 0) ) + +#else + +#define G2ERROR(exp,m) ((void)0) +#define G2WARNING(exp,m) ((void)0) +#define G2NOTE(exp,m) ((void)0) +#define G2ANIM(ghlInfo,m) ((void)0) + +#endif + +// get/set time +static int G2TimeBases[NUM_G2T_TIME]; + +// used in tr_init.cpp +qboolean gG2_GBMNoReconstruct; +qboolean gG2_GBMUseSPMethod; + +// Local functions pre-definition +qboolean G2_TestModelPointers(CGhoul2Info* ghlInfo); +void CL_InitRef(void); + +#ifdef _FULL_G2_LEAK_CHECKING +int g_Ghoul2Allocations = 0; +int g_G2ServerAlloc = 0; +int g_G2ClientAlloc = 0; +int g_G2AllocServer = 0; + +//stupid debug crap to track leaks in case they happen. +//we used to shove everything into a map and delete it all and not care about +//leaks, but that was not the Right Thing. -rww +#define MAX_TRACKED_ALLOC 4096 +static bool g_G2AllocTrackInit = false; //want to keep this thing contained +static CGhoul2Info_v *g_G2AllocTrack[MAX_TRACKED_ALLOC]; + +void G2_DEBUG_InitPtrTracker(void) +{ + memset(g_G2AllocTrack, 0, sizeof(g_G2AllocTrack)); + g_G2AllocTrackInit = true; +} + +void G2_DEBUG_ReportLeaks(void) +{ + int i = 0; + + if (!g_G2AllocTrackInit) + { + Com_Printf("g2 leak tracker was never initialized!\n"); + return; + } + + while (i < MAX_TRACKED_ALLOC) + { + if (g_G2AllocTrack[i]) + { + Com_Printf("Bad guy found in slot %i, attempting to access...", i); + CGhoul2Info_v &g2v = *g_G2AllocTrack[i]; + CGhoul2Info &g2 = g2v[0]; + + if (g2v.IsValid() && g2.mFileName && g2.mFileName[0]) + { + Com_Printf("Bad guy's filename is %s\n", g2.mFileName); + } + else + { + Com_Printf("He's not valid! BURN HIM!\n"); + } + } + i++; + } +} + +void G2_DEBUG_ShovePtrInTracker(CGhoul2Info_v *g2) +{ + int i = 0; + + if (!g_G2AllocTrackInit) + { + G2_DEBUG_InitPtrTracker(); + } + + if (!g_G2AllocTrackInit) + { + G2_DEBUG_InitPtrTracker(); + } + + while (i < MAX_TRACKED_ALLOC) + { + if (!g_G2AllocTrack[i]) + { + g_G2AllocTrack[i] = g2; + return; + } + i++; + } + + CGhoul2Info_v &g2v = *g2; + + if (g2v[0].currentModel && g2v[0].currentModel->name && g2v[0].currentModel->name[0]) + { + Com_Printf("%s could not be fit into g2 debug instance tracker.\n", g2v[0].currentModel->name); + } + else + { + Com_Printf("Crap g2 instance passed to instance tracker (in).\n"); + } +} + +void G2_DEBUG_RemovePtrFromTracker(CGhoul2Info_v *g2) +{ + int i = 0; + + if (!g_G2AllocTrackInit) + { + G2_DEBUG_InitPtrTracker(); + } + + while (i < MAX_TRACKED_ALLOC) + { + if (g_G2AllocTrack[i] == g2) + { + g_G2AllocTrack[i] = NULL; + return; + } + i++; + } + + CGhoul2Info_v &g2v = *g2; + + if (g2v[0].currentModel && g2v[0].currentModel->name && g2v[0].currentModel->name[0]) + { + Com_Printf("%s not in g2 debug instance tracker.\n", g2v[0].currentModel->name); + } + else + { + Com_Printf("Crap g2 instance passed to instance tracker (out).\n"); + } +} +#endif + +// RAGDOLL_BEGIN +void G2API_SetTime(int currentTime,int clock) +{ + assert(clock>=0&&clockG2TimeBases[0]+200) + { + G2TimeBases[1]=0; // use server time instead + return; + } +#if G2_DEBUG_TIME + Com_Printf(" after c%6d s%6d\n",G2TimeBases[1],G2TimeBases[0]); +#endif +} + +int G2API_GetTime(int argTime) // this may or may not return arg depending on ghoul2_time cvar +{ + int ret=G2TimeBases[1]; + if ( !ret ) + { + ret = G2TimeBases[0]; + } + + return ret; +} +//rww - RAGDOLL_END + +void G2API_AttachInstanceToEntNum(CGhoul2Info_v &ghoul2, int entityNum, qboolean server) +{ //Assign the pointers in the arrays +#ifdef _G2_LISTEN_SERVER_OPT + if (server) + { + ghoul2[0].entityNum = entityNum; + } + else + { + g2ClientAttachments[entityNum] = &ghoul2; + } +#endif +} + +void G2API_ClearAttachedInstance(int entityNum) +{ +#ifdef _G2_LISTEN_SERVER_OPT + g2ClientAttachments[entityNum] = NULL; +#endif +} + +void G2API_CleanEntAttachments(void) +{ +#ifdef _G2_LISTEN_SERVER_OPT + int i = 0; + + while (i < MAX_GENTITIES) + { + g2ClientAttachments[i] = NULL; + i++; + } +#endif +} + +#ifdef _G2_LISTEN_SERVER_OPT +void CopyBoneCache(CBoneCache *to, CBoneCache *from); +#endif + +qboolean G2API_OverrideServerWithClientData(CGhoul2Info_v& ghoul2, int modelIndex) +{ +#ifndef _G2_LISTEN_SERVER_OPT + return qfalse; +#else + CGhoul2Info *serverInstance = &ghoul2[modelIndex]; + CGhoul2Info *clientInstance; + + if (ri.Cvar_VariableIntegerValue( "dedicated" )) + { //No client to get from! + return qfalse; + } + + if (!g2ClientAttachments[serverInstance->entityNum]) + { //No clientside instance is attached to this entity + return qfalse; + } + + CGhoul2Info_v &g2Ref = *g2ClientAttachments[serverInstance->entityNum]; + clientInstance = &g2Ref[0]; + + int frameNum = G2API_GetTime(0); + + if (clientInstance->mSkelFrameNum != frameNum) + { //it has to be constructed already + return qfalse; + } + + if (!clientInstance->mBoneCache) + { //that just won't do + return qfalse; + } + + //Just copy over the essentials + serverInstance->aHeader = clientInstance->aHeader; + serverInstance->animModel = clientInstance->animModel; + serverInstance->currentAnimModelSize = clientInstance->currentAnimModelSize; + serverInstance->currentModel = clientInstance->currentModel; + serverInstance->currentModelSize = clientInstance->currentModelSize; + serverInstance->mAnimFrameDefault = clientInstance->mAnimFrameDefault; + serverInstance->mModel = clientInstance->mModel; + serverInstance->mModelindex = clientInstance->mModelindex; + serverInstance->mSurfaceRoot = clientInstance->mSurfaceRoot; + serverInstance->mTransformedVertsArray = clientInstance->mTransformedVertsArray; + + if (!serverInstance->mBoneCache) + { //if this is the case.. I guess we can use the client one instead + serverInstance->mBoneCache = clientInstance->mBoneCache; + } + + //Copy the contents of the client cache over the contents of the server cache + if (serverInstance->mBoneCache != clientInstance->mBoneCache) + { + CopyBoneCache(serverInstance->mBoneCache, clientInstance->mBoneCache); + } + + serverInstance->mSkelFrameNum = clientInstance->mSkelFrameNum; + return qtrue; +#endif +} + +void RemoveBoneCache(CBoneCache* boneCache); + +static size_t GetSizeOfGhoul2Info ( const CGhoul2Info& g2Info ) +{ + size_t size = 0; + + // This is pretty ugly, but we don't want to save everything in the CGhoul2Info object. + size += offsetof (CGhoul2Info, mTransformedVertsArray) - offsetof (CGhoul2Info, mModelindex); + + // Surface vector + size + size += sizeof (int); + size += g2Info.mSlist.size() * sizeof (surfaceInfo_t); + + // Bone vector + size + size += sizeof (int); + size += g2Info.mBlist.size() * sizeof (boneInfo_t); + + // Bolt vector + size + size += sizeof (int); + size += g2Info.mBltlist.size() * sizeof (boltInfo_t); + + return size; +} + +static size_t SerializeGhoul2Info ( char *buffer, const CGhoul2Info& g2Info ) +{ + char *base = buffer; + size_t blockSize; + + // Oh the ugliness... + blockSize = offsetof (CGhoul2Info, mTransformedVertsArray) - offsetof (CGhoul2Info, mModelindex); + memcpy (buffer, &g2Info.mModelindex, blockSize); + buffer += blockSize; + + // Surfaces vector + size + *(int *)buffer = g2Info.mSlist.size(); + buffer += sizeof (int); + + blockSize = g2Info.mSlist.size() * sizeof (surfaceInfo_t); + memcpy (buffer, g2Info.mSlist.data(), g2Info.mSlist.size() * sizeof (surfaceInfo_t)); + buffer += blockSize; + + // Bones vector + size + *(int *)buffer = g2Info.mBlist.size(); + buffer += sizeof (int); + + blockSize = g2Info.mBlist.size() * sizeof (boneInfo_t); + memcpy (buffer, g2Info.mBlist.data(), g2Info.mBlist.size() * sizeof (boneInfo_t)); + buffer += blockSize; + + // Bolts vector + size + *(int *)buffer = g2Info.mBltlist.size(); + buffer += sizeof (int); + + blockSize = g2Info.mBltlist.size() * sizeof (boltInfo_t); + memcpy (buffer, g2Info.mBltlist.data(), g2Info.mBltlist.size() * sizeof (boltInfo_t)); + buffer += blockSize; + + return static_cast(buffer - base); +} + +static size_t DeserializeGhoul2Info ( const char *buffer, CGhoul2Info& g2Info ) +{ + const char *base = buffer; + size_t size; + + size = offsetof (CGhoul2Info, mTransformedVertsArray) - offsetof (CGhoul2Info, mModelindex); + memcpy (&g2Info.mModelindex, buffer, size); + buffer += size; + + // Surfaces vector + size = *(int *)buffer; + buffer += sizeof (int); + + g2Info.mSlist.assign ((surfaceInfo_t *)buffer, (surfaceInfo_t *)buffer + size); + buffer += sizeof (surfaceInfo_t) * size; + + // Bones vector + size = *(int *)buffer; + buffer += sizeof (int); + + g2Info.mBlist.assign ((boneInfo_t *)buffer, (boneInfo_t *)buffer + size); + buffer += sizeof (boneInfo_t) * size; + + // Bolt vector + size = *(int *)buffer; + buffer += sizeof (int); + + g2Info.mBltlist.assign ((boltInfo_t *)buffer, (boltInfo_t *)buffer + size); + buffer += sizeof (boltInfo_t) * size; + + return static_cast(buffer - base); +} + +class Ghoul2InfoArray : public IGhoul2InfoArray +{ + std::vector mInfos[MAX_G2_MODELS]; + int mIds[MAX_G2_MODELS]; + std::list mFreeIndecies; + void DeleteLow(int idx) + { + for (size_t model=0; model< mInfos[idx].size(); model++) + { + if (mInfos[idx][model].mBoneCache) + { + RemoveBoneCache(mInfos[idx][model].mBoneCache); + mInfos[idx][model].mBoneCache=0; + } + } + + mInfos[idx].clear(); + + if ((mIds[idx]>>G2_MODEL_BITS)>(1<<(31-G2_MODEL_BITS))) + { + mIds[idx]=MAX_G2_MODELS+idx; //rollover reset id to minimum value + mFreeIndecies.push_back(idx); + } + else + { + mIds[idx]+=MAX_G2_MODELS; + mFreeIndecies.push_front(idx); + } + } +public: + Ghoul2InfoArray() + { + int i; + for (i=0;i(buffer - base); + } + + size_t Deserialize ( const char *buffer, size_t size ) + { + const char *base = buffer; + size_t count; + + // Free indices + count = *(int *)buffer; + buffer += sizeof (int); + + mFreeIndecies.assign ((int *)buffer, (int *)buffer + count); + buffer += sizeof (int) * count; + + // IDs + memcpy (mIds, buffer, sizeof (mIds)); + buffer += sizeof (mIds); + + // Ghoul2 infos + for ( size_t i = 0; i < MAX_G2_MODELS; i++ ) + { + mInfos[i].clear(); + + count = *(int *)buffer; + buffer += sizeof (int); + + mInfos[i].resize (count); + + for ( size_t j = 0; j < count; j++ ) + { + buffer += DeserializeGhoul2Info (buffer, mInfos[i][j]); + } + } + + return static_cast(buffer - base); + } + + ~Ghoul2InfoArray() + { + if (mFreeIndecies.size() < MAX_G2_MODELS) + { +#if G2API_DEBUG + char mess[1000]; + sprintf(mess, "************************\nLeaked %ld ghoul2info slots\n", MAX_G2_MODELS - mFreeIndecies.size()); + Com_DPrintf(mess); +#endif + for (int i = 0; i < MAX_G2_MODELS; i++) + { + std::list::iterator j; + for (j = mFreeIndecies.begin(); j != mFreeIndecies.end(); ++j) + { + if (*j == i) + break; + } + if (j == mFreeIndecies.end()) + { +#if G2API_DEBUG + sprintf(mess, "Leaked Info idx=%d id=%d sz=%ld\n", i, mIds[i], mInfos[i].size()); + Com_DPrintf(mess); + if (mInfos[i].size()) + { + sprintf(mess, "%s\n", mInfos[i][0].mFileName); + Com_DPrintf(mess); + } +#endif + DeleteLow(i); + } + } + } +#if G2API_DEBUG + else + { + Com_DPrintf("No ghoul2 info slots leaked\n"); + } +#endif + } + + int New() + { + if (mFreeIndecies.empty()) + { + assert(0); + Com_Error(ERR_FATAL, "Out of ghoul2 info slots"); + + } + // gonna pull from the front, doing a + int idx=*mFreeIndecies.begin(); + mFreeIndecies.erase(mFreeIndecies.begin()); + return mIds[idx]; + } + bool IsValid(int handle) const + { + if ( handle <= 0 ) + { + return false; + } + assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK) 0); //null handle + assert((handle & G2_INDEX_MASK) >= 0 && (handle & G2_INDEX_MASK) < MAX_G2_MODELS); //junk handle + assert(mIds[handle & G2_INDEX_MASK] == handle); // not a valid handle, could be old or garbage + if (mIds[handle & G2_INDEX_MASK] == handle) + { + DeleteLow(handle&G2_INDEX_MASK); + } + } + std::vector &Get(int handle) + { + assert(handle>0); //null handle + assert((handle&G2_INDEX_MASK)>=0&&(handle&G2_INDEX_MASK)=MAX_G2_MODELS||mIds[handle&G2_INDEX_MASK]!=handle)); + + return mInfos[handle&G2_INDEX_MASK]; + } + const std::vector &Get(int handle) const + { + assert(handle>0); + assert(mIds[handle&G2_INDEX_MASK]==handle); // not a valid handle, could be old or garbage + return mInfos[handle&G2_INDEX_MASK]; + } + +#if G2API_DEBUG + std::vector& GetDebug(int handle) + { + assert(!(handle <= 0 || (handle & G2_INDEX_MASK) < 0 || (handle & G2_INDEX_MASK) >= MAX_G2_MODELS || mIds[handle & G2_INDEX_MASK] != handle)); + + return mInfos[handle & G2_INDEX_MASK]; + } + void TestAllAnims() + { + int j; + for (j=0;j &ghoul2=mInfos[j]; + int i; + for (i=0; i& DebugG2Info(int handle) +{ + return ((Ghoul2InfoArray*)(&TheGhoul2InfoArray()))->GetDebug(handle); +} + +CGhoul2Info& DebugG2InfoI(int handle, int item) +{ + return ((Ghoul2InfoArray*)(&TheGhoul2InfoArray()))->GetDebug(handle)[item]; +} + +void TestAllGhoul2Anims() +{ + ((Ghoul2InfoArray*)(&TheGhoul2InfoArray()))->TestAllAnims(); +} +#endif + +#define PERSISTENT_G2DATA "g2infoarray" + +void RestoreGhoul2InfoArray() +{ + if (singleton == NULL) + { + // Create the ghoul2 info array + TheGhoul2InfoArray(); + + size_t size; + const void *data = ri.PD_Load (PERSISTENT_G2DATA, &size); + if ( data == NULL ) + { + return; + } + +#ifdef _DEBUG + size_t read = +#endif // _DEBUG + singleton->Deserialize((const char*)data, size); + Z_Free((void*)data); +#ifdef _DEBUG + assert(read == size); +#endif + } +} + +void SaveGhoul2InfoArray() +{ + size_t size = singleton->GetSerializedSize(); + void *data = R_Malloc (size, TAG_GHOUL2, qfalse); + size_t written = singleton->Serialize ((char *)data); +#ifdef _DEBUG + assert(written == size); +#endif // _DEBUG + + if ( !ri.PD_Store (PERSISTENT_G2DATA, data, size) ) + { + Com_Printf (S_COLOR_RED "ERROR: Failed to store persistent renderer data.\n"); + } +} + +void Ghoul2InfoArray_Free(void) +{ + if(singleton) { + delete singleton; + singleton = NULL; + } +} + +// this is the ONLY function to read entity states directly +void G2API_CleanGhoul2Models(CGhoul2Info_v& ghoul2) +{ +#ifdef _G2_GORE + G2API_ClearSkinGore(ghoul2); +#endif + ghoul2.~CGhoul2Info_v(); +} + +qhandle_t G2API_PrecacheGhoul2Model(const char *fileName) +{ + return RE_RegisterModel(fileName); +} + +// initialise all that needs to be on a new Ghoul II model +int G2API_InitGhoul2Model(CGhoul2Info_v& ghoul2, const char* fileName, int modelIndex, qhandle_t customSkin, qhandle_t customShader, int modelFlags, int lodBias) +{ + int model = -1; + + G2ERROR(fileName && fileName[0], "NULL filename"); + + // are we actually asking for a model to be loaded. + if (!fileName || !fileName[0]) + { + assert(0); + return -1; + } + + // find a free spot in the list + for (model=0; model< ghoul2.size(); model++) + { + if (ghoul2[model].mModelindex == -1) + { + ghoul2[model]=CGhoul2Info(); + break; + } + } + if (model == ghoul2.size()) + { + assert(model < 8); //arb, just catching run-away models (why 4 instead of 8 in MP?) + CGhoul2Info info; + Q_strncpyz(info.mFileName, fileName, sizeof(info.mFileName)); + info.mModelindex = 0; + + if (G2_TestModelPointers(&info)) + { + ghoul2.push_back(CGhoul2Info()); + } + else + { + return -1; + } + } + + Q_strncpyz(ghoul2[model].mFileName, fileName, sizeof(ghoul2[model].mFileName)); + + ghoul2[model].mModelindex = model; + if (!G2_TestModelPointers(&ghoul2[model])) + { + ghoul2[model].mFileName[0]=0; + ghoul2[model].mModelindex = -1; + } + else + { + G2_Init_Bone_List(ghoul2[model].mBlist, ghoul2[model].aHeader->numBones); + G2_Init_Bolt_List(ghoul2[model].mBltlist); + ghoul2[model].mCustomShader = customShader; + ghoul2[model].mCustomSkin = customSkin; + ghoul2[model].mLodBias = lodBias; + ghoul2[model].mAnimFrameDefault = 0; + ghoul2[model].mFlags = 0; + + ghoul2[model].mModelBoltLink = -1; + } + return ghoul2[model].mModelindex; +} + +qboolean G2API_SetLodBias(CGhoul2Info *ghlInfo, int lodBias) +{ + G2ERROR(ghlInfo, "G2API_SetLodBias: NULL ghlInfo"); + if (G2_SetupModelPointers(ghlInfo)) + { + ghlInfo->mLodBias = lodBias; + return qtrue; + } + return qfalse; +} + +qboolean G2API_SetSkin(CGhoul2Info* ghlInfo, qhandle_t customSkin, qhandle_t renderSkin) +{ + G2ERROR(ghlInfo, "G2API_SetSkin: NULL ghlInfo"); + if (G2_SetupModelPointers(ghlInfo)) + { + ghlInfo->mCustomSkin = customSkin; +#ifndef JK2_MODE + if (renderSkin) + { + // this is going to set the surfs on/off matching the skin file + // header moved to tr_local.h, implemented in G2_surface.cpp + G2_SetSurfaceOnOffFromSkin( ghlInfo, renderSkin ); + } +#endif + return qtrue; + } + return qfalse; +} + +qboolean G2API_SetShader(CGhoul2Info *ghlInfo, qhandle_t customShader) +{ + G2ERROR(ghlInfo, "G2API_SetShader: NULL ghlInfo"); + if (ghlInfo) + { + ghlInfo->mCustomShader = customShader; + return qtrue; + } + return qfalse; +} + +qboolean G2API_SetSurfaceOnOff(CGhoul2Info* ghlInfo, const char* surfaceName, const int flags) +{ + G2ERROR(ghlInfo, "G2API_SetSurfaceOnOff: NULL ghlInfo"); + if (G2_SetupModelPointers(ghlInfo)) + { + G2ERROR(!(flags & ~(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS)), "G2API_SetSurfaceOnOff Illegal Flags"); + // ensure we flush the cache + ghlInfo->mMeshFrameNum = 0; + return G2_SetSurfaceOnOff(ghlInfo, surfaceName, flags); + } + return qfalse; +} + +qboolean G2API_SetRootSurface(CGhoul2Info_v &ghoul2, const int modelIndex, const char *surfaceName) +{ + G2ERROR(ghoul2.IsValid(), "G2API_SetRootSurface: Invalid ghlInfo"); + G2ERROR(surfaceName, "G2API_SetRootSurface: Invalid surfaceName"); + + if (G2_SetupModelPointers(ghoul2)) + { + G2ERROR(modelIndex >= 0 && modelIndex < ghlInfo.size(), "Bad Model Index"); + if (modelIndex >= 0 && modelIndex < ghoul2.size()) + { + return G2_SetRootSurface(ghoul2, modelIndex, surfaceName); + } + } + + return qfalse; +} + +int G2API_AddSurface(CGhoul2Info *ghlInfo, int surfaceNumber, int polyNumber, float BarycentricI, float BarycentricJ, int lod ) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + // ensure we flush the cache + ghlInfo->mMeshFrameNum = 0; + return G2_AddSurface(ghlInfo, surfaceNumber, polyNumber, BarycentricI, BarycentricJ, lod); + } + return -1; +} + +qboolean G2API_RemoveSurface(CGhoul2Info *ghlInfo, const int index) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + // ensure we flush the cache + ghlInfo->mMeshFrameNum = 0; + return G2_RemoveSurface(ghlInfo->mSlist, index); + } + return qfalse; +} + +int G2API_GetParentSurface(CGhoul2Info *ghlInfo, const int index) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_GetParentSurface(ghlInfo, index); + } + return -1; +} + +int G2API_GetSurfaceRenderStatus(CGhoul2Info* ghlInfo, const char* surfaceName) +{ + G2ERROR(surfaceName, "G2API_GetSurfaceRenderStatus: Invalid surfaceName"); + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_IsSurfaceRendered(ghlInfo, surfaceName, ghlInfo->mSlist); + } + return -1; +} + +qboolean G2API_RemoveGhoul2Model(CGhoul2Info_v& ghlInfo, const int modelIndex) +{ + // sanity check + if (!ghlInfo.size() || (ghlInfo.size() <= modelIndex) || modelIndex < 0 || (ghlInfo[modelIndex].mModelindex < 0)) + { + // if we hit this assert then we are trying to delete a ghoul2 model on a ghoul2 instance that + // one way or another is already gone. + assert(0); + return qfalse; + } + + if (modelIndex < ghlInfo.size()) + { +#ifdef _G2_GORE + // Cleanup the gore attached to this model + if ( ghlInfo[modelIndex].mGoreSetTag ) + { + DeleteGoreSet ( ghlInfo[modelIndex].mGoreSetTag ); + ghlInfo[modelIndex].mGoreSetTag = 0; + } +#endif + + if (ghlInfo[modelIndex].mBoneCache) + { + RemoveBoneCache(ghlInfo[modelIndex].mBoneCache); + ghlInfo[modelIndex].mBoneCache=0; + } + + // clear out the vectors this model used. + ghlInfo[modelIndex].mBlist.clear(); + ghlInfo[modelIndex].mBltlist.clear(); + ghlInfo[modelIndex].mSlist.clear(); + + // set us to be the 'not active' state + ghlInfo[modelIndex].mModelindex = -1; + ghlInfo[modelIndex].mFileName[0] = 0; + ghlInfo[modelIndex] = CGhoul2Info(); + + int newSize = ghlInfo.size(); + + // ! the part below doesn't exist in SP Vanilla, but it makes sence ! + + // now look through the list from the back and see if there is a block of -1's we can resize off the end of the list + for (int i=ghlInfo.size()-1; i>-1; i--) + { + if (ghlInfo[i].mModelindex == -1) + { + newSize = i; + } + // once we hit one that isn't a -1, we are done. + else + { + break; + } + } + // do we need to resize? + if (newSize != ghlInfo.size()) + { + // yes, so lets do it + ghlInfo.resize(newSize); + } + } + + + return qtrue; +} + +int G2API_GetAnimIndex(CGhoul2Info* ghlInfo) +{ + return (ghlInfo) + ? ghlInfo->animModelIndexOffset + : 0; +} + +qboolean G2API_SetAnimIndex(CGhoul2Info* ghlInfo, const int index) +{ + // Is This A Valid G2 Model? + if (ghlInfo) + { + // Is This A New Anim Index? + if (ghlInfo->animModelIndexOffset != index) + { + ghlInfo->animModelIndexOffset = index; + // Clear anim size so SetupModelPointers recalcs + ghlInfo->currentAnimModelSize = 0; + + // Kill All Existing Animation, Blending, Etc. + //--------------------------------------------- + for (size_t index = 0; index < ghlInfo->mBlist.size(); index++) + { + ghlInfo->mBlist[index].flags &= ~(BONE_ANIM_TOTAL); + ghlInfo->mBlist[index].flags &= ~(BONE_ANGLES_TOTAL); + } + } + return qtrue; + } + return qfalse; +} + +//check if a bone exists on skeleton without actually adding to the bone list -rww +qboolean G2API_DoesBoneExist(CGhoul2Info_v& ghoul2, int modelIndex, const char *boneName) +{ + CGhoul2Info *ghlInfo = &ghoul2[modelIndex]; + + if (G2_SetupModelPointers(ghlInfo)) + { //model is valid + mdxaHeader_t *mdxa = ghlInfo->currentModel->data.gla; + if (mdxa) + { //get the skeleton data and iterate through the bones + int i; + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + + offsets = (mdxaSkelOffsets_t *)((byte *)mdxa + sizeof(mdxaHeader_t)); + + for (i = 0; i < mdxa->numBones; i++) + { + skel = (mdxaSkel_t *)((byte *)mdxa + sizeof(mdxaHeader_t) + offsets->offsets[i]); + if (!Q_stricmp(skel->name, boneName)) + { //got it + return qtrue; + } + } + } + } + + //guess it doesn't exist + return qfalse; +} + + +qboolean G2API_SetBoneAnimIndex(CGhoul2Info *ghlInfo, const int index, const int startFrame, const int endFrame, const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) +{ + //rww - RAGDOLL_BEGIN + if (ghlInfo && (ghlInfo->mFlags & GHOUL2_RAG_STARTED)) + { + return qfalse; + } + //rww - RAGDOLL_END + + qboolean ret = qfalse; + if (G2_SetupModelPointers(ghlInfo)) + { + G2ERROR(startFrame >= 0, "startframe < 0"); + G2ERROR(startFrame < ghlInfo->aHeader->numFrames, "startframe >= numframes"); + G2ERROR(endFrame > 0, "endframe <= 0"); + G2ERROR(endFrame <= ghlInfo->aHeader->numFrames, "endframe > numframes"); + G2ERROR(setFrame < ghlInfo->aHeader->numFrames, "setframe >= numframes"); + G2ERROR(setFrame == -1.0f || setFrame >= 0.0f, "setframe < 0 but not -1"); + + // this is cruel + if (startFrame < 0 || startFrame >= ghlInfo->aHeader->numFrames) + { + *(int*)&startFrame = 0; // cast away const + } + if (endFrame <= 0 || endFrame > ghlInfo->aHeader->numFrames) + { + *(int*)&endFrame = 1; + } + if (setFrame != -1.0f && (setFrame < 0.0f || setFrame >= (float)ghlInfo->aHeader->numFrames)) + { + *(float*)&setFrame = 0.0f; + } + ghlInfo->mSkelFrameNum = 0; + + G2ERROR(index >= 0 && index < (int)ghlInfo->mBlist.size(), va("Out of Range Bone Index (%s)", ghlInfo->mFileName)); + + if (index >= 0 && index < (int)ghlInfo->mBlist.size()) + { + G2ERROR(ghlInfo->mBlist[index].boneNumber >= 0, va("Bone Index is not Active (%s)", ghlInfo->mFileName)); + int a_currentTime = G2API_GetTime(currentTime); + + ret = G2_Set_Bone_Anim_Index(ghlInfo->mBlist, index, startFrame, endFrame, flags, animSpeed, a_currentTime, setFrame, blendTime, ghlInfo->aHeader->numFrames); + G2ANIM(ghlInfo, "G2API_SetBoneAnimIndex"); + } + } + + G2WARNING(ret, va("G2API_SetBoneAnimIndex Failed (%s)", ghlInfo->mFileName)); + return ret; +} + +qboolean G2API_SetBoneAnim(CGhoul2Info* ghlInfo, const char* boneName, const int startFrame, const int endFrame, + const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) +{ + qboolean ret = qfalse; + G2ERROR(boneName, "G2API_SetBoneAnim: NULL boneName"); + if (boneName) + { + int a_startFrame = (startFrame < 0 || startFrame > 100000) ? 0 : startFrame; + int a_endFrame = (endFrame < 0 || endFrame > 100000) ? 1 : endFrame; + float a_setFrame = ((setFrame < 0.f && setFrame != -1.f) || setFrame > 100000) ? 0.f : setFrame; + + qboolean setPtrs = qfalse; + bool res = false; + + //rww - RAGDOLL_BEGIN + if (ghlInfo) + { + res = G2_SetupModelPointers(ghlInfo); + setPtrs = qtrue; + + if (res) + { + if (ghlInfo->mFlags & GHOUL2_RAG_STARTED) + { + return qfalse; + } + } + } + //rww - RAGDOLL_END + + if (!setPtrs) + { + res = G2_SetupModelPointers(ghlInfo); + } + + if (res && ghlInfo) + { + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + int a_currentTime = G2API_GetTime(currentTime); + return G2_Set_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, a_startFrame, a_endFrame, flags, animSpeed, a_currentTime, a_setFrame, blendTime); + } + } + return qfalse; +} + +qboolean G2API_GetBoneAnim(CGhoul2Info* ghlInfo, const char* boneName, const int currentTime, float* currentFrame, + int* startFrame, int* endFrame, int* flags, float* animSpeed, qhandle_t* modelList) +{ + G2ERROR(boneName, "G2API_GetBoneAnim: NULL boneName"); + + assert(startFrame != endFrame); //this is bad + assert(startFrame != flags); //this is bad + assert(endFrame != flags); //this is bad + assert(currentFrame != animSpeed); //this is bad + + if (G2_SetupModelPointers(ghlInfo)) + { + int a_CurrentTime=G2API_GetTime(currentTime); +#ifdef _DEBUG + /* + assert(*endFrame>0); + assert(*endFrame<100000); + assert(*startFrame>=0); + assert(*startFrame<100000); + assert(*currentFrame>=0.0f); + assert(*currentFrame<100000.0f); + */ + if (*endFrame<1) + { + *endFrame=1; + } + if (*endFrame>100000) + { + *endFrame=1; + } + if (*startFrame<0) + { + *startFrame=0; + } + if (*startFrame>100000) + { + *startFrame=1; + } + if (*currentFrame<0.0f) + { + *currentFrame=0.0f; + } + if (*currentFrame>100000) + { + *currentFrame=1; + } +#endif + + qboolean ret = G2_Get_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, a_CurrentTime, currentFrame, startFrame, endFrame, flags, animSpeed); + return ret; + } + + G2WARNING(ret, "G2API_GetBoneAnim Failed"); + return qfalse; +} + +qboolean G2API_GetBoneAnimIndex(CGhoul2Info* ghlInfo, const int iBoneIndex, const int currentTime, float* currentFrame, + int* startFrame, int* endFrame, int* flags, float* animSpeed, qhandle_t* modelList) +{ + qboolean ret = qfalse; + if (G2_SetupModelPointers(ghlInfo)) + { + int a_currentTime = G2API_GetTime(currentTime); + + if (iBoneIndex >= 0 && iBoneIndex < (int)ghlInfo->mBlist.size()) + { + if ((ghlInfo->mBlist[iBoneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))) + { + int sf, ef; + ret = G2_Get_Bone_Anim_Index( + ghlInfo->mBlist,// boneInfo_v &blist, + iBoneIndex, // const int index, + a_currentTime, // const int a_currentTime, + currentFrame, // float *currentFrame, + &sf, // int *startFrame, + &ef, // int *endFrame, + flags, // int *flags, + animSpeed, // float *retAnimSpeed, + ghlInfo->aHeader->numFrames + ); + + G2ERROR(sf >= 0, "returning startframe<0"); + G2ERROR(sf < ghlInfo->aHeader->numFrames, "returning startframe>=numframes"); + G2ERROR(ef > 0, "returning endframe<=0"); + G2ERROR(ef <= ghlInfo->aHeader->numFrames, "returning endframe>numframes"); + + if (currentFrame) + { + G2ERROR(*currentFrame >= 0.0f, "returning currentframe<0"); + G2ERROR(((int)(*currentFrame)) < ghlInfo->aHeader->numFrames, "returning currentframe>=numframes"); + } + if (endFrame) + { + *endFrame = ef; + } + if (startFrame) + { + *startFrame = sf; + } + } + } + } + if (!ret) + { + if (endFrame) *endFrame = 1; + if (startFrame) *startFrame = 0; + if (flags) *flags = 0; + if (currentFrame) *currentFrame = 0.0f; + if (animSpeed) *animSpeed = 1.0f; + } + G2NOTE(ret, "G2API_GetBoneAnimIndex Failed"); + return ret; +} + +qboolean G2API_GetAnimRange(CGhoul2Info *ghlInfo, const char *boneName, int *startFrame, int *endFrame) +{ + G2ERROR(boneName, "G2API_GetAnimRange: NULL boneName"); + assert(startFrame != endFrame); //this is bad + + if (boneName && G2_SetupModelPointers(ghlInfo)) + { + qboolean ret=G2_Get_Bone_Anim_Range(ghlInfo, ghlInfo->mBlist, boneName, startFrame, endFrame); + G2ANIM(ghlInfo, "G2API_GetAnimRange"); +#ifdef _DEBUG + assert(*endFrame > 0); + assert(*endFrame < 100000); + assert(*startFrame >= 0); + assert(*startFrame < 100000); +#endif + return ret; + } + return qfalse; +} + +qboolean G2API_GetAnimRangeIndex(CGhoul2Info* ghlInfo, const int boneIndex, int* startFrame, int* endFrame) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + G2ERROR(boneIndex >= 0 && boneIndex < (int)ghlInfo->mBlist.size(), "Bad Bone Index"); + if (boneIndex >= 0 && boneIndex < (int)ghlInfo->mBlist.size()) + { + return G2_Get_Bone_Anim_Range_Index(ghlInfo->mBlist, boneIndex, startFrame, endFrame); + } + } + return qfalse; +} + +qboolean G2API_PauseBoneAnim(CGhoul2Info *ghlInfo, const char *boneName, const int currentTime) +{ + G2ERROR(boneName, "G2API_PauseBoneAnim: NULL boneName"); + if (G2_SetupModelPointers(ghlInfo)) + { + int a_currentTime = G2API_GetTime(currentTime); + return G2_Pause_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName, a_currentTime); + } + G2WARNING(ret, "G2API_PauseBoneAnim Failed"); + return qfalse; +} + +qboolean G2API_PauseBoneAnimIndex(CGhoul2Info* ghlInfo, const int boneIndex, const int currentTime) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + int a_currentTime = G2API_GetTime(currentTime); + + if (boneIndex >= 0 && boneIndex < (int)ghlInfo->mBlist.size()) + { + return G2_Pause_Bone_Anim_Index(ghlInfo->mBlist, boneIndex, a_currentTime, ghlInfo->aHeader->numFrames); + } + } + + G2WARNING(ret, "G2API_PauseBoneAnimIndex Failed"); + return qfalse; +} + +qboolean G2API_IsPaused(CGhoul2Info *ghlInfo, const char *boneName) +{ + G2ERROR(boneName, "G2API_IsPaused: NULL boneName"); + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_IsPaused(ghlInfo, ghlInfo->mBlist, boneName); + } + return qfalse; +} + +qboolean G2API_StopBoneAnimIndex(CGhoul2Info *ghlInfo, const int index) +{ + G2ERROR(ghlInfo, "G2API_StopBoneAnimIndex: NULL ghlInfo"); + if (G2_SetupModelPointers(ghlInfo)) + { + if (index >= 0 && index < (int)ghlInfo->mBlist.size()) + { + return G2_Stop_Bone_Anim_Index(ghlInfo->mBlist, index); + } + } + return qfalse; +} + +qboolean G2API_StopBoneAnim(CGhoul2Info *ghlInfo, const char *boneName) +{ + G2ERROR(boneName, "G2API_StopBoneAnim: NULL boneName"); + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_Stop_Bone_Anim(ghlInfo, ghlInfo->mBlist, boneName); + } + G2WARNING(ret, "G2API_StopBoneAnim Failed"); + return qfalse; +} + +qboolean G2API_SetBoneAnglesIndex( + CGhoul2Info *ghlInfo, const int index, const vec3_t angles, const int flags, + const Eorientations yaw, const Eorientations pitch, const Eorientations roll, + qhandle_t *modelList, int blendTime, int currentTime) +{ + qboolean setPtrs = qfalse; + bool res = false; + + if (ghlInfo) + { + res = G2_SetupModelPointers(ghlInfo); + setPtrs = qtrue; + + if (res) + { + if (ghlInfo->mFlags & GHOUL2_RAG_STARTED) + { + return qfalse; + } + } + } + + if (!setPtrs) + { + res = G2_SetupModelPointers(ghlInfo); + } + + if (res) + { + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + return G2_Set_Bone_Angles_Index(ghlInfo, ghlInfo->mBlist, index, angles, flags, yaw, pitch, roll, blendTime, currentTime); + } + + return qfalse; +} + +qboolean G2API_SetBoneAngles(CGhoul2Info* ghlInfo, const char* boneName, const vec3_t angles, const int flags, + const Eorientations up, const Eorientations left, const Eorientations forward, qhandle_t* modelList, int blendTime, int currentTime) +{ + //rww - RAGDOLL_BEGIN + if (ghlInfo && ghlInfo->mFlags & GHOUL2_RAG_STARTED) + { + return qfalse; + } + //rww - RAGDOLL_END + + qboolean ret = qfalse; + + G2ERROR(boneName, "G2API_SetBoneAngles: NULL boneName"); + if (boneName && G2_SetupModelPointers(ghlInfo)) + { + currentTime = G2API_GetTime(currentTime); + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + ret = G2_Set_Bone_Angles(ghlInfo, ghlInfo->mBlist, boneName, angles, flags, up, left, forward, blendTime, currentTime); + } + + G2WARNING(ret, "G2API_SetBoneAngles Failed"); + return ret; +} + +qboolean G2API_SetBoneAnglesMatrixIndex( + CGhoul2Info *ghlInfo, const int index, const mdxaBone_t &matrix, const int flags, + qhandle_t *modelList, int blendTime, int currentTime) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + currentTime = G2API_GetTime(currentTime); + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + G2ERROR(index >= 0 && index < (int)ghlInfo->mBlist.size(), "Bad Bone Index"); + if (index >= 0 && index < (int)ghlInfo->mBlist.size()) + { + return G2_Set_Bone_Angles_Matrix_Index(ghlInfo->mBlist, index, matrix, flags, blendTime, currentTime); + } + } + return qfalse; +} + +qboolean G2API_SetBoneAnglesMatrix( + CGhoul2Info *ghlInfo, const char *boneName, const mdxaBone_t &matrix, const int flags, + qhandle_t *modelList, int blendTime, int currentTime) +{ + G2ERROR(boneName, "G2API_SetBoneAnglesMatrix: NULL boneName"); + if (G2_SetupModelPointers(ghlInfo)) + { + currentTime = G2API_GetTime(currentTime); + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + return G2_Set_Bone_Angles_Matrix(ghlInfo, ghlInfo->mBlist, boneName, matrix, flags, blendTime, currentTime); + } + return qfalse; +} + +qboolean G2API_StopBoneAnglesIndex(CGhoul2Info *ghlInfo, const int index) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + G2ERROR(index >= 0 && index < (int)ghlInfo->mBlist.size(), "Bad Bone Index"); + if (index >= 0 && index < (int)ghlInfo->mBlist.size()) + { + return G2_Stop_Bone_Angles_Index(ghlInfo->mBlist, index); + } + } + return qfalse; +} + +qboolean G2API_StopBoneAngles(CGhoul2Info *ghlInfo, const char *boneName) +{ + if (boneName && G2_SetupModelPointers(ghlInfo)) + { + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + return G2_Stop_Bone_Angles( + ghlInfo, ghlInfo->mBlist, boneName); + } + return qfalse; +} + +void G2API_AbsurdSmoothing(CGhoul2Info_v &ghoul2, qboolean status) +{ + assert(ghoul2.size()); + CGhoul2Info *ghlInfo = &ghoul2[0]; +#ifndef REND2_SP + if (status) + { //turn it on + ghlInfo->mFlags |= GHOUL2_CRAZY_SMOOTH; + } + else + { //off + ghlInfo->mFlags &= ~GHOUL2_CRAZY_SMOOTH; + } +#endif +} + +//rww - RAGDOLL_BEGIN +class CRagDollParams; +void G2_SetRagDoll(CGhoul2Info_v &ghoul2V,CRagDollParams *parms); +void G2API_SetRagDoll(CGhoul2Info_v &ghoul2,CRagDollParams *parms) +{ + G2_SetRagDoll(ghoul2,parms); +} + +void G2_ResetRagDoll(CGhoul2Info_v &ghoul2V); +void G2API_ResetRagDoll(CGhoul2Info_v &ghoul2) +{ + G2_ResetRagDoll(ghoul2); +} +//rww - RAGDOLL_END + +qboolean G2API_RemoveBone(CGhoul2Info* ghlInfo, const char* boneName) +{ + G2ERROR(boneName, "G2API_RemoveBone: NULL boneName"); + + if (boneName && G2_SetupModelPointers(ghlInfo)) + { + // ensure we flush the cache + ghlInfo->mSkelFrameNum = 0; + return G2_Remove_Bone(ghlInfo, ghlInfo->mBlist, boneName); + } + return qfalse; +} + +//rww - RAGDOLL_BEGIN +#ifdef _DEBUG +extern int ragTraceTime; +extern int ragSSCount; +extern int ragTraceCount; +#endif + +// This is G2API_AnimateG2Models +void G2API_AnimateG2ModelsRag(CGhoul2Info_v &ghoul2, int AcurrentTime, CRagDollUpdateParams *params) +{ + int model; + int currentTime = G2API_GetTime(AcurrentTime); + +#ifdef _DEBUG + ragTraceTime = 0; + ragSSCount = 0; + ragTraceCount = 0; +#endif + + // Walk the list and find all models that are active + for (model = 0; model < ghoul2.size(); model++) + { + if (ghoul2[model].mModel) + { + G2_Animate_Bone_List(ghoul2, currentTime, model, params); + } + } +} +// rww - RAGDOLL_END + +int G2_Find_Bone_Rag( + CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName); +#define RAG_PCJ (0x00001) +#define RAG_EFFECTOR (0x00100) + +static boneInfo_t* G2_GetRagBoneConveniently(CGhoul2Info_v &ghoul2, const char *boneName) +{ + assert(ghoul2.size()); + CGhoul2Info *ghlInfo = &ghoul2[0]; + + if (!(ghlInfo->mFlags & GHOUL2_RAG_STARTED)) + { // can't do this if not in ragdoll + return NULL; + } + + int boneIndex = G2_Find_Bone_Rag(ghlInfo, ghlInfo->mBlist, boneName); + + if (boneIndex < 0) + { // bad bone specification + return NULL; + } + + boneInfo_t *bone = &ghlInfo->mBlist[boneIndex]; + + if (!(bone->flags & BONE_ANGLES_RAGDOLL)) + { // only want to return rag bones + return NULL; + } + + return bone; +} + +qboolean G2API_RagPCJConstraint(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t min, vec3_t max) +{ + boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName); + + if (!bone) + { + return qfalse; + } + + if (!(bone->RagFlags & RAG_PCJ)) + { // this function is only for PCJ bones + return qfalse; + } + + VectorCopy(min, bone->minAngles); + VectorCopy(max, bone->maxAngles); + + return qtrue; +} + +qboolean G2API_RagPCJGradientSpeed(CGhoul2Info_v &ghoul2, const char *boneName, const float speed) +{ + boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName); + + if (!bone) + { + return qfalse; + } + + if (!(bone->RagFlags & RAG_PCJ)) + { // this function is only for PCJ bones + return qfalse; + } + + bone->overGradSpeed = speed; + + return qtrue; +} + +qboolean G2API_RagEffectorGoal(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t pos) +{ + boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName); + + if (!bone) + { + return qfalse; + } + + if (!(bone->RagFlags & RAG_EFFECTOR)) + { // this function is only for effectors + return qfalse; + } + + if (!pos) + { // go back to none in case we have one then + bone->hasOverGoal = false; + } + else + { + VectorCopy(pos, bone->overGoalSpot); + bone->hasOverGoal = true; + } + return qtrue; +} + +qboolean G2API_GetRagBonePos(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t pos, vec3_t entAngles, vec3_t entPos, vec3_t entScale) +{ + return qfalse; +} + +qboolean G2API_RagEffectorKick(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t velocity) +{ + boneInfo_t *bone = G2_GetRagBoneConveniently(ghoul2, boneName); + + if (!bone) + { + return qfalse; + } + + if (!(bone->RagFlags & RAG_EFFECTOR)) + { //this function is only for effectors + return qfalse; + } + + bone->epVelocity[2] = 0; + VectorAdd(bone->epVelocity, velocity, bone->epVelocity); + bone->physicsSettled = false; + + return qtrue; +} + +qboolean G2API_RagForceSolve(CGhoul2Info_v &ghoul2, qboolean force) +{ + assert(ghoul2.size()); + CGhoul2Info *ghlInfo = &ghoul2[0]; + + if (!(ghlInfo->mFlags & GHOUL2_RAG_STARTED)) + { //can't do this if not in ragdoll + return qfalse; + } + + if (force) + { + ghlInfo->mFlags |= GHOUL2_RAG_FORCESOLVE; + } + else + { + ghlInfo->mFlags &= ~GHOUL2_RAG_FORCESOLVE; + } + + return qtrue; +} + +qboolean G2_SetBoneIKState(CGhoul2Info_v &ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params); + +qboolean G2API_SetBoneIKState(CGhoul2Info_v &ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params) +{ + return G2_SetBoneIKState(ghoul2, time, boneName, ikState, params); +} + +qboolean G2_IKMove(CGhoul2Info_v &ghoul2, int time, sharedIKMoveParams_t *params); + +qboolean G2API_IKMove(CGhoul2Info_v &ghoul2, int time, sharedIKMoveParams_t *params) +{ + return G2_IKMove(ghoul2, time, params); +} + +qboolean G2API_RemoveBolt(CGhoul2Info *ghlInfo, const int index) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_Remove_Bolt(ghlInfo->mBltlist, index); + } + return qfalse; +} + +int G2API_AddBolt(CGhoul2Info* ghlInfo, const char* boneName) +{ + // assert(ghoul2.size()>modelIndex); + if (boneName && G2_SetupModelPointers(ghlInfo)) + { + return G2_Add_Bolt(ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, boneName); + } + return -1; +} + +int G2API_AddBoltSurfNum(CGhoul2Info *ghlInfo, const int surfIndex) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_Add_Bolt_Surf_Num( + ghlInfo, ghlInfo->mBltlist, ghlInfo->mSlist, surfIndex); + } + return -1; +} + +qboolean G2API_AttachG2Model(CGhoul2Info* ghlInfo, CGhoul2Info* ghlInfoTo, int toBoltIndex, int toModel) +{ + qboolean ret = qfalse; + + if (G2_SetupModelPointers(ghlInfo) && G2_SetupModelPointers(ghlInfoTo)) + { + G2ERROR(toBoltIndex >= 0 && toBoltIndex < (int)ghlInfoTo->mBltlist.size(), "Invalid Bolt Index"); + G2ERROR(ghlInfoTo->mBltlist.size() > 0, "Empty Bolt List"); + + assert(toBoltIndex >= 0); + + if (toBoltIndex >= 0 && ghlInfoTo->mBltlist.size()) + { + // make sure we have a model to attach, a model to attach to, and a bolt on that model + if (((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) + { + // encode the bolt address into the model bolt link + toModel &= MODEL_AND; + toBoltIndex &= BOLT_AND; + ghlInfo->mModelBoltLink = (toModel << MODEL_SHIFT) | (toBoltIndex << BOLT_SHIFT); + ret = qtrue; + } + } + } + G2WARNING(ret, "G2API_AttachG2Model Failed"); + return ret; +} + +void G2API_SetBoltInfo(CGhoul2Info_v &ghoul2, int modelIndex, int boltInfo) +{ + if (ghoul2.size() > modelIndex) + { + ghoul2[modelIndex].mModelBoltLink = boltInfo; + } +} + +qboolean G2API_DetachG2Model(CGhoul2Info *ghlInfo) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + ghlInfo->mModelBoltLink = -1; + return qtrue; + } + return qfalse; +} + +qboolean G2API_AttachEnt(int* boltInfo, CGhoul2Info* ghlInfoTo, int toBoltIndex, int entNum, int toModelNum) +{ + if (boltInfo && G2_SetupModelPointers(ghlInfoTo)) + { + // make sure we have a model to attach, a model to attach to, and a + // bolt on that model + if (ghlInfoTo->mBltlist.size() && + ((ghlInfoTo->mBltlist[toBoltIndex].boneNumber != -1) || + (ghlInfoTo->mBltlist[toBoltIndex].surfaceNumber != -1))) + { + // encode the bolt address into the model bolt link + toModelNum &= MODEL_AND; + toBoltIndex &= BOLT_AND; + entNum &= ENTITY_AND; + *boltInfo = (toBoltIndex << BOLT_SHIFT) | + (toModelNum << MODEL_SHIFT) | + (entNum << ENTITY_SHIFT); + return qtrue; + } + } + return qfalse; +} + +void G2API_DetachEnt(int* boltInfo) +{ + G2ERROR(boltInfo, "G2API_DetachEnt: NULL boltInfo"); + if (boltInfo) + { + *boltInfo = 0; + } +} + +bool G2_NeedsRecalc(CGhoul2Info *ghlInfo, int frameNum); + +qboolean G2API_GetBoltMatrix(CGhoul2Info_v& ghoul2, const int modelIndex, const int boltIndex, mdxaBone_t* matrix, + const vec3_t angles, const vec3_t position, const int frameNum, qhandle_t* modelList, const vec3_t scale) +{ + G2ERROR(ghoul2.IsValid(), "Invalid ghlInfo"); + G2ERROR(matrix, "NULL matrix"); + G2ERROR(modelIndex >= 0 && modelIndex < ghoul2.size(), "Invalid ModelIndex"); + const static mdxaBone_t identityMatrix = + { + { + {0.0f, -1.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f} + } + }; + + G2_GenerateWorldMatrix(angles, position); + if (G2_SetupModelPointers(ghoul2)) + { + if (matrix && modelIndex >= 0 && modelIndex < ghoul2.size()) + { + int tframeNum = G2API_GetTime(frameNum); + CGhoul2Info *ghlInfo = &ghoul2[modelIndex]; + G2ERROR + (boltIndex >= 0 && (boltIndex < ghlInfo->mBltlist.size()), + va("Invalid Bolt Index (%d:%s)", + boltIndex, + ghlInfo->mFileName)); + + if (boltIndex >= 0 && ghlInfo && (boltIndex < (int)ghlInfo->mBltlist.size())) + { + mdxaBone_t bolt; + + if (G2_NeedsRecalc(ghlInfo, tframeNum)) + { + G2_ConstructGhoulSkeleton(ghoul2, tframeNum, true, scale); + } + + G2_GetBoltMatrixLow(*ghlInfo, boltIndex, scale, bolt); + + // scale the bolt position by the scale factor for this model + // since at this point its still in model space + if (scale[0]) + { + bolt.matrix[0][3] *= scale[0]; + } + + if (scale[1]) + { + bolt.matrix[1][3] *= scale[1]; + } + + if (scale[2]) + { + bolt.matrix[2][3] *= scale[2]; + } + + VectorNormalize((float *)&bolt.matrix[0]); + VectorNormalize((float *)&bolt.matrix[1]); + VectorNormalize((float *)&bolt.matrix[2]); + + Mat3x4_Multiply(matrix, &worldMatrix, &bolt); +#if G2API_DEBUG + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 4; j++) + { + assert(!_isnan(matrix->matrix[i][j])); + } + } +#endif // _DEBUG + G2ANIM(ghlInfo, "G2API_GetBoltMatrix"); + + return qtrue; + } + } + } + else + { + G2WARNING(0, "G2API_GetBoltMatrix Failed on empty or bad model"); + } + Mat3x4_Multiply(matrix, &worldMatrix, (mdxaBone_t *)&identityMatrix); + return qfalse; +} + +void G2API_ListSurfaces(CGhoul2Info *ghlInfo) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + G2_List_Model_Surfaces(ghlInfo->mFileName); + } +} + +void G2API_ListBones(CGhoul2Info *ghlInfo, int frame) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + G2_List_Model_Bones(ghlInfo->mFileName, frame); + } +} + +// decide if we have Ghoul2 models associated with this ghoul list or not +qboolean G2API_HaveWeGhoul2Models(CGhoul2Info_v &ghoul2) +{ + if (ghoul2.size()) + { + for (int i = 0; i < ghoul2.size(); i++) + { + if (ghoul2[i].mModelindex != -1) + { + return qtrue; + } + } + } + return qfalse; +} + +// run through the Ghoul2 models and set each of the mModel values to the +// correct one from the cgs.gameModel offset lsit +void G2API_SetGhoul2ModelIndexes(CGhoul2Info_v &ghoul2, qhandle_t *modelList, qhandle_t *skinList) +{ + G2ERROR(ghoul2.IsValid(), "G2API_SetGhoul2ModelIndexes: Invalid ghlInfo"); + + for (int i = 0; i < ghoul2.size(); i++) + { + if (ghoul2[i].mModelindex != -1) + { + ghoul2[i].mSkin = skinList[ghoul2[i].mCustomSkin]; + } + } +} + +char *G2API_GetAnimFileNameIndex(qhandle_t modelIndex) +{ + model_t* mod_m = R_GetModelByHandle(modelIndex); + + G2ERROR(mod_m && mod_m->data.glm, "G2API_GetAnimFileNameIndex: Bad Model"); + if (mod_m && mod_m->data.glm && mod_m->data.glm->header) + { + return mod_m->data.glm->header->animName; + } + return ""; +} + +// As above, but gets the internal embedded name, not the name of the disk file. +// This is needed for some unfortunate jiggery-hackery to do with frameskipping & the animevents.cfg file +char* G2API_GetAnimFileInternalNameIndex(qhandle_t modelIndex) +{ + model_t* mod_a = R_GetModelByHandle(modelIndex); + + G2ERROR(mod_a && mod_a->data.gla, "G2API_GetAnimFileInternalNameIndex: Bad Model"); + if (mod_a && mod_a->data.gla) + { + return mod_a->data.gla->name; + } + return ""; +} + +qboolean G2API_GetAnimFileName(CGhoul2Info *ghlInfo, char **filename) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_GetAnimFileName(ghlInfo->mFileName, filename); + } + return qfalse; +} + +/* +======================= +SV_QsortEntityNumbers +======================= +*/ +static int QDECL QsortDistance(const void *a, const void *b) +{ + const float &ea = ((CCollisionRecord*)a)->mDistance; + const float &eb = ((CCollisionRecord*)b)->mDistance; + return (ea < eb) ? -1 : 1; +} + +void G2API_CollisionDetect( + CCollisionRecord* collRecMap, CGhoul2Info_v& ghoul2, + const vec3_t angles, const vec3_t position, int frameNumber, int entNum, + vec3_t rayStart, vec3_t rayEnd, vec3_t scale, + CMiniHeap* G2VertSpace, EG2_Collision eG2TraceType, int useLod, float fRadius) +{ + // not exactly correct, but doesn't matter + G2ERROR(ghoul2.IsValid(), "G2API_CollisionDetect: Invalid ghlInfo"); + G2ERROR(collRecMap, "G2API_CollisionDetect: NULL Collision Rec"); + + if (G2_SetupModelPointers(ghoul2) && collRecMap) + { + frameNumber = G2API_GetTime(frameNumber); + vec3_t transRayStart, transRayEnd; + + // make sure we have transformed the whole skeletons for each model + G2_ConstructGhoulSkeleton(ghoul2, frameNumber, true, scale); + + // pre generate the world matrix - used to transform the incoming ray + G2_GenerateWorldMatrix(angles, position); + + ri.GetG2VertSpaceServer()->ResetHeap(); + +// now having done that, time to build the model +#ifdef _G2_GORE + G2_TransformModel(ghoul2, frameNumber, scale, ri.GetG2VertSpaceServer(), useLod, false); +#else + G2_TransformModel(ghoul2, frameNumber, scale, ri.GetG2VertSpaceServer(), useLod); +#endif + + // model is built. Lets check to see if any triangles are actually hit. + // first up, translate the ray to model space + TransformAndTranslatePoint(rayStart, transRayStart, &worldMatrixInv); + TransformAndTranslatePoint(rayEnd, transRayEnd, &worldMatrixInv); + + // now walk each model and check the ray against each poly - sigh, this is SO expensive. I wish there was a better way to do this. +#ifdef _G2_GORE + G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, eG2TraceType, useLod, fRadius, 0, 0, 0, 0, 0, qfalse); +#else + G2_TraceModels(ghoul2, transRayStart, transRayEnd, collRecMap, entNum, eG2TraceType, useLod, fRadius); +#endif + + ri.GetG2VertSpaceServer()->ResetHeap(); + // now sort the resulting array of collision records so they are distance ordered + qsort(collRecMap, MAX_G2_COLLISIONS, sizeof(CCollisionRecord), QsortDistance); + G2ANIM(ghoul2, "G2API_CollisionDetect"); + } +} + +qboolean G2API_SetGhoul2ModelFlags(CGhoul2Info *ghlInfo, const int flags) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + ghlInfo->mFlags &= GHOUL2_NEWORIGIN; + ghlInfo->mFlags |= flags; + return qtrue; + } + return qfalse; +} + +int G2API_GetGhoul2ModelFlags(CGhoul2Info *ghlInfo) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + return (ghlInfo->mFlags & ~GHOUL2_NEWORIGIN); + } + return 0; +} + +// given a boltmatrix, return in vec a normalised vector for the axis requested in flags +void G2API_GiveMeVectorFromMatrix(mdxaBone_t& boltMatrix, Eorientations flags, vec3_t& vec) +{ + switch (flags) + { + case ORIGIN: + vec[0] = boltMatrix.matrix[0][3]; + vec[1] = boltMatrix.matrix[1][3]; + vec[2] = boltMatrix.matrix[2][3]; + break; + case POSITIVE_Y: + vec[0] = boltMatrix.matrix[0][1]; + vec[1] = boltMatrix.matrix[1][1]; + vec[2] = boltMatrix.matrix[2][1]; + break; + case POSITIVE_X: + vec[0] = boltMatrix.matrix[0][0]; + vec[1] = boltMatrix.matrix[1][0]; + vec[2] = boltMatrix.matrix[2][0]; + break; + case POSITIVE_Z: + vec[0] = boltMatrix.matrix[0][2]; + vec[1] = boltMatrix.matrix[1][2]; + vec[2] = boltMatrix.matrix[2][2]; + break; + case NEGATIVE_Y: + vec[0] = -boltMatrix.matrix[0][1]; + vec[1] = -boltMatrix.matrix[1][1]; + vec[2] = -boltMatrix.matrix[2][1]; + break; + case NEGATIVE_X: + vec[0] = -boltMatrix.matrix[0][0]; + vec[1] = -boltMatrix.matrix[1][0]; + vec[2] = -boltMatrix.matrix[2][0]; + break; + case NEGATIVE_Z: + vec[0] = -boltMatrix.matrix[0][2]; + vec[1] = -boltMatrix.matrix[1][2]; + vec[2] = -boltMatrix.matrix[2][2]; + break; + } +} + +// copy a model from one ghoul2 instance to another, and reset the root surface on the new model if need be +// NOTE if modelIndex = -1 then copy all the models +void G2API_CopyGhoul2Instance(CGhoul2Info_v &g2From, CGhoul2Info_v &g2To, int modelIndex) +{ + G2ERROR(ghoul2From.IsValid(),"G2API_CopyGhoul2Instance: Invalid ghlInfo"); + + if (g2From.IsValid()) + { + g2To.DeepCopy(g2From); + +#ifdef _G2_GORE //check through gore stuff then, as well. + int model = 0; + + //(since we are sharing this gore set with the copied instance we will have to increment + //the reference count - if the goreset is "removed" while the refcount is > 0, the refcount + //is decremented to avoid giving other instances an invalid pointer -rww) + while (model < g2To.size()) + { + if (g2To[model].mGoreSetTag) + { + CGoreSet* gore = FindGoreSet(g2To[model].mGoreSetTag); + assert(gore); + if (gore) + { + gore->mRefCount++; + } + } + + model++; + } +#endif + G2ANIM(ghoul2From, "G2API_CopyGhoul2Instance (source)"); + G2ANIM(ghoul2To, "G2API_CopyGhoul2Instance (dest)"); + } +} + +char* G2API_GetSurfaceName(CGhoul2Info* ghlInfo, int surfNumber) +{ + static char noSurface[1] = ""; + + if (G2_SetupModelPointers(ghlInfo)) + { + model_t *mod = (model_t *)ghlInfo->currentModel; + mdxmSurface_t* surf = 0; + mdxmSurfHierarchy_t* surfInfo = 0; + mdxmHeader_t* mdxm; + + mdxm = mod->data.glm->header; + + //ok, I guess it's semi-valid for the user to be passing in surface > numSurfs because they don't know how many surfs a model + //may have.. but how did they get that surf index to begin with? Oh well. + if (surfNumber < 0 || surfNumber >= mdxm->numSurfaces) + { + Com_Printf("G2API_GetSurfaceName: You passed in an invalid surface number (%i) for model %s.\n", surfNumber, ghlInfo->mFileName); + return noSurface; + } + + surf = (mdxmSurface_t*)G2_FindSurface(mod, surfNumber, 0); + if (surf) + { + mdxmHierarchyOffsets_t* surfIndexes = (mdxmHierarchyOffsets_t*)((byte*)ghlInfo->currentModel->data.glm->header + sizeof(mdxmHeader_t)); + surfInfo = (mdxmSurfHierarchy_t*)((byte*)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); + return surfInfo->name; + } + } + return noSurface; +} + + +int G2API_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName) +{ + G2ERROR(surfaceName, "G2API_GetSurfaceIndex: NULL surfaceName"); + + if (G2_SetupModelPointers(ghlInfo)) + { + return G2_GetSurfaceIndex(ghlInfo, surfaceName); + } + return -1; +} + +char* G2API_GetGLAName(CGhoul2Info* ghlInfo) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + assert(ghlInfo && ghlInfo->currentModel->data.glm); + return (char*)ghlInfo->aHeader->name; + } + return NULL; +} + +qboolean G2API_SetNewOrigin(CGhoul2Info* ghlInfo, const int boltIndex) +{ + if (G2_SetupModelPointers(ghlInfo)) + { + if (boltIndex < 0) + { + char modelName[MAX_QPATH]; + if (ghlInfo->currentModel && ghlInfo->currentModel->name[0]) + { + strcpy(modelName, ghlInfo->currentModel->name); + } + else + { + strcpy(modelName, "[Unknown - unexpected]"); + } + + Com_Error(ERR_DROP, "Bad boltindex (%i) trying to SetNewOrigin (naughty naughty!)\nModel %s\n", boltIndex, modelName); + } + + ghlInfo->mNewOrigin = boltIndex; + ghlInfo->mFlags |= GHOUL2_NEWORIGIN; + return qtrue; + } + return qfalse; +} + +int G2API_GetBoneIndex(CGhoul2Info *ghlInfo, const char *boneName, qboolean bAddIfNotFound) +{ + G2ERROR(boneName, "G2API_GetBoneIndex: NULL boneName"); + if (boneName && G2_SetupModelPointers(ghlInfo)) + { + return G2_Get_Bone_Index(ghlInfo, boneName, bAddIfNotFound); + } + return -1; +} + +void G2API_SaveGhoul2Models(CGhoul2Info_v &ghoul2) +{ + G2_SaveGhoul2Models(ghoul2); + G2ANIM(ghoul2, "G2API_SaveGhoul2Models"); +} + +void G2API_LoadGhoul2Models(CGhoul2Info_v &ghoul2, char *buffer) +{ + G2_LoadGhoul2Model(ghoul2, buffer); + G2ANIM(ghoul2, "G2API_LoadGhoul2Models"); +} + +// this is kinda sad, but I need to call the destructor in this module (exe), not the game.dll... +// +void G2API_LoadSaveCodeDestructGhoul2Info(CGhoul2Info_v &ghoul2) +{ + +#ifdef _G2_GORE + G2API_ClearSkinGore ( ghoul2 ); +#endif + ghoul2.~CGhoul2Info_v(); // so I can load junk over it then memset to 0 without orphaning +} + +//see if surfs have any shader info... +qboolean G2API_SkinlessModel(CGhoul2Info_v& ghoul2, int modelIndex) +{ + CGhoul2Info *g2 = &ghoul2[modelIndex]; + + if (G2_SetupModelPointers(g2)) + { + model_t *mod = (model_t *)g2->currentModel; + + if (mod && + mod->data.glm && + mod->data.glm->header) + { + mdxmSurfHierarchy_t *surf; + int i; + mdxmHeader_t *mdxm = mod->data.glm->header; + + surf = (mdxmSurfHierarchy_t *) ( (byte *)mdxm + mdxm->ofsSurfHierarchy ); + + for (i = 0; i < mdxm->numSurfaces; i++) + { + if (surf->shader[0]) + { //found a surface with a shader name, ok. + return qfalse; + } + + surf = (mdxmSurfHierarchy_t *)( (byte *)surf + (intptr_t)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surf->numChildren ] )); + } + } + } + + //found nothing. + return qtrue; +} + +int G2API_Ghoul2Size(CGhoul2Info_v &ghoul2) +{ + return ghoul2.size(); +} + +//#ifdef _SOF2 +#ifdef _G2_GORE +void ResetGoreTag(); // put here to reduce coupling + +//way of seeing how many marks are on a model currently -rww +int G2API_GetNumGoreMarks(CGhoul2Info_v& ghoul2, int modelIndex) +{ + CGhoul2Info *g2 = &ghoul2[modelIndex]; + + if (g2->mGoreSetTag) + { + CGoreSet *goreSet = FindGoreSet(g2->mGoreSetTag); + + if (goreSet) + { + return goreSet->mGoreRecords.size(); + } + } + + return 0; +} + +void G2API_ClearSkinGore ( CGhoul2Info_v &ghoul2 ) +{ + for (int i = 0; i < ghoul2.size(); i++) + { + if (ghoul2[i].mGoreSetTag) + { + DeleteGoreSet(ghoul2[i].mGoreSetTag); + ghoul2[i].mGoreSetTag = 0; + } + } +} + +extern int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod); +void G2API_AddSkinGore(CGhoul2Info_v &ghoul2,SSkinGoreData &gore) +{ + if (VectorLength(gore.rayDirection)<.1f) + { + assert(0); // can't add gore without a shot direction + return; + } + + // make sure we have transformed the whole skeletons for each model + G2_ConstructGhoulSkeleton(ghoul2, gore.currentTime, true, gore.scale); + + // pre generate the world matrix - used to transform the incoming ray + G2_GenerateWorldMatrix(gore.angles, gore.position); + + // first up, translate the ray to model space + vec3_t transRayDirection, transHitLocation; + TransformAndTranslatePoint(gore.hitLocation, transHitLocation, &worldMatrixInv); + TransformPoint(gore.rayDirection, transRayDirection, &worldMatrixInv); + if (!gore.useTheta) + { + vec3_t t; + VectorCopy(gore.uaxis, t); + TransformPoint(t, gore.uaxis, &worldMatrixInv); + } + + int lod; + ResetGoreTag(); + const int lodbias = Com_Clamp(0, 2, G2_DecideTraceLod(ghoul2[0], r_lodbias->integer)); + const int maxLod = Com_Clamp(0, ghoul2[0].currentModel->numLods, 3); //limit to the number of lods the main model has + + for(lod = lodbias; lod < maxLod; lod++) + { + // now having done that, time to build the model + ri.GetG2VertSpaceServer()->ResetHeap(); + + G2_TransformModel(ghoul2, gore.currentTime, gore.scale, ri.GetG2VertSpaceServer(), lod, true, &gore); + + // now walk each model and compute new texture coordinates + G2_TraceModels(ghoul2, transHitLocation, transRayDirection, 0, gore.entNum, G2_NOCOLLIDE, lod, 1.0f, gore.SSize, gore.TSize, gore.theta, gore.shader, &gore, qtrue); + } +} +#else + +void G2API_ClearSkinGore(CGhoul2Info_v& ghoul2) {} +void G2API_AddSkinGore(CGhoul2Info_v& ghoul2, SSkinGoreData& gore) {} + +#endif + +qboolean G2_TestModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up +{ + G2ERROR(ghlInfo,"G2_TestModelPointers: NULL ghlInfo"); + if (!ghlInfo) + { + return qfalse; + } + ghlInfo->mValid=false; + if (ghlInfo->mModelindex != -1) + { + ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); + ghlInfo->currentModel = R_GetModelByHandle(ghlInfo->mModel); + if (ghlInfo->currentModel) + { + //Rend2 - Not sure why, but we need MOD_MDXM here to stop ghoul2 crashes. + if (ghlInfo->currentModel->type == MOD_MDXM && ghlInfo->currentModel->data.glm && ghlInfo->currentModel->data.glm->header) + { + mdxmHeader_t *mdxm = ghlInfo->currentModel->data.glm->header; + if (ghlInfo->currentModelSize) + { + if (ghlInfo->currentModelSize != mdxm->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + + ghlInfo->currentModelSize = mdxm->ofsEnd; + ghlInfo->animModel = R_GetModelByHandle(mdxm->animIndex + ghlInfo->animModelIndexOffset); + + if (ghlInfo->animModel) + { + ghlInfo->aHeader = ghlInfo->animModel->data.gla; + if (ghlInfo->aHeader) + { + if (ghlInfo->currentAnimModelSize) + { + if (ghlInfo->currentAnimModelSize!=ghlInfo->aHeader->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + ghlInfo->currentAnimModelSize=ghlInfo->aHeader->ofsEnd; + ghlInfo->mValid=true; + } + } + } + } + } + if (!ghlInfo->mValid) + { + ghlInfo->currentModel = NULL; + ghlInfo->currentModelSize = 0; + ghlInfo->animModel = NULL; + ghlInfo->currentAnimModelSize = 0; + ghlInfo->aHeader = NULL; + } + return (qboolean)ghlInfo->mValid; +} + +#ifdef G2_PERFORMANCE_ANALYSIS +#include "qcommon/timing.h" +extern timing_c G2PerformanceTimer_G2_SetupModelPointers; +extern int G2Time_G2_SetupModelPointers; +#endif + +qboolean G2_SetupModelPointers(CGhoul2Info *ghlInfo) // returns true if the model is properly set up +{ +#ifdef G2_PERFORMANCE_ANALYSIS + G2PerformanceTimer_G2_SetupModelPointers.Start(); +#endif + G2ERROR(ghlInfo,"G2_SetupModelPointers: NULL ghlInfo"); + if (!ghlInfo) + { + return qfalse; + } + + ghlInfo->mValid=false; + + if (ghlInfo->mModelindex != -1) + { + G2ERROR(ghlInfo->mFileName[0],"empty ghlInfo->mFileName"); + + // RJ - experimental optimization! + ghlInfo->mModel = RE_RegisterModel(ghlInfo->mFileName); + ghlInfo->currentModel = R_GetModelByHandle(ghlInfo->mModel); + + G2ERROR(ghlInfo->currentModel,va("NULL Model (glm) %s",ghlInfo->mFileName)); + if (ghlInfo->currentModel) + { + G2ERROR(ghlInfo->currentModel->modelData, va("Model has no mdxm (glm) %s", ghlInfo->mFileName)); + + if (ghlInfo->currentModel->data.glm && ghlInfo->currentModel->data.glm->header) + { + mdxmHeader_t *mdxm = ghlInfo->currentModel->data.glm->header; + if (ghlInfo->currentModelSize) + { + if (ghlInfo->currentModelSize!=mdxm->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + ghlInfo->currentModelSize=mdxm->ofsEnd; + G2ERROR(ghlInfo->currentModelSize, va("Zero sized Model? (glm) %s",ghlInfo->mFileName)); + + ghlInfo->animModel = R_GetModelByHandle(mdxm->animIndex + ghlInfo->animModelIndexOffset); + G2ERROR(ghlInfo->animModel, va("NULL Model (gla) %s",ghlInfo->mFileName)); + + if (ghlInfo->animModel) + { + ghlInfo->aHeader = ghlInfo->animModel->data.gla; + G2ERROR(ghlInfo->aHeader,va("Model has no mdxa (gla) %s",ghlInfo->mFileName)); + if (ghlInfo->aHeader) + { + if (ghlInfo->currentAnimModelSize) + { + if (ghlInfo->currentAnimModelSize!=ghlInfo->aHeader->ofsEnd) + { + Com_Error(ERR_DROP, "Ghoul2 model was reloaded and has changed, map must be restarted.\n"); + } + } + + ghlInfo->currentAnimModelSize = ghlInfo->aHeader->ofsEnd; + G2ERROR(ghlInfo->currentAnimModelSize, va("Zero sized Model? (gla) %s",ghlInfo->mFileName)); + ghlInfo->mValid = true; + } + } + } + } + } + if (!ghlInfo->mValid) + { + ghlInfo->currentModel = NULL; + ghlInfo->currentModelSize = 0; + ghlInfo->animModel = NULL; + ghlInfo->currentAnimModelSize = 0; + ghlInfo->aHeader = NULL; + } + +#ifdef G2_PERFORMANCE_ANALYSIS + G2Time_G2_SetupModelPointers += G2PerformanceTimer_G2_SetupModelPointers.End(); +#endif + return (qboolean)ghlInfo->mValid; +} + +qboolean G2_SetupModelPointers(CGhoul2Info_v &ghoul2) // returns true if any model is properly set up +{ + qboolean ret = qfalse; + + for (int i=0; imValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) + +//===================================================================================================================== +// Bolt List handling routines - so entities can attach themselves to any part of the model in question + +// Given a bone number, see if that bone is already in our bone list +int G2_Find_Bolt_Bone_Num(boltInfo_v &bltlist, const int boneNum) +{ + if (boneNum == -1) + { + return -1; + } + + // look through entire list + for(size_t i=0; imValid); + boltInfo_t tempBolt; + + assert(surfNum >= 0 && surfNum < (int)slist.size()); + // first up, make sure have a surface first + if (surfNum >= (int)slist.size()) + { + return -1; + } + + // look through entire list - see if it's already there first + for(size_t i=0; imValid); + model_t* mod_m = (model_t*)ghlInfo->currentModel; + model_t* mod_a = (model_t*)ghlInfo->animModel; + int x, surfNum = -1; + mdxaSkel_t* skel; + mdxaSkelOffsets_t* offsets; + boltInfo_t tempBolt; + uint32_t flags; + + // first up, we'll search for that which this bolt names in all the surfaces + surfNum = G2_IsSurfaceLegal(mod_m, boneName, &flags); + + // did we find it as a surface? + if (surfNum != -1) + { + // look through entire list - see if it's already there first + for(size_t i=0; idata.gla; + offsets = (mdxaSkelOffsets_t *)((byte *)mdxa + sizeof(mdxaHeader_t)); + + // walk the entire list of bones in the gla file for this model and see if any match the name of the bone we want to find + for (x=0; x< mdxa->numBones; x++) + { + skel = (mdxaSkel_t *)((byte *)mdxa + sizeof(mdxaHeader_t) + offsets->offsets[x]); + // if name is the same, we found it + if (!Q_stricmp(skel->name, boneName)) + { + break; + } + } + + // check to see we did actually make a match with a bone in the model + if (x == mdxa->numBones) + { + // didn't find it? Error + //assert(0&&x == mod_a->mdxa->numBones); +#ifdef _DEBUG +// Com_Printf("WARNING: %s not found on skeleton\n", boneName); +#endif + return -1; + } + + // look through entire list - see if it's already there first + for(size_t i=0; i- 1; i--) + { + if ((bltlist[i].surfaceNumber == -1) && (bltlist[i].boneNumber == -1)) + { + newSize = i; + } + // once we hit one that isn't a -1, we are done. + else + { + break; + } + } + // do we need to resize? + if (newSize != bltlist.size()) + { + // yes, so lets do it + bltlist.resize(newSize); + } + + } + return qtrue; + } + + assert(0); + + // no + return qfalse; +} + +// set the bolt list to all unused so the bone transformation routine ignores it. +void G2_Init_Bolt_List(boltInfo_v &bltlist) +{ + bltlist.clear(); +} + +// remove any bolts that reference original surfaces, generated surfaces, or bones that aren't active anymore +void G2_RemoveRedundantBolts(boltInfo_v &bltlist, surfaceInfo_v &slist, int *activeSurfaces, int *activeBones) +{ + // walk the bolt list + for (size_t i=0; i +#include "ghoul2/ghoul2_gore.h" +//rww - RAGDOLL_END + +extern mdxaBone_t worldMatrix; +extern mdxaBone_t worldMatrixInv; + +////////////////////////////////// + +//rww - RAGDOLL_BEGIN +#ifndef __linux__ + #include +#else + #include +#endif + +#include "client/client.h" //while this is all "shared" code, there are some places where we want to make cgame callbacks (for ragdoll) only if the cgvm exists +//rww - RAGDOLL_END + +#define RAG_PCJ (0x00001) +#define RAG_PCJ_POST_MULT (0x00002) // has the pcj flag as well +#define RAG_PCJ_MODEL_ROOT (0x00004) // has the pcj flag as well +#define RAG_PCJ_PELVIS (0x00008) // has the pcj flag and POST_MULT as well +#define RAG_EFFECTOR (0x00100) +#define RAG_WAS_NOT_RENDERED (0x01000) // not particularily reliable, more of a hint +#define RAG_WAS_EVER_RENDERED (0x02000) // not particularily reliable, more of a hint +#define RAG_BONE_LIGHTWEIGHT (0x04000) //used to indicate a bone's velocity treatment +#define RAG_PCJ_IK_CONTROLLED (0x08000) //controlled from IK move input +#define RAG_UNSNAPPABLE (0x10000) //cannot be broken out of constraints ever + +// thiese flags are on the model and correspond to... +//#define GHOUL2_RESERVED_FOR_RAGDOLL 0x0ff0 // these are not defined here for dependecies sake +#define GHOUL2_RAG_STARTED 0x0010 // we are actually a ragdoll +#define GHOUL2_RAG_PENDING 0x0100 // got start death anim but not end death anim +#define GHOUL2_RAG_DONE 0x0200 // got end death anim +#define GHOUL2_RAG_COLLISION_DURING_DEATH 0x0400 // ever have gotten a collision (da) event +#define GHOUL2_RAG_COLLISION_SLIDE 0x0800 // ever have gotten a collision (slide) event +#define GHOUL2_RAG_FORCESOLVE 0x1000 //api-override, determine if ragdoll should be forced to continue solving even if it thinks it is settled + +// functions predefinition +static void G2_DoIK(CGhoul2Info_v& ghoul2V, int g2Index, CRagDollUpdateParams* params); +static void G2_RagDoll(CGhoul2Info_v& ghoul2V, int g2Index, CRagDollUpdateParams* params, int curTime); + +//===================================================================================================================== +// Bone List handling routines - so entities can override bone info on a bone by bone level, and also interrogate this info + +// Given a bone name, see if that bone is already in our bone list - note the model_t pointer that gets passed in here MUST point at the +// gla file, not the glm file type. +int G2_Find_Bone(CGhoul2Info* ghlInfo, boneInfo_v& blist, const char* boneName) +{ + mdxaSkel_t* skel; + mdxaSkelOffsets_t* offsets; + offsets = (mdxaSkelOffsets_t*)((byte*)ghlInfo->aHeader + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t*)((byte*)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]); + + // look through entire list + for (size_t i = 0; i < blist.size(); i++) + { + // if this bone entry has no info in it, bounce over it + if (blist[i].boneNumber == -1) + { + continue; + } + + // figure out what skeletal info structure this bone entry is looking at + skel = (mdxaSkel_t*)((byte*)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]); + + // if name is the same, we found it + if (!Q_stricmp(skel->name, boneName)) + { + return i; + } + } + + // didn't find it + return -1; +} + +// Given a bone name, see if that bone is already in our bone list - note the model_t pointer that gets passed in here MUST point at the +// gla file, not the glm file type. +int G2_Find_Bone(const model_t *mod, boneInfo_v &blist, const char *boneName) +{ + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + mdxaHeader_t *mdxa = mod->data.gla; + offsets = (mdxaSkelOffsets_t *)((byte *)mdxa + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)mdxa + sizeof(mdxaHeader_t) + offsets->offsets[0]); + + // look through entire list + for(size_t i=0; ioffsets[blist[i].boneNumber]); + + // if name is the same, we found it + if (!Q_stricmp(skel->name, boneName)) + { + return i; + } + } + + // didn't find it + return -1; +} + +// we need to add a bone to the list - find a free one and see if we can find a corresponding bone in the gla file +int G2_Add_Bone (const model_t *mod, boneInfo_v &blist, const char *boneName) +{ + int x; + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + boneInfo_t tempBone; + mdxaHeader_t *mdxa = mod->data.gla; + + //rww - RAGDOLL_BEGIN + memset(&tempBone, 0, sizeof(tempBone)); + //rww - RAGDOLL_END + + offsets = (mdxaSkelOffsets_t *)((byte *)mdxa + sizeof(mdxaHeader_t)); + + // walk the entire list of bones in the gla file for this model and see if any match the name of the bone we want to find + for (x=0; x< mdxa->numBones; x++) + { + skel = (mdxaSkel_t *)((byte *)mdxa + sizeof(mdxaHeader_t) + offsets->offsets[x]); + // if name is the same, we found it + if (!Q_stricmp(skel->name, boneName)) + { + break; + } + } + + // check to see we did actually make a match with a bone in the model + if (x == mdxa->numBones) + { + // didn't find it? Error + //assert(0); +#ifdef _DEBUG + Com_Printf("WARNING: Failed to add bone %s\n", boneName); +#endif + +#ifdef _RAG_PRINT_TEST + Com_Printf("WARNING: Failed to add bone %s\n", boneName); +#endif + return -1; + } + + // look through entire list - see if it's already there first + for(size_t i=0; ioffsets[blist[i].boneNumber]); + // if name is the same, we found it + if (!Q_stricmp(skel->name, boneName)) + { + return i; + } + } + else + { + // if we found an entry that had a -1 for the bonenumber, then we hit a bone slot that was empty + blist[i].boneNumber = x; + blist[i].flags = 0; + return i; + } + } + +#ifdef _RAG_PRINT_TEST + Com_Printf("New bone added for %s\n", boneName); +#endif + // ok, we didn't find an existing bone of that name, or an empty slot. Lets add an entry + tempBone.boneNumber = x; + tempBone.flags = 0; + blist.push_back(tempBone); + return blist.size()-1; +} + + +// Given a model handle, and a bone name, we want to remove this bone from the bone override list +qboolean G2_Remove_Bone_Index ( boneInfo_v &blist, int index) +{ + /* + if (index != -1) + { + if (blist[index].flags & BONE_ANGLES_RAGDOLL) + { + return qtrue; // don't accept any calls on ragdoll bones + } + } + */ + + // did we find it? + if (index != -1) + { + // check the flags first - if it's still being used Do NOT remove it + if (!blist[index].flags) + { + + // set this bone to not used + blist[index].boneNumber = -1; + + unsigned int newSize = blist.size(); + // now look through the list from the back and see if there is a block of -1's we can resize off the end of the list + for (int i=blist.size()-1; i>-1; i--) + { + if (blist[i].boneNumber == -1) + { + newSize = i; + } + // once we hit one that isn't a -1, we are done. + else + { + break; + } + } + // do we need to resize? + if (newSize != blist.size()) + { + // yes, so lets do it + blist.resize(newSize); + } + + return qtrue; + } + } + +// assert(0); + // no + return qfalse; +} + +// given a bone number, see if there is an override bone in the bone list +int G2_Find_Bone_In_List(boneInfo_v &blist, const int boneNum) +{ + // look through entire list + for(size_t i=0; idata.gla; + offsets = (mdxaSkelOffsets_t *)((byte *)mdxa + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)mdxa + sizeof(mdxaHeader_t) + offsets->offsets[blist[index].boneNumber]); + + Mat3x4_Multiply(&temp1, boneOverride,&skel->BasePoseMatInv); + Mat3x4_Multiply(boneOverride,&skel->BasePoseMat, &temp1); + + } + else + { + VectorCopy(angles, newAngles); + + // why I should need do this Fuck alone knows. But I do. + if (left == POSITIVE_Y) + { + newAngles[0] +=180; + } + + Create_Matrix(newAngles, &temp1); + + permutation.matrix[0][0] = permutation.matrix[0][1] = permutation.matrix[0][2] = permutation.matrix[0][3] = 0; + permutation.matrix[1][0] = permutation.matrix[1][1] = permutation.matrix[1][2] = permutation.matrix[1][3] = 0; + permutation.matrix[2][0] = permutation.matrix[2][1] = permutation.matrix[2][2] = permutation.matrix[2][3] = 0; + + // determine what axis newAngles Yaw should revolve around + switch (forward) + { + case NEGATIVE_X: + permutation.matrix[0][0] = -1; // works + break; + case POSITIVE_X: + permutation.matrix[0][0] = 1; // works + break; + case NEGATIVE_Y: + permutation.matrix[1][0] = -1; + break; + case POSITIVE_Y: + permutation.matrix[1][0] = 1; + break; + case NEGATIVE_Z: + permutation.matrix[2][0] = -1; + break; + case POSITIVE_Z: + permutation.matrix[2][0] = 1; + break; + default: + break; + } + + // determine what axis newAngles pitch should revolve around + switch (left) + { + case NEGATIVE_X: + permutation.matrix[0][1] = -1; + break; + case POSITIVE_X: + permutation.matrix[0][1] = 1; + break; + case NEGATIVE_Y: + permutation.matrix[1][1] = -1; // works + break; + case POSITIVE_Y: + permutation.matrix[1][1] = 1; // works + break; + case NEGATIVE_Z: + permutation.matrix[2][1] = -1; + break; + case POSITIVE_Z: + permutation.matrix[2][1] = 1; + break; + default: + break; + } + + // determine what axis newAngles Roll should revolve around + switch (up) + { + case NEGATIVE_X: + permutation.matrix[0][2] = -1; + break; + case POSITIVE_X: + permutation.matrix[0][2] = 1; + break; + case NEGATIVE_Y: + permutation.matrix[1][2] = -1; + break; + case POSITIVE_Y: + permutation.matrix[1][2] = 1; + break; + case NEGATIVE_Z: + permutation.matrix[2][2] = -1; // works + break; + case POSITIVE_Z: + permutation.matrix[2][2] = 1; // works + break; + default: + break; + } + + Mat3x4_Multiply(boneOverride, &temp1,&permutation); + + } + + // keep a copy of the matrix in the newmatrix which is actually what we use + memcpy(&blist[index].newMatrix, &blist[index].matrix, sizeof(mdxaBone_t)); + +} + +//========================================================================================= +//// Public Bone Routines + + +// Given a model handle, and a bone name, we want to remove this bone from the bone override list +qboolean G2_Remove_Bone (CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName) +{ + assert(ghlInfo->animModel); + int index = G2_Find_Bone(ghlInfo->animModel, blist, boneName); + if (index == -1) + { + return qfalse; + } + + return G2_Remove_Bone_Index(blist, index); +} + +#define DEBUG_PCJ (0) + + +// Given a model handle, and a bone name, we want to set angles specifically +// for overriding +qboolean G2_Set_Bone_Angles_Index(CGhoul2Info* ghlInfo, boneInfo_v& blist, const int index, + const float* angles, const int flags, const Eorientations yaw, + const Eorientations pitch, const Eorientations roll, + const int blendTime, const int currentTime) +{ + if (index < 0 || (index >= (int)blist.size()) || (blist[index].boneNumber == -1)) + { + return qfalse; + } + + // yes, so set the angles and flags correctly + blist[index].flags &= ~BONE_ANGLES_TOTAL; + blist[index].flags |= flags; + blist[index].boneBlendStart = currentTime; + blist[index].boneBlendTime = blendTime; + +#if DEBUG_PCJ + OutputDebugString(va("%8x %2d %6d (%6.2f,%6.2f,%6.2f) %d %d %d %d\n", (int)ghlInfo, index, currentTime, angles[0], angles[1], angles[2], yaw, pitch, roll, flags)); +#endif + G2_Generate_Matrix(ghlInfo->animModel, blist, index, angles, flags, yaw, pitch, roll); + return qtrue; +} + +// Given a model handle, and a bone name, we want to set angles specifically for overriding +qboolean G2_Set_Bone_Angles(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const float *angles, + const int flags, const Eorientations up, const Eorientations left, const Eorientations forward, + const int blendTime, const int currentTime) +{ + model_t* mod_a = (model_t *)ghlInfo->animModel; + + int index = G2_Find_Bone(mod_a, blist, boneName); + if (index == -1) + { + index = G2_Add_Bone(mod_a, blist, boneName); + } + + // did we find it? + if (index != -1) + { + if (blist[index].flags & BONE_ANGLES_RAGDOLL) + { + return qtrue; // don't accept any calls on ragdoll bones + } + + // yes, so set the angles and flags correctly + blist[index].flags &= ~(BONE_ANGLES_TOTAL); + blist[index].flags |= flags; + blist[index].boneBlendStart = currentTime; + blist[index].boneBlendTime = blendTime; +#if DEBUG_PCJ + Com_OPrintf("%2d %6d (%6.2f,%6.2f,%6.2f) %d %d %d %d\n",index,currentTime,angles[0],angles[1],angles[2],up,left,forward,flags); +#endif + + G2_Generate_Matrix(mod_a, blist, index, angles, flags, up, left, forward); + return qtrue; + } + + // failed to add bone + return qfalse; +} + +// Given a model handle, and a bone name, we want to set angles specifically for overriding - using a matrix directly +qboolean G2_Set_Bone_Angles_Matrix_Index(boneInfo_v& blist, const int index, + const mdxaBone_t& matrix, const int flags, + const int blendTime, const int currentTime) +{ + + if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1)) + { + // we are attempting to set a bone override that doesn't exist + assert(0); + return qfalse; + } + + if (index != -1 && (blist[index].flags & BONE_ANGLES_RAGDOLL)) + { + return qtrue; // don't accept any calls on ragdoll bones + } + // yes, so set the angles and flags correctly + blist[index].flags &= ~(BONE_ANGLES_TOTAL); + blist[index].flags |= flags; + blist[index].boneBlendStart = currentTime; + blist[index].boneBlendTime = blendTime; + + memcpy(&blist[index].matrix, &matrix, sizeof(mdxaBone_t)); + memcpy(&blist[index].newMatrix, &matrix, sizeof(mdxaBone_t)); + return qtrue; + +} + +// Given a model handle, and a bone name, we want to set angles specifically for overriding - using a matrix directly +qboolean G2_Set_Bone_Angles_Matrix(CGhoul2Info* ghlInfo, boneInfo_v& blist, const char* boneName, const mdxaBone_t& matrix, + const int flags, const int blendTime, const int currentTime) +{ + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index == -1) + { + index = G2_Add_Bone(ghlInfo->animModel, blist, boneName); + } + + // did we find it? + if (index != -1) + { + // yes, so set the angles and flags correctly + blist[index].flags &= ~(BONE_ANGLES_TOTAL); + blist[index].flags |= flags; + + memcpy(&blist[index].matrix, &matrix, sizeof(mdxaBone_t)); + memcpy(&blist[index].newMatrix, &matrix, sizeof(mdxaBone_t)); + return qtrue; + } + return qfalse; +} + +#define DEBUG_G2_TIMING (0) + +// given a model, bone name, a bonelist, a start/end frame number, a anim speed and some anim flags, set up or modify an existing bone entry for a new set of anims +qboolean G2_Set_Bone_Anim_Index( + boneInfo_v &blist, const int index, const int startFrame, const int endFrame, const int flags, const float animSpeed, + const int currentTime, const float setFrame, const int blendTime, const int numFrames) +{ + int modFlags = flags; + + if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1)) + { + // we are attempting to set a bone override that doesn't exist + assert(0); + return qfalse; + } + + // sanity check to see if setfram is within animation bounds + assert((setFrame == -1) || ((setFrame >= startFrame) && (setFrame < endFrame)) || ((setFrame > endFrame) && (setFrame <= (startFrame + 1)))); + + if (modFlags & BONE_ANIM_BLEND) + { + float currentFrame, animSpeed; + int startFrame, endFrame, flags; + // figure out where we are now + if (G2_Get_Bone_Anim_Index(blist, index, currentTime, ¤tFrame, &startFrame, &endFrame, &flags, &animSpeed, numFrames)) + { + if (blist[index].blendStart == currentTime) //we're replacing a blend in progress which hasn't started + { + // set the amount of time it's going to take to blend this anim with the last frame of the last one + blist[index].blendTime = blendTime; + } + else + { + if (animSpeed<0.0f) + { + blist[index].blendFrame = floor(currentFrame); + blist[index].blendLerpFrame = floor(currentFrame); + } + else + { + blist[index].blendFrame = currentFrame; + blist[index].blendLerpFrame = currentFrame+1; + + // cope with if the lerp frame is actually off the end of the anim + if (blist[index].blendFrame >= endFrame ) + { + // we only want to lerp with the first frame of the anim if we are looping + if (blist[index].flags & BONE_ANIM_OVERRIDE_LOOP) + { + blist[index].blendFrame = startFrame; + } + // if we intend to end this anim or freeze after this, then just keep on the last frame + else + { + // assert(endFrame>0); + if (endFrame <= 0) + { + blist[index].blendLerpFrame = 0; + } + else + { + blist[index].blendFrame = endFrame -1; + } + } + } + + // cope with if the lerp frame is actually off the end of the anim + if (blist[index].blendLerpFrame >= endFrame ) + { + // we only want to lerp with the first frame of the anim if we are looping + if (blist[index].flags & BONE_ANIM_OVERRIDE_LOOP) + { + blist[index].blendLerpFrame = startFrame; + } + // if we intend to end this anim or freeze after this, then just keep on the last frame + else + { + // assert(endFrame>0); + if (endFrame <= 0) + { + blist[index].blendLerpFrame = 0; + } + else + { + blist[index].blendLerpFrame = endFrame - 1; + } + } + } + } + // set the amount of time it's going to take to blend this anim with the last frame of the last one + blist[index].blendTime = blendTime; + blist[index].blendStart = currentTime; + + } + } + // hmm, we weren't animating on this bone. In which case disable the blend + else + { + blist[index].blendFrame = blist[index].blendLerpFrame = 0; + blist[index].blendTime = 0; + modFlags &= ~(BONE_ANIM_BLEND); + } + } + else + { + blist[index].blendFrame = blist[index].blendLerpFrame = 0; + blist[index].blendTime = blist[index].blendStart = 0; + // we aren't blending, so remove the option to do so + modFlags &= ~BONE_ANIM_BLEND; + } + // yes, so set the anim data and flags correctly + blist[index].endFrame = endFrame; + blist[index].startFrame = startFrame; + blist[index].animSpeed = animSpeed; + blist[index].pauseTime = 0; + + assert(blist[index].blendFrame >= 0 && blist[index].blendFrame < numFrames); + assert(blist[index].blendLerpFrame >= 0 && blist[index].blendLerpFrame < numFrames); + // start up the animation:) + if (setFrame != -1) + { + blist[index].startTime = (currentTime - (((setFrame - (float)startFrame) * 50.0) / animSpeed)); + } + else + { + blist[index].startTime = currentTime; + } + blist[index].flags &= ~(BONE_ANIM_TOTAL); + blist[index].flags |= modFlags; + +#if DEBUG_G2_TIMING + if (index==2) + { + const boneInfo_t &bone=blist[index]; + char mess[1000]; + if (bone.flags&BONE_ANIM_BLEND) + { + sprintf(mess,"sab[%2d] %5d %5d (%5d-%5d) %4.2f %4x bt(%5d-%5d) %7.2f %5d\n", + index, + currentTime, + bone.startTime, + bone.startFrame, + bone.endFrame, + bone.animSpeed, + bone.flags, + bone.blendStart, + bone.blendStart+bone.blendTime, + bone.blendFrame, + bone.blendLerpFrame + ); + } + else + { + sprintf(mess,"saa[%2d] %5d %5d (%5d-%5d) %4.2f %4x\n", + index, + currentTime, + bone.startTime, + bone.startFrame, + bone.endFrame, + bone.animSpeed, + bone.flags + ); + } + Com_OPrintf("%s",mess); + } +#endif + + return qtrue; + +} + +// given a model, bone name, a bonelist, a start/end frame number, a anim speed and some anim flags, set up or modify an existing bone entry for a new set of anims +qboolean G2_Set_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int startFrame, const int endFrame, + const int flags, const float animSpeed, const int currentTime, const float setFrame, const int blendTime) +{ + model_t* mod_a = (model_t *)ghlInfo->animModel; + int modFlags = flags; + int index = G2_Find_Bone(mod_a, blist, boneName); + + // did we find it? + if (index != -1) + { + return G2_Set_Bone_Anim_Index(blist, index, startFrame, endFrame, modFlags, animSpeed, currentTime, setFrame, blendTime, ghlInfo->aHeader->numFrames); + } + + // no - lets try and add this bone in + index = G2_Add_Bone(ghlInfo->animModel, blist, boneName); + + // did we find a free one? + if (index != -1) + { + blist[index].blendFrame = blist[index].blendLerpFrame = 0; + blist[index].blendTime = 0; + // we aren't blending, so remove the option to do so + modFlags &= ~BONE_ANIM_BLEND; + // yes, so set the anim data and flags correctly + blist[index].endFrame = endFrame; + blist[index].startFrame = startFrame; + blist[index].animSpeed = animSpeed; + blist[index].pauseTime = 0; + // start up the animation:) + if (setFrame != -1) + { + blist[index].startTime = (currentTime - (((setFrame - (float)startFrame) * 50.0) / animSpeed)); + } + else + { + blist[index].startTime = currentTime; + } + blist[index].flags &= ~BONE_ANIM_TOTAL; + blist[index].flags |= modFlags; + assert(blist[index].blendFrame >= 0 && blist[index].blendFrame < ghlInfo->aHeader->numFrames); + assert(blist[index].blendLerpFrame >= 0 && blist[index].blendLerpFrame < ghlInfo->aHeader->numFrames); + return qtrue; + } + + return qfalse; +} + +qboolean G2_Get_Bone_Anim_Range_Index(boneInfo_v& blist, const int boneIndex, int* startFrame, int* endFrame) +{ + if (boneIndex != -1) + { + assert(boneIndex >= 0 && boneIndex < (int)blist.size()); + // are we an animating bone? + if (blist[boneIndex].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE)) + { + *startFrame = blist[boneIndex].startFrame; + *endFrame = blist[boneIndex].endFrame; + return qtrue; + } + } + return qfalse; +} + +qboolean G2_Get_Bone_Anim_Range(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, int *startFrame, int *endFrame) +{ + model_t *mod_a = (model_t *)ghlInfo->animModel; + int index = G2_Find_Bone(mod_a, blist, boneName); + + // did we find it? + if (index != -1) + { + // are we an animating bone? + if (blist[index].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE)) + { + *startFrame = blist[index].startFrame; + *endFrame = blist[index].endFrame; + return qtrue; + } + } + return qfalse; +} + +// given a model, bonelist and bonename, return the current frame, startframe and endframe of the current animation +// NOTE if we aren't running an animation, then qfalse is returned +void G2_TimingModel(boneInfo_t &bone,int currentTime,int numFramesInFile,int ¤tFrame,int &newFrame,float &lerp); + +qboolean G2_Get_Bone_Anim_Index(boneInfo_v& blist, const int index, const int currentTime, + float* currentFrame, int* startFrame, int* endFrame, int* flags, float* retAnimSpeed, int numFrames) +{ + + // did we find it? + if ((index>=0) && !((index >= (int)blist.size()) || (blist[index].boneNumber == -1))) + { + + // are we an animating bone? + if (blist[index].flags & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE)) + { + int lcurrentFrame,newFrame; + float lerp; + G2_TimingModel(blist[index],currentTime,numFrames,lcurrentFrame,newFrame,lerp); + + if (currentFrame) *currentFrame =float(lcurrentFrame)+lerp; + if (startFrame) *startFrame = blist[index].startFrame; + if (endFrame) *endFrame = blist[index].endFrame; + if (flags) *flags = blist[index].flags; + if (retAnimSpeed) *retAnimSpeed = blist[index].animSpeed; + return qtrue; + } + } + + if (startFrame) *startFrame=0; + if (endFrame) *endFrame=1; + if (currentFrame) *currentFrame=0.0f; + if (flags) *flags=0; + if (retAnimSpeed) *retAnimSpeed=0.0f; + return qfalse; +} + +// given a model, bonelist and bonename, return the current frame, startframe and endframe of the current animation +// NOTE if we aren't running an animation, then qfalse is returned +qboolean G2_Get_Bone_Anim(CGhoul2Info* ghlInfo, boneInfo_v& blist, const char* boneName, const int currentTime, + float* currentFrame, int* startFrame, int* endFrame, int* flags, float* retAnimSpeed) +{ + model_t* mod_a = (model_t *)ghlInfo->animModel; + int index = G2_Find_Bone(mod_a, blist, boneName); + + if (index==-1) + { + index = G2_Add_Bone(mod_a, blist, boneName); + + if (index == -1) + { + return qfalse; + } + } + + assert(ghlInfo->aHeader); + + if (G2_Get_Bone_Anim_Index(blist, index, currentTime, currentFrame, startFrame, endFrame, flags, retAnimSpeed, ghlInfo->aHeader->numFrames)) + { + assert(*startFrame>=0&&*startFrameaHeader->numFrames); + assert(*endFrame>0&&*endFrame<=ghlInfo->aHeader->numFrames); + assert(*currentFrame>=0.0f&&((int)(*currentFrame))aHeader->numFrames); + return qtrue; + } + + return qfalse; +} + +// given a model, bonelist and bonename, lets pause an anim if it's playing. +qboolean G2_Pause_Bone_Anim_Index(boneInfo_v& blist, const int boneIndex, const int currentTime, int numFrames) +{ + if (boneIndex >= 0 && boneIndex < (int)blist.size()) + { + // are we pausing or un pausing? + if (blist[boneIndex].pauseTime) + { + int startFrame, endFrame, flags; + float currentFrame, animSpeed; + + // figure out what frame we are on now + if (G2_Get_Bone_Anim_Index(blist, boneIndex, blist[boneIndex].pauseTime, ¤tFrame, &startFrame, &endFrame, &flags, &animSpeed, numFrames)) + { + // reset start time so we are actually on this frame right now + G2_Set_Bone_Anim_Index(blist, boneIndex, startFrame, endFrame, flags, animSpeed, currentTime, currentFrame, 0, numFrames); + // no pausing anymore + blist[boneIndex].pauseTime = 0; + } + else + { + return qfalse; + } + } + // ahh, just pausing, the easy bit + else + { + blist[boneIndex].pauseTime = currentTime; + } + + return qtrue; + } + assert(0); + + return qfalse; +} + +// given a model, bonelist and bonename, lets pause an anim if it's playing. +qboolean G2_Pause_Bone_Anim(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName, const int currentTime) +{ + model_t* mod_a = (model_t *)ghlInfo->animModel; + int index = G2_Find_Bone(mod_a, blist, boneName); + + // did we find it? + if (index != -1) + { + return (G2_Pause_Bone_Anim_Index(blist, index, currentTime, ghlInfo->aHeader->numFrames)); + } + return qfalse; +} + +qboolean G2_IsPaused(CGhoul2Info* ghlInfo, boneInfo_v& blist, const char* boneName) +{ + int index = G2_Find_Bone(ghlInfo, blist, boneName); + if (index != -1) + { + // are we paused? + if (blist[index].pauseTime) + { + // yup. paused. + return qtrue; + } + return qfalse; + } + + return qfalse; +} + +// given a model, bonelist and bonename, lets stop an anim if it's playing. +qboolean G2_Stop_Bone_Anim_Index(boneInfo_v &blist, const int index) +{ + + if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1)) + { + // we are attempting to set a bone override that doesn't exist + return qfalse; + } + + blist[index].flags &= ~(BONE_ANIM_TOTAL); + // try and remove this bone if we can + return G2_Remove_Bone_Index(blist, index); +} + +// given a model, bonelist and bonename, lets stop an anim if it's playing. +qboolean G2_Stop_Bone_Anim(CGhoul2Info* ghlInfo, boneInfo_v& blist, const char* boneName) +{ + int index = G2_Find_Bone(ghlInfo, blist, boneName); + + // did we find it? + if (index != -1) + { + blist[index].flags &= ~(BONE_ANIM_TOTAL); + // try and remove this bone if we can + return G2_Remove_Bone_Index(blist, index); + } + assert(0); + + return qfalse; +} + +// given a model, bonelist and bonename, lets stop an anim if it's playing. +qboolean G2_Stop_Bone_Angles_Index(boneInfo_v &blist, const int index) +{ + + if ((index >= (int)blist.size()) || (blist[index].boneNumber == -1)) + { + // we are attempting to set a bone override that doesn't exist + assert(0); + return qfalse; + } + + blist[index].flags &= ~(BONE_ANGLES_TOTAL); + // try and remove this bone if we can + return G2_Remove_Bone_Index(blist, index); + +} + +// given a model, bonelist and bonename, lets stop an anim if it's playing. +qboolean G2_Stop_Bone_Angles(CGhoul2Info* ghlInfo, boneInfo_v& blist, const char* boneName) +{ + int index = G2_Find_Bone(ghlInfo, blist, boneName); + + // did we find it? + if (index != -1) + { + blist[index].flags &= ~(BONE_ANGLES_TOTAL); + // try and remove this bone if we can + return G2_Remove_Bone_Index(blist, index); + } + assert(0); + + return qfalse; +} + + +// actually walk the bone list and update each and every bone if we have ended an animation for them. + +void G2_Animate_Bone_List(CGhoul2Info_v &ghoul2, const int currentTime, const int index, CRagDollUpdateParams* params) +{ + bool anyRagDoll = false; + bool anyIK = false; + for (size_t i = 0; i < ghoul2[index].mBlist.size(); i++) + { + if (ghoul2[index].mBlist[i].boneNumber != -1) + { + if (ghoul2[index].mBlist[i].flags & BONE_ANGLES_RAGDOLL) + { + if (ghoul2[index].mBlist[i].RagFlags & RAG_PCJ_IK_CONTROLLED) + { + anyIK = true; + } + anyRagDoll = true; + + if (anyIK && anyRagDoll) + { + break; + } + } + } + } + if (!index && params) + { + if (anyIK) + { + //we use ragdoll params so we know what our current position, etc. is. + G2_DoIK(ghoul2, 0, params); + } + else + { + G2_RagDoll(ghoul2, 0, params, currentTime); + } + } +} + +//rww - RAGDOLL_BEGIN +/* + + + rag stuff + +*/ +static void G2_RagDollSolve(CGhoul2Info_v &ghoul2V,int g2Index,float decay,int frameNum,const vec3_t currentOrg,bool LimitAngles,CRagDollUpdateParams *params = NULL); +static void G2_RagDollCurrentPosition(CGhoul2Info_v &ghoul2V,int g2Index,int frameNum,const vec3_t angles,const vec3_t position,const vec3_t scale); +static bool G2_RagDollSettlePositionNumeroTrois(CGhoul2Info_v &ghoul2V,const vec3_t currentOrg,CRagDollUpdateParams *params, int curTime); +static bool G2_RagDollSetup(CGhoul2Info &ghoul2,int frameNum,bool resetOrigin,const vec3_t origin,bool anyRendered); + +void G2_GetBoneBasepose(CGhoul2Info &ghoul2,int boneNum,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv); +int G2_GetBoneDependents(CGhoul2Info &ghoul2,int boneNum,int *tempDependents,int maxDep); +void G2_GetBoneMatrixLow(CGhoul2Info &ghoul2,int boneNum,const vec3_t scale,mdxaBone_t &retMatrix,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv); +int G2_GetParentBoneMatrixLow(CGhoul2Info &ghoul2,int boneNum,const vec3_t scale,mdxaBone_t &retMatrix,mdxaBone_t *&retBasepose,mdxaBone_t *&retBaseposeInv); +bool G2_WasBoneRendered(CGhoul2Info &ghoul2,int boneNum); + +#define MAX_BONES_RAG (256) + +struct SRagEffector +{ + vec3_t currentOrigin; + vec3_t desiredDirection; + vec3_t desiredOrigin; + float radius; + float weight; +}; + +#define RAG_MASK (CONTENTS_SOLID|CONTENTS_TERRAIN)//|CONTENTS_SHOTCLIP|CONTENTS_TERRAIN//(/*MASK_SOLID|*/CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_SHOTCLIP|CONTENTS_TERRAIN|CONTENTS_BODY) + +extern cvar_t *broadsword; +extern cvar_t *broadsword_kickbones; +extern cvar_t *broadsword_kickorigin; +extern cvar_t *broadsword_dontstopanim; +extern cvar_t *broadsword_waitforshot; +extern cvar_t *broadsword_playflop; + +extern cvar_t *broadsword_effcorr; + +extern cvar_t *broadsword_ragtobase; + +extern cvar_t *broadsword_dircap; + +extern cvar_t *broadsword_extra1; +extern cvar_t *broadsword_extra2; + +//#define flrand Q_flrand + +static mdxaBone_t* ragBasepose[MAX_BONES_RAG]; +static mdxaBone_t* ragBaseposeInv[MAX_BONES_RAG]; +static mdxaBone_t ragBones[MAX_BONES_RAG]; +static SRagEffector ragEffectors[MAX_BONES_RAG]; +static boneInfo_t *ragBoneData[MAX_BONES_RAG]; +static int tempDependents[MAX_BONES_RAG]; +static int ragBlistIndex[MAX_BONES_RAG]; +static int numRags; +static vec3_t ragBoneMins; +static vec3_t ragBoneMaxs; +static vec3_t ragBoneCM; +static bool haveDesiredPelvisOffset=false; +static vec3_t desiredPelvisOffset; // this is for the root +static float ragOriginChange=0.0f; +static vec3_t ragOriginChangeDir; +//debug +#if 0 +static vec3_t handPos={0,0,0}; +static vec3_t handPos2={0,0,0}; +#endif + +enum ERagState +{ + ERS_DYNAMIC, + ERS_SETTLING, + ERS_SETTLED +}; +static int ragState; + +static std::vector rag; // once we get the dependents precomputed this can be local + + +static void G2_Generate_MatrixRag( + // caution this must not be called before the whole skeleton is "remembered" + boneInfo_v &blist, + int index) +{ + + + boneInfo_t &bone=blist[index];//.sent; + + memcpy(&bone.matrix,&bone.ragOverrideMatrix, sizeof(mdxaBone_t)); +#ifdef _DEBUG + int i,j; + for (i = 0; i < 3; i++ ) + { + for (j = 0; j < 4; j++ ) + { + assert( !Q_isnan(bone.matrix.matrix[i][j])); + } + } +#endif// _DEBUG + memcpy(&blist[index].newMatrix,&bone.matrix, sizeof(mdxaBone_t)); +} + +int G2_Find_Bone_Rag(CGhoul2Info *ghlInfo, boneInfo_v &blist, const char *boneName) +{ + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + + offsets = (mdxaSkelOffsets_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]); + + /* + model_t *currentModel; + model_t *animModel; + mdxaHeader_t *aHeader; + + currentModel = R_GetModelByHandle(RE_RegisterModel(ghlInfo->mFileName)); + assert(currentModel); + animModel = R_GetModelByHandle(currentModel->mdxm->animIndex); + assert(animModel); + aHeader = animModel->mdxa; + assert(aHeader); + + offsets = (mdxaSkelOffsets_t *)((byte *)aHeader + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]); + */ + + // look through entire list + for(size_t i=0; iaHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]); + //skel = (mdxaSkel_t *)((byte *)aHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]); + + // if name is the same, we found it + if (!Q_stricmp(skel->name, boneName)) + { + return i; + } + } +#if _DEBUG +// G2_Bone_Not_Found(boneName,ghlInfo->mFileName); +#endif + // didn't find it + return -1; +} + +static int G2_Set_Bone_Rag(const mdxaHeader_t *mod_a, + boneInfo_v &blist, + const char *boneName, + CGhoul2Info &ghoul2, + const vec3_t scale, + const vec3_t origin) +{ + // do not change the state of the skeleton here + int index = G2_Find_Bone_Rag(&ghoul2, blist, boneName); + + if (index == -1) + { + index = G2_Add_Bone(ghoul2.animModel, blist, boneName); + } + + if (index != -1) + { + boneInfo_t &bone=blist[index]; + VectorCopy(origin,bone.extraVec1); + + G2_GetBoneMatrixLow(ghoul2,bone.boneNumber,scale,bone.originalTrueBoneMatrix,bone.basepose,bone.baseposeInv); +// bone.parentRawBoneIndex=G2_GetParentBoneMatrixLow(ghoul2,bone.boneNumber,scale,bone.parentTrueBoneMatrix,bone.baseposeParent,bone.baseposeInvParent); + assert( !Q_isnan(bone.originalTrueBoneMatrix.matrix[1][1])); + assert( !Q_isnan(bone.originalTrueBoneMatrix.matrix[1][3])); + bone.originalOrigin[0]=bone.originalTrueBoneMatrix.matrix[0][3]; + bone.originalOrigin[1]=bone.originalTrueBoneMatrix.matrix[1][3]; + bone.originalOrigin[2]=bone.originalTrueBoneMatrix.matrix[2][3]; + } + return index; +} + +static int G2_Set_Bone_Angles_Rag( + CGhoul2Info &ghoul2, + const mdxaHeader_t *mod_a, + boneInfo_v &blist, + const char *boneName, + const int flags, + const float radius, + const vec3_t angleMin=0, + const vec3_t angleMax=0, + const int blendTime=500) +{ + int index = G2_Find_Bone_Rag(&ghoul2, blist, boneName); + + if (index == -1) + { + index = G2_Add_Bone(ghoul2.animModel, blist, boneName); + } + if (index != -1) + { + boneInfo_t &bone=blist[index]; + bone.flags &= ~(BONE_ANGLES_TOTAL); + bone.flags |= BONE_ANGLES_RAGDOLL; + if (flags&RAG_PCJ) + { + if (flags&RAG_PCJ_POST_MULT) + { + bone.flags |= BONE_ANGLES_POSTMULT; + } + else if (flags&RAG_PCJ_MODEL_ROOT) + { + bone.flags |= BONE_ANGLES_PREMULT; +// bone.flags |= BONE_ANGLES_POSTMULT; + } + else + { + assert(!"Invalid RAG PCJ\n"); + } + } + bone.ragStartTime=G2API_GetTime(0); + bone.boneBlendStart = bone.ragStartTime; + bone.boneBlendTime = blendTime; + bone.radius=radius; + bone.weight=1.0f; + + //init the others to valid values + bone.epGravFactor = 0; + VectorClear(bone.epVelocity); + bone.solidCount = 0; + bone.physicsSettled = false; + bone.snapped = false; + + bone.parentBoneIndex = -1; + + bone.offsetRotation = 0.0f; + + bone.overGradSpeed = 0.0f; + VectorClear(bone.overGoalSpot); + bone.hasOverGoal = false; + bone.hasAnimFrameMatrix = -1; + +// bone.weight=pow(radius,1.7f); //cubed was too harsh +// bone.weight=radius*radius*radius; + if (angleMin&&angleMax) + { + VectorCopy(angleMin,bone.minAngles); + VectorCopy(angleMax,bone.maxAngles); + } + else + { + VectorCopy(bone.currentAngles,bone.minAngles); // I guess this isn't a rag pcj then + VectorCopy(bone.currentAngles,bone.maxAngles); + } + if (!bone.lastTimeUpdated) + { + static mdxaBone_t id = + { + { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f } + } + }; + memcpy(&bone.ragOverrideMatrix,&id, sizeof(mdxaBone_t)); + VectorClear(bone.anglesOffset); + VectorClear(bone.positionOffset); + VectorClear(bone.velocityEffector); // this is actually a velocity now + VectorClear(bone.velocityRoot); // this is actually a velocity now + VectorClear(bone.lastPosition); + VectorClear(bone.lastShotDir); + bone.lastContents=0; + // if this is non-zero, we are in a dynamic state + bone.firstCollisionTime=bone.ragStartTime; + // if this is non-zero, we are in a settling state + bone.restTime=0; + // if they are both zero, we are in a settled state + + bone.firstTime=0; + + bone.RagFlags=flags; + bone.DependentRagIndexMask=0; + + G2_Generate_MatrixRag(blist,index); // set everything to th id + +#if 0 + VectorClear(bone.currentAngles); +// VectorAdd(bone.minAngles,bone.maxAngles,bone.currentAngles); +// VectorScale(bone.currentAngles,0.5f,bone.currentAngles); +#else + { + if ( + (flags&RAG_PCJ_MODEL_ROOT) || + (flags&RAG_PCJ_PELVIS) || + !(flags&RAG_PCJ)) + { + VectorClear(bone.currentAngles); + } + else + { + int k; + for (k=0;k<3;k++) + { + float scalar=flrand(-1.0f,1.0f); + scalar*=flrand(-1.0f,1.0f)*flrand(-1.0f,1.0f); + // this is a heavily central distribution + // center it on .5 (and make it small) + scalar*=0.5f; + scalar+=0.5f; + + bone.currentAngles[k]=(bone.minAngles[k]-bone.maxAngles[k])*scalar+bone.maxAngles[k]; + } + } + } +// VectorClear(bone.currentAngles); +#endif + VectorCopy(bone.currentAngles,bone.lastAngles); + } + } + return index; +} + +class CRagDollParams; +const mdxaHeader_t *G2_GetModA(CGhoul2Info &ghoul2); + + +static void G2_RagDollMatchPosition() +{ + haveDesiredPelvisOffset=false; + int i; + for (i=0;inumBones); +#else //The anims on every bone are messed up too, as are the angles. There's not really any way to get back to a normal state, so just clear the list + //and let them re-set their anims/angles gameside. + int i = 0; + while (i < blist.size()) + { + boneInfo_t &bone = blist[i]; + if (bone.boneNumber != -1 && (bone.flags & BONE_ANGLES_RAGDOLL)) + { + bone.flags &= ~BONE_ANGLES_RAGDOLL; + bone.flags &= ~BONE_ANGLES_IK; + bone.RagFlags = 0; + bone.lastTimeUpdated = 0; + VectorClear(bone.currentAngles); + bone.ragStartTime = 0; + } + i++; + } +#endif + ghoul2.mFlags &= ~(GHOUL2_RAG_PENDING|GHOUL2_RAG_DONE|GHOUL2_RAG_STARTED); +} + +void G2_SetRagDoll(CGhoul2Info_v &ghoul2V,CRagDollParams *parms) +{ + if (parms) + { + parms->CallRagDollBegin = false; + } + if (!broadsword||!broadsword->integer||!parms) + { + return; + } + int model; + for (model = 0; model < ghoul2V.size(); model++) + { + if (ghoul2V[model].mModelindex != -1) + { + break; + } + } + if (model==ghoul2V.size()) + { + return; + } + CGhoul2Info &ghoul2=ghoul2V[model]; + const mdxaHeader_t *mod_a=G2_GetModA(ghoul2); + if (!mod_a) + { + return; + } + int curTime=G2API_GetTime(0); + boneInfo_v &blist = ghoul2.mBlist; + int index = G2_Find_Bone_Rag(&ghoul2, blist, "model_root"); + switch (parms->RagPhase) + { + case CRagDollParams::RP_START_DEATH_ANIM: + ghoul2.mFlags|=GHOUL2_RAG_PENDING; + return; /// not doing anything with this yet + break; + case CRagDollParams::RP_END_DEATH_ANIM: + ghoul2.mFlags|=GHOUL2_RAG_PENDING|GHOUL2_RAG_DONE; + if (broadsword_waitforshot && + broadsword_waitforshot->integer) + { + if (broadsword_waitforshot->integer==2) + { + if (!(ghoul2.mFlags&(GHOUL2_RAG_COLLISION_DURING_DEATH|GHOUL2_RAG_COLLISION_SLIDE))) + { + //nothing was encountered, lets just wait for the first shot + return; // we ain't starting yet + } + } + else + { + return; // we ain't starting yet + } + } + break; + case CRagDollParams::RP_DEATH_COLLISION: + if (parms->collisionType) + { + ghoul2.mFlags|=GHOUL2_RAG_COLLISION_SLIDE; + } + else + { + ghoul2.mFlags|=GHOUL2_RAG_COLLISION_DURING_DEATH; + } + if (broadsword_dontstopanim && broadsword_waitforshot && + (broadsword_dontstopanim->integer || broadsword_waitforshot->integer) + ) + { + if (!(ghoul2.mFlags&GHOUL2_RAG_DONE)) + { + return; // we ain't starting yet + } + } + break; + case CRagDollParams::RP_CORPSE_SHOT: + if (broadsword_kickorigin && + broadsword_kickorigin->integer) + { + if (index>=0&&index<(int)blist.size()) + { + boneInfo_t &bone=blist[index]; + if (bone.boneNumber>=0) + { + if (bone.flags & BONE_ANGLES_RAGDOLL) + { + //rww - Would need ent pointer here. But.. since this is SW, we aren't even having corpse shooting anyway I'd imagine. + /* + float magicFactor14=8.0f; //64.0f; // kick strength + + if (parms->fShotStrength) + { //if there is a shot strength, use it instead + magicFactor14 = parms->fShotStrength; + } + + parms->me->s.pos.trType = TR_GRAVITY; + parms->me->s.pos.trDelta[0] += bone.lastShotDir[0]*magicFactor14; + parms->me->s.pos.trDelta[1] += bone.lastShotDir[1]*magicFactor14; + //parms->me->s.pos.trDelta[2] = fabsf(bone.lastShotDir[2])*magicFactor14; + //rww - The vertical portion of this doesn't seem to work very well + //I am just leaving it whatever it is for now, because my velocity scaling + //only works on x and y and the gravity stuff for NPCs is a bit unpleasent + //trying to change/work with + assert( !Q_isnan(bone.lastShotDir[1])); + */ + } + } + } + } + break; + case CRagDollParams::RP_GET_PELVIS_OFFSET: + if (parms->RagPhase==CRagDollParams::RP_GET_PELVIS_OFFSET) + { + VectorClear(parms->pelvisAnglesOffset); + VectorClear(parms->pelvisPositionOffset); + } + // intentional lack of a break + case CRagDollParams::RP_SET_PELVIS_OFFSET: + if (index>=0&&index<(int)blist.size()) + { + boneInfo_t &bone=blist[index]; + if (bone.boneNumber>=0) + { + if (bone.flags & BONE_ANGLES_RAGDOLL) + { + if (parms->RagPhase==CRagDollParams::RP_GET_PELVIS_OFFSET) + { + VectorCopy(bone.anglesOffset,parms->pelvisAnglesOffset); + VectorCopy(bone.positionOffset,parms->pelvisPositionOffset); + } + else + { + VectorCopy(parms->pelvisAnglesOffset,bone.anglesOffset); + VectorCopy(parms->pelvisPositionOffset,bone.positionOffset); + } + } + } + } + return; + break; + case CRagDollParams::RP_DISABLE_EFFECTORS: + // not doing anything with this yet + return; + break; + default: + assert(0); + return; + break; + } + if (ghoul2.mFlags&GHOUL2_RAG_STARTED) + { + // only going to begin ragdoll once, everything else depends on what happens to the origin + return; + } + + ghoul2.mFlags|=GHOUL2_RAG_PENDING|GHOUL2_RAG_DONE|GHOUL2_RAG_STARTED; // well anyway we are going live + parms->CallRagDollBegin=qtrue; + + G2_GenerateWorldMatrix(parms->angles, parms->position); + G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale); + + G2_Set_Bone_Rag(mod_a,blist,"model_root",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"pelvis",ghoul2,parms->scale,parms->position); + + G2_Set_Bone_Rag(mod_a,blist,"lower_lumbar",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"upper_lumbar",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"thoracic",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"cranium",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rhumerus",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"lhumerus",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rradius",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"lradius",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rfemurYZ",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"lfemurYZ",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rtibia",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"ltibia",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rhand",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"lhand",ghoul2,parms->scale,parms->position); + //G2_Set_Bone_Rag(mod_a,blist,"rtarsal",ghoul2,parms->scale,parms->position); + //G2_Set_Bone_Rag(mod_a,blist,"ltarsal",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rtalus",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"ltalus",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rradiusX",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"lradiusX",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"rfemurX",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"lfemurX",ghoul2,parms->scale,parms->position); + G2_Set_Bone_Rag(mod_a,blist,"ceyebrow",ghoul2,parms->scale,parms->position); + + //int startFrame = 3665, endFrame = 3665+1; + int startFrame = parms->startFrame, endFrame = parms->endFrame; + assert(startFrame < mod_a->numFrames); + assert(endFrame < mod_a->numFrames); + + G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"upper_lumbar",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),150,0,true); + G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"lower_lumbar",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),150,0,true); + G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"Motion",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),150,0,true); +// G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"model_root",startFrame,endFrame-1, +// BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, +// 1.0f,curTime,float(startFrame),150,0,true); + G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"lfemurYZ",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),150,0,true); + G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"rfemurYZ",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),150,0,true); + + G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"rhumerus",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),150,0,true); + G2_Set_Bone_Anim_No_BS(ghoul2, mod_a,blist,"lhumerus",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),150,0,true); + + G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale); + + static const float fRadScale = 0.3f;//0.5f; + + vec3_t pcjMin,pcjMax; + VectorSet(pcjMin,-90.0f,-45.0f,-45.0f); + VectorSet(pcjMax,90.0f,45.0f,45.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"model_root",RAG_PCJ_MODEL_ROOT|RAG_PCJ|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,100); + VectorSet(pcjMin,-45.0f,-45.0f,-45.0f); + VectorSet(pcjMax,45.0f,45.0f,45.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"pelvis",RAG_PCJ_PELVIS|RAG_PCJ|RAG_PCJ_POST_MULT|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,100); + +#if 1 + // new base anim, unconscious flop + int pcjflags=RAG_PCJ|RAG_PCJ_POST_MULT;//|RAG_EFFECTOR; + + VectorSet(pcjMin,-15.0f,-15.0f,-15.0f); + VectorSet(pcjMax,15.0f,15.0f,15.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lower_lumbar",pcjflags|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,500); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"upper_lumbar",pcjflags|RAG_UNSNAPPABLE,10.0f*fRadScale,pcjMin,pcjMax,500); + VectorSet(pcjMin,-25.0f,-25.0f,-25.0f); + VectorSet(pcjMax,25.0f,25.0f,25.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"thoracic",pcjflags|RAG_EFFECTOR|RAG_UNSNAPPABLE,12.0f*fRadScale,pcjMin,pcjMax,500); + + VectorSet(pcjMin,-10.0f,-10.0f,-90.0f); + VectorSet(pcjMax,10.0f,10.0f,90.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"cranium",pcjflags|RAG_BONE_LIGHTWEIGHT|RAG_UNSNAPPABLE,6.0f*fRadScale,pcjMin,pcjMax,500); + + static const float sFactLeg = 1.0f; + static const float sFactArm = 1.0f; + static const float sRadArm = 1.0f; + static const float sRadLeg = 1.0f; + + VectorSet(pcjMin,-100.0f,-40.0f,-15.0f); + VectorSet(pcjMax,-15.0f,80.0f,15.0f); + VectorScale(pcjMin, sFactArm, pcjMin); + VectorScale(pcjMax, sFactArm, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT|RAG_UNSNAPPABLE,(4.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500); + VectorSet(pcjMin,-50.0f,-80.0f,-15.0f); + VectorSet(pcjMax,15.0f,40.0f,15.0f); + VectorScale(pcjMin, sFactArm, pcjMin); + VectorScale(pcjMax, sFactArm, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT|RAG_UNSNAPPABLE,(4.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500); + + VectorSet(pcjMin,-25.0f,-20.0f,-20.0f); + VectorSet(pcjMax,90.0f,20.0f,-20.0f); + VectorScale(pcjMin, sFactArm, pcjMin); + VectorScale(pcjMax, sFactArm, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rradius",pcjflags|RAG_BONE_LIGHTWEIGHT,(3.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500); + VectorSet(pcjMin,-90.0f,-20.0f,-20.0f); + VectorSet(pcjMax,30.0f,20.0f,-20.0f); + VectorScale(pcjMin, sFactArm, pcjMin); + VectorScale(pcjMax, sFactArm, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lradius",pcjflags|RAG_BONE_LIGHTWEIGHT,(3.0f*sRadArm)*fRadScale,pcjMin,pcjMax,500); + + + VectorSet(pcjMin,-80.0f,-50.0f,-20.0f); + VectorSet(pcjMax,30.0f,5.0f,20.0f); + VectorScale(pcjMin, sFactLeg, pcjMin); + VectorScale(pcjMax, sFactLeg, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500); + VectorSet(pcjMin,-60.0f,-5.0f,-20.0f); + VectorSet(pcjMax,50.0f,50.0f,20.0f); + VectorScale(pcjMin, sFactLeg, pcjMin); + VectorScale(pcjMax, sFactLeg, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500); + + VectorSet(pcjMin,-20.0f,-15.0f,-15.0f); + VectorSet(pcjMax,100.0f,15.0f,15.0f); + VectorScale(pcjMin, sFactLeg, pcjMin); + VectorScale(pcjMax, sFactLeg, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500); + VectorSet(pcjMin,20.0f,-15.0f,-15.0f); + VectorSet(pcjMax,100.0f,15.0f,15.0f); + VectorScale(pcjMin, sFactLeg, pcjMin); + VectorScale(pcjMax, sFactLeg, pcjMax); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadLeg)*fRadScale,pcjMin,pcjMax,500); +#else + // old base anim + int pcjflags=RAG_PCJ|RAG_PCJ_POST_MULT|RAG_EFFECTOR; + + VectorSet(pcjMin,-15.0f,-15.0f,-15.0f); + VectorSet(pcjMax,45.0f,15.0f,15.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lower_lumbar",pcjflags,10.0f,pcjMin,pcjMax,500); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"upper_lumbar",pcjflags,10.0f,pcjMin,pcjMax,500); + VectorSet(pcjMin,-45.0f,-45.0f,-45.0f); + VectorSet(pcjMax,45.0f,45.0f,45.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"thoracic",pcjflags,10.0f,pcjMin,pcjMax,500); + + VectorSet(pcjMin,-10.0f,-10.0f,-90.0f); + VectorSet(pcjMax,10.0f,10.0f,90.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"cranium",pcjflags|RAG_BONE_LIGHTWEIGHT,6.0f,pcjMin,pcjMax,500); + + //VectorSet(pcjMin,-45.0f,-90.0f,-100.0f); + VectorSet(pcjMin,-180.0f,-180.0f,-100.0f); + //VectorSet(pcjMax,60.0f,60.0f,45.0f); + VectorSet(pcjMax,180.0f,180.0f,45.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500); + //VectorSet(pcjMin,-45.0f,-60.0f,-45.0f); + VectorSet(pcjMin,-180.0f,-180.0f,-100.0f); + //VectorSet(pcjMax,60.0f,90.0f,100.0f); + VectorSet(pcjMax,180.0f,180.0f,100.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lhumerus",pcjflags|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500); + + //-120/120 + VectorSet(pcjMin,-120.0f,-20.0f,-20.0f); + VectorSet(pcjMax,50.0f,20.0f,-20.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rradius",pcjflags|RAG_BONE_LIGHTWEIGHT,3.0f,pcjMin,pcjMax,500); + VectorSet(pcjMin,-120.0f,-20.0f,-20.0f); + VectorSet(pcjMax,5.0f,20.0f,-20.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lradius",pcjflags|RAG_BONE_LIGHTWEIGHT,3.0f,pcjMin,pcjMax,500); + + VectorSet(pcjMin,-90.0f,-50.0f,-20.0f); + VectorSet(pcjMax,50.0f,20.0f,20.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,6.0f,pcjMin,pcjMax,500); + VectorSet(pcjMin,-90.0f,-20.0f,-20.0f); + VectorSet(pcjMax,50.0f,50.0f,20.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lfemurYZ",pcjflags|RAG_BONE_LIGHTWEIGHT,6.0f,pcjMin,pcjMax,500); + + //120 + VectorSet(pcjMin,-20.0f,-15.0f,-15.0f); + VectorSet(pcjMax,120.0f,15.0f,15.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500); + VectorSet(pcjMin,20.0f,-15.0f,-15.0f); + VectorSet(pcjMax,120.0f,15.0f,15.0f); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltibia",pcjflags|RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,4.0f,pcjMin,pcjMax,500); +#endif + + + float sRadEArm = 1.2f; + float sRadELeg = 1.2f; + +// int rhand= + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rhand",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lhand",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale); + //G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtarsal",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale); + //G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltarsal",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale); +// G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtibia",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale); +// G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltibia",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rtalus",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ltalus",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(4.0f*sRadELeg)*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rradiusX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lradiusX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(6.0f*sRadEArm)*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"rfemurX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(10.0f*sRadELeg)*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"lfemurX",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,(10.0f*sRadELeg)*fRadScale); + //G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ceyebrow",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,10.0f*fRadScale); + G2_Set_Bone_Angles_Rag(ghoul2, mod_a,blist,"ceyebrow",RAG_EFFECTOR|RAG_BONE_LIGHTWEIGHT,5.0f); +//match the currrent animation + if (!G2_RagDollSetup(ghoul2,curTime,true,parms->position,false)) + { + assert(!"failed to add any rag bones"); + return; + } + G2_RagDollCurrentPosition(ghoul2V,model,curTime,parms->angles,parms->position,parms->scale); + int k; + + CRagDollUpdateParams fparms; + VectorCopy(parms->position, fparms.position); + VectorCopy(parms->angles, fparms.angles); + VectorCopy(parms->scale, fparms.scale); + VectorClear(fparms.velocity); + fparms.me = parms->me; + fparms.settleFrame = parms->endFrame; + fparms.groundEnt = parms->groundEnt; + + //Guess I don't need to do this, do I? + G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale); + + vec3_t dPos; + VectorCopy(parms->position, dPos); +#ifdef _OLD_STYLE_SETTLE + dPos[2] -= 6; +#endif + + for (k=0;kangles,dPos,parms->scale); + G2_RagDollMatchPosition(); + G2_RagDollSolve(ghoul2V,model,1.0f*(1.0f-k/40.0f),curTime,dPos,false); + } +} + +void G2_SetRagDollBullet(CGhoul2Info &ghoul2,const vec3_t rayStart,const vec3_t hit) +{ + if (!broadsword||!broadsword->integer) + { + return; + } + vec3_t shotDir; + VectorSubtract(hit,rayStart,shotDir); + float len=VectorLength(shotDir); + if (len<1.0f) + { + return; + } + float lenr=1.0f/len; + shotDir[0]*=lenr; + shotDir[1]*=lenr; + shotDir[2]*=lenr; + + bool firstOne=false; + if (broadsword_kickbones&&broadsword_kickbones->integer) + { + int magicFactor13=150.0f; // squared radius multiplier for shot effects + boneInfo_v &blist = ghoul2.mBlist; + for(int i=(int)(blist.size()-1);i>=0;i--) + { + boneInfo_t &bone=blist[i]; + if ((bone.flags & BONE_ANGLES_TOTAL)) + { + if (bone.flags & BONE_ANGLES_RAGDOLL) + { + if (!firstOne) + { + firstOne=true; +#if 0 + int curTime=G2API_GetTime(0); + const mdxaHeader_t *mod_a=G2_GetModA(ghoul2); + int startFrame = 0, endFrame = 0; +#if 1 + TheGhoul2Wraith()->GetAnimFrames(ghoul2.mID, "unconsciousdeadflop01", startFrame, endFrame); + if (startFrame == -1 && endFrame == -1) + { //A bad thing happened! Just use the hardcoded numbers even though they could be wrong. + startFrame = 3573; + endFrame = 3583; + assert(0); + } + G2_Set_Bone_Anim_No_BS(mod_a,blist,"upper_lumbar",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),75,0,true); + G2_Set_Bone_Anim_No_BS(mod_a,blist,"lfemurYZ",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),75,0,true); + G2_Set_Bone_Anim_No_BS(mod_a,blist,"rfemurYZ",startFrame,endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f,curTime,float(startFrame),75,0,true); +#else + TheGhoul2Wraith()->GetAnimFrames(ghoul2.mID, "backdeadflop01", startFrame, endFrame); + if (startFrame == -1 && endFrame == -1) + { //A bad thing happened! Just use the hardcoded numbers even though they could be wrong. + startFrame = 3581; + endFrame = 3592; + assert(0); + } + G2_Set_Bone_Anim_No_BS(mod_a,blist,"upper_lumbar",endFrame,startFrame+1, + BONE_ANIM_OVERRIDE_FREEZE, + -1.0f,curTime,float(endFrame-1),50,0,true); + G2_Set_Bone_Anim_No_BS(mod_a,blist,"lfemurYZ",endFrame,startFrame+1, + BONE_ANIM_OVERRIDE_FREEZE, + -1.0f,curTime,float(endFrame-1),50,0,true); + G2_Set_Bone_Anim_No_BS(mod_a,blist,"rfemurYZ",endFrame,startFrame+1, + BONE_ANIM_OVERRIDE_FREEZE, + -1.0f,curTime,float(endFrame-1),50,0,true); +#endif +#endif + } + + VectorCopy(shotDir,bone.lastShotDir); + vec3_t dir; + VectorSubtract(bone.lastPosition,hit,dir); + len=VectorLength(dir); + if (len<1.0f) + { + len=1.0f; + } + lenr=1.0f/len; + float effect=lenr; + effect*=magicFactor13*effect; // this is cubed, one of them is absorbed by the next calc + bone.velocityEffector[0]=shotDir[0]*(effect+flrand(0.0f,0.05f)); + bone.velocityEffector[1]=shotDir[1]*(effect+flrand(0.0f,0.05f)); + bone.velocityEffector[2]=fabs(shotDir[2])*(effect+flrand(0.0f,0.05f)); +// bone.velocityEffector[0]=shotDir[0]*(effect+flrand(0.0f,0.05f))*flrand(-0.1f,3.0f); +// bone.velocityEffector[1]=shotDir[1]*(effect+flrand(0.0f,0.05f))*flrand(-0.1f,3.0f); +// bone.velocityEffector[2]=fabs(shotDir[2])*(effect+flrand(0.0f,0.05f))*flrand(-0.1f,3.0f); + assert( !Q_isnan(shotDir[2])); + // bone.currentAngles[0]+=flrand(-10.0f*lenr,10.0f*lenr); + // bone.currentAngles[1]+=flrand(-10.0f*lenr,10.0f*lenr); + // bone.currentAngles[2]+=flrand(-10.0f*lenr,10.0f*lenr); + // bone.lastAngles[0]+=flrand(-10.0f*lenr,10.0f*lenr); + // bone.lastAngles[1]+=flrand(-10.0f*lenr,10.0f*lenr); + // bone.lastAngles[2]+=flrand(-10.0f*lenr,10.0f*lenr); + + // go dynamic + bone.firstCollisionTime=G2API_GetTime(0); +// bone.firstCollisionTime=0; + bone.restTime=0; + } + } + } + } +} + + +static float G2_RagSetState(CGhoul2Info &ghoul2, boneInfo_t &bone,int frameNum,const vec3_t origin,bool &resetOrigin) +{ + ragOriginChange=DistanceSquared(origin,bone.extraVec1); + VectorSubtract(origin,bone.extraVec1,ragOriginChangeDir); + + float decay=1.0f; + + int dynamicTime=1000; + int settleTime=1000; + + if (ghoul2.mFlags & GHOUL2_RAG_FORCESOLVE) + { + ragState=ERS_DYNAMIC; + if (frameNum>bone.firstCollisionTime+dynamicTime) + { + VectorCopy(origin,bone.extraVec1); + if (ragOriginChange>15.0f) + { //if we moved, or if this bone is still in solid + bone.firstCollisionTime=frameNum; + } + else + { + // settle out + bone.firstCollisionTime=0; + bone.restTime=frameNum; + ragState=ERS_SETTLING; + } + } + } + else if (bone.firstCollisionTime>0) + { + ragState=ERS_DYNAMIC; + if (frameNum>bone.firstCollisionTime+dynamicTime) + { + VectorCopy(origin,bone.extraVec1); + if (ragOriginChange>15.0f) + { //if we moved, or if this bone is still in solid + bone.firstCollisionTime=frameNum; + } + else + { + // settle out + bone.firstCollisionTime=0; + bone.restTime=frameNum; + ragState=ERS_SETTLING; + } + } +//decay=0.0f; + } + else if (bone.restTime>0) + { + decay=1.0f-(frameNum-bone.restTime)/float(dynamicTime); + if (decay<0.0f) + { + decay=0.0f; + } + if (decay>1.0f) + { + decay=1.0f; + } + float magicFactor8=1.0f; // Power for decay + decay=pow(decay,magicFactor8); + ragState=ERS_SETTLING; + if (frameNum>bone.restTime+settleTime) + { + VectorCopy(origin,bone.extraVec1); + if (ragOriginChange>15.0f) + { + bone.restTime=frameNum; + } + else + { + // stop + bone.restTime=0; + ragState=ERS_SETTLED; + } + } +//decay=0.0f; + } + else + { + if (bone.RagFlags & RAG_PCJ_IK_CONTROLLED) + { + bone.firstCollisionTime=frameNum; + ragState=ERS_DYNAMIC; + } + else if (ragOriginChange>15.0f) + { + bone.firstCollisionTime=frameNum; + ragState=ERS_DYNAMIC; + } + else + { + ragState=ERS_SETTLED; + } + decay=0.0f; + } +// ragState=ERS_SETTLED; +// decay=0.0f; + return decay; +} + +static bool G2_RagDollSetup(CGhoul2Info &ghoul2,int frameNum,bool resetOrigin,const vec3_t origin,bool anyRendered) +{ + int minSurvivingBone=10000; + //int minSurvivingBoneAt=-1; + int minSurvivingBoneAlt=10000; + //int minSurvivingBoneAtAlt=-1; + + assert(ghoul2.mFileName[0]); + boneInfo_v &blist = ghoul2.mBlist; + rag.clear(); + int numRendered=0; + int numNotRendered=0; + //int pelvisAt=-1; + for(size_t i=0; i=0) + { + assert(bone.boneNumberbone.boneNumber) + { + minSurvivingBone=bone.boneNumber; + //minSurvivingBoneAt=i; + } + } + else if (wasRendered) + { + if (minSurvivingBoneAlt>bone.boneNumber) + { + minSurvivingBoneAlt=bone.boneNumber; + //minSurvivingBoneAtAlt=i; + } + } + if ( + anyRendered && + (bone.RagFlags&RAG_WAS_EVER_RENDERED) && + !(bone.RagFlags&RAG_PCJ_MODEL_ROOT) && + !(bone.RagFlags&RAG_PCJ_PELVIS) && + !wasRendered && + (bone.RagFlags&RAG_EFFECTOR) + ) + { + // this thing was rendered in the past, but wasn't now, although other bones were, lets get rid of it +// bone.flags &= ~BONE_ANGLES_RAGDOLL; +// bone.RagFlags = 0; +//Com_OPrintf("Deleted Effector %d\n",i); +// continue; + } + if ((int)rag.size()=0 && + pelvisAt>=0) + { + { + // remove the pelvis as a rag + boneInfo_t &bone=blist[minSurvivingBoneAt]; + bone.flags&=~BONE_ANGLES_RAGDOLL; + bone.RagFlags=0; + } + { + // the root-est bone is now our "pelvis + boneInfo_t &bone=blist[minSurvivingBoneAt]; + VectorSet(bone.minAngles,-14500.0f,-14500.0f,-14500.0f); + VectorSet(bone.maxAngles,14500.0f,14500.0f,14500.0f); + bone.RagFlags|=RAG_PCJ_PELVIS|RAG_PCJ; // this guy is our new "pelvis" + bone.flags |= BONE_ANGLES_POSTMULT; + bone.ragStartTime=G2API_GetTime(0); + } + } + } +#endif + numRags=0; + //int ragStartTime=0; + for(size_t i=0; i=0); + assert(numRagsinteger) + { + return; + } + + if (!params) + { + assert(0); + return; + } + + vec3_t dPos; + VectorCopy(params->position, dPos); +#ifdef _OLD_STYLE_SETTLE + dPos[2] -= 6; +#endif + +// params->DebugLine(handPos,handPos2,false); + int frameNum=G2API_GetTime(0); + CGhoul2Info &ghoul2=ghoul2V[g2Index]; + assert(ghoul2.mFileName[0]); + boneInfo_v &blist = ghoul2.mBlist; + + // hack for freezing ragdoll (no idea if it works) +#if 0 + if (0) + { + // we gotta hack this to basically freeze the timers + for(i=0; i=0) + { + assert(bone.boneNumber=0) + { + assert(bone.boneNumber= 0 && bone2.solidCount > 8) + { + noneInSolid = false; + break; + } + } + + if (noneInSolid) + { //we're settled then + params->RagDollSettled(); + return; + } + else + { + continue; + } +#else + params->RagDollSettled(); + return; +#endif + } + if (G2_WasBoneRendered(ghoul2,bone.boneNumber)) + { + anyRendered=true; + break; + } + } + } + } + + int iters = (ragState == ERS_DYNAMIC) ? 4 : 2; + /* + //bool kicked=false; + if (ragOriginChangeDir[2]<-100.0f) + { + //kicked=true; + //iters*=8; + iters*=2; //rww - changed to this.. it was getting up to around 600 traces at times before (which is insane) + } + */ + if (iters) + { + if (!G2_RagDollSetup(ghoul2,frameNum,resetOrigin,dPos,anyRendered)) + { + return; + } + // ok, now our data structures are compact and set up in topological order + + for (int i=0;iangles,dPos,params->scale); + + if (G2_RagDollSettlePositionNumeroTrois(ghoul2V,dPos,params,curTime)) + { +#if 0 + //effectors are start solid alot, so this was pretty extreme + if (!kicked&&iters<4) + { + kicked=true; + //iters*=4; + iters*=2; + } +#endif + } + //params->position[2] += 16; + G2_RagDollSolve(ghoul2V,g2Index,decay*2.0f,frameNum,dPos,true,params); + } + } + + if (params->me != ENTITYNUM_NONE) + { +#if 0 + vec3_t worldMins,worldMaxs; + worldMins[0]=params->position[0]-17; + worldMins[1]=params->position[1]-17; + worldMins[2]=params->position[2]; + worldMaxs[0]=params->position[0]+17; + worldMaxs[1]=params->position[1]+17; + worldMaxs[2]=params->position[2]; +//Com_OPrintf(va("%f \n",worldMins[2]); +// params->DebugLine(worldMins,worldMaxs,true); +#endif + G2_RagDollCurrentPosition(ghoul2V,g2Index,frameNum,params->angles,params->position,params->scale); +// SV_UnlinkEntity(params->me); +// params->me->SetMins(BB_SHOOTING_SIZE,ragBoneMins); +// params->me->SetMaxs(BB_SHOOTING_SIZE,ragBoneMaxs); +// SV_LinkEntity(params->me); + } +} + +#ifdef _DEBUG +#define _DEBUG_BONE_NAMES +#endif + +static inline char *G2_Get_Bone_Name(CGhoul2Info *ghlInfo, boneInfo_v &blist, int boneNum) +{ + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + offsets = (mdxaSkelOffsets_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)ghlInfo->aHeader + sizeof(mdxaHeader_t) + offsets->offsets[0]); + + // look through entire list + for(size_t i=0; iaHeader + sizeof(mdxaHeader_t) + offsets->offsets[blist[i].boneNumber]); + + return skel->name; + } + + // didn't find it + return "BONE_NOT_FOUND"; +} + +char *G2_GetBoneNameFromSkel(CGhoul2Info &ghoul2, int boneNum); + +static void G2_RagDollCurrentPosition(CGhoul2Info_v &ghoul2V,int g2Index,int frameNum,const vec3_t angles,const vec3_t position,const vec3_t scale) +{ + CGhoul2Info &ghoul2=ghoul2V[g2Index]; + assert(ghoul2.mFileName[0]); + G2_GenerateWorldMatrix(angles,position); + G2_ConstructGhoulSkeleton(ghoul2V, frameNum, false, scale); + + float totalWt=0.0f; + int i; + for (i=0;iragBoneMaxs[k]) + { + ragBoneMaxs[k]=ragEffectors[i].currentOrigin[k]; + } + if (ragEffectors[i].currentOrigin[k]0.0f); + int k; + { + float wtInv=1.0f/totalWt; + for (k=0;k<3;k++) + { + ragBoneMaxs[k]-=position[k]; + ragBoneMins[k]-=position[k]; + ragBoneMaxs[k]+=10.0f; + ragBoneMins[k]-=10.0f; + ragBoneCM[k]*=wtInv; + + ragBoneCM[k]=ragEffectors[0].currentOrigin[k]; // use the pelvis + } + } +} + +#ifdef _DEBUG +int ragTraceTime = 0; +int ragSSCount = 0; +int ragTraceCount = 0; +#endif + +void Rag_Trace( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, const int passEntityNum, const int contentmask, const EG2_Collision eG2TraceType, const int useLod ) +{ +#ifdef _DEBUG + int ragPreTrace = ri.Milliseconds(); +#endif + // AntiAnti: should we set entityNum? + results->entityNum = ENTITYNUM_NONE; + ri.SV_Trace(results, start, mins, maxs, end, passEntityNum, contentmask, eG2TraceType, useLod); + results->entityNum = results->fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; + +#ifdef _DEBUG + int ragPostTrace = ri.Milliseconds(); + + ragTraceTime += (ragPostTrace - ragPreTrace); + if (results->startsolid) + { + ragSSCount++; + } + ragTraceCount++; +#endif +} + +//run advanced physics on each bone indivudually +//an adaption of my "exphys" custom game physics model +#define MAX_GRAVITY_PULL 256//512 + +static inline bool G2_BoneOnGround(const vec3_t org, const vec3_t mins, const vec3_t maxs, const int ignoreNum) +{ + trace_t tr; + vec3_t gSpot; + + VectorCopy(org, gSpot); + gSpot[2] -= 1.0f; //seems reasonable to me + + Rag_Trace(&tr, org, mins, maxs, gSpot, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0); + + if (tr.fraction != 1.0f && !tr.startsolid && !tr.allsolid) + { //not in solid, and hit something. Guess it's ground. + return true; + } + + return false; +} + +static inline bool G2_ApplyRealBonePhysics(boneInfo_t &bone, SRagEffector &e, CRagDollUpdateParams *params, vec3_t goalSpot, const vec3_t goalBase, const vec3_t testMins, const vec3_t testMaxs, + const float gravity, const float mass, const float bounce) +{ + trace_t tr; + vec3_t projectedOrigin; + vec3_t vNorm; + vec3_t ground; + vec3_t usedOrigin; + float velScaling = 0.1f; + float vTotal = 0.0f; + bool boneOnGround = false; + + assert(mass <= 1.0f && mass >= 0.01f); + + if (bone.physicsSettled) + { //then we have no need to continue + return true; + } + + if (goalBase) + { + VectorCopy(goalBase, usedOrigin); + } + else + { + VectorCopy(e.currentOrigin, usedOrigin); + } + + if (gravity) + { + //factor it in before we do anything. + VectorCopy(usedOrigin, ground); + ground[2] -= 1.0f; + + Rag_Trace(&tr, usedOrigin, testMins, testMaxs, ground, params->me, RAG_MASK, G2_NOCOLLIDE, 0); + + if (tr.entityNum == ENTITYNUM_NONE) + { + boneOnGround = false; + } + else + { + boneOnGround = true; + } + + if (!boneOnGround) + { + if (!params->velocity[2]) + { //only increase gravitational pull once the actual entity is still + bone.epGravFactor += gravity; + } + + if (bone.epGravFactor > MAX_GRAVITY_PULL) + { //cap it off if needed + bone.epGravFactor = MAX_GRAVITY_PULL; + } + + bone.epVelocity[2] -= bone.epGravFactor; + } + else + { //if we're sitting on something then reset the gravity factor. + bone.epGravFactor = 0; + } + } + else + { + boneOnGround = G2_BoneOnGround(usedOrigin, testMins, testMaxs, params->me); + } + + if (!bone.epVelocity[0] && !bone.epVelocity[1] && !bone.epVelocity[2]) + { //nothing to do if we have no velocity even after gravity. + VectorCopy(usedOrigin, goalSpot); + return true; + } + + //get the projected origin based on velocity. + VectorMA(usedOrigin, velScaling, bone.epVelocity, projectedOrigin); + + //scale it down based on mass + VectorScale(bone.epVelocity, 1.0f-mass, bone.epVelocity); + + VectorCopy(bone.epVelocity, vNorm); + vTotal = VectorNormalize(vNorm); + + if (vTotal < 1 && boneOnGround) + { //we've pretty much stopped moving anyway, just clear it out then. + VectorClear(bone.epVelocity); + bone.epGravFactor = 0; + VectorCopy(usedOrigin, goalSpot); + return true; + } + + Rag_Trace(&tr, usedOrigin, testMins, testMaxs, projectedOrigin, params->me, RAG_MASK, G2_NOCOLLIDE, 0); + + if (tr.startsolid || tr.allsolid) + { //can't go anywhere from here + return false; + } + + //Go ahead and set it to the trace endpoint regardless of what it hit + VectorCopy(tr.endpos, goalSpot); + + if (tr.fraction == 1.0f) + { //Nothing was in the way. + return true; + } + + if (bounce) + { + vTotal *= bounce; //scale it by bounce + + VectorScale(tr.plane.normal, vTotal, vNorm); //scale the trace plane normal by the bounce factor + + if (vNorm[2] > 0) + { + bone.epGravFactor -= vNorm[2]*(1.0f-mass); //The lighter it is the more gravity will be reduced by bouncing vertically. + if (bone.epGravFactor < 0) + { + bone.epGravFactor = 0; + } + } + + VectorAdd(bone.epVelocity, vNorm, bone.epVelocity); //add it into the existing velocity. + + //I suppose it could be sort of neat to make a game callback here to actual do stuff + //when bones slam into things. But it could be slow too. + /* + if (tr.entityNum != ENTITYNUM_NONE && ent->touch) + { //then call the touch function + ent->touch(ent, &g_entities[tr.entityNum], &tr); + } + */ + } + else + { //if no bounce, kill when it hits something. + bone.epVelocity[0] = 0; + bone.epVelocity[1] = 0; + + if (!gravity) + { + bone.epVelocity[2] = 0; + } + } + return true; +} + +#ifdef _DEBUG_BONE_NAMES +static inline void G2_RagDebugBox(vec3_t mins, vec3_t maxs, int duration) +{ + return; //do something +} + +static inline void G2_RagDebugLine(vec3_t start, vec3_t end, int time, int color, int radius) +{ + return; //do something +} +#endif + +#ifdef _OLD_STYLE_SETTLE +static bool G2_RagDollSettlePositionNumeroTrois(CGhoul2Info_v &ghoul2V, const vec3_t currentOrg, CRagDollUpdateParams *params, int curTime) +{ + haveDesiredPelvisOffset=false; + vec3_t desiredPos; + int i; + + assert(params); + //assert(params->me); //no longer valid, because me is an index! + int ignoreNum=params->me; + + bool anyStartSolid=false; + + vec3_t groundSpot={0,0,0}; + // lets find the floor at our quake origin + { + vec3_t testStart; + VectorCopy(currentOrg,testStart); //last arg is dest + vec3_t testEnd; + VectorCopy(testStart,testEnd); //last arg is dest + testEnd[2]-=200.0f; + + vec3_t testMins; + vec3_t testMaxs; + VectorSet(testMins,-10,-10,-10); + VectorSet(testMaxs,10,10,10); + + { + trace_t tr; + assert( !Q_isnan(testStart[1])); + assert( !Q_isnan(testEnd[1])); + assert( !Q_isnan(testMins[1])); + assert( !Q_isnan(testMaxs[1])); + Rag_Trace(&tr, testStart, testMins, testMaxs, testEnd, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0/*SV_TRACE_NO_PLAYER*/); + if (tr.entityNum==0) + { + VectorAdvance(testStart,.5f,testEnd,tr.endpos); + } + if (tr.startsolid) + { + //hmmm, punt + VectorCopy(currentOrg,groundSpot); //last arg is dest + groundSpot[2]-=30.0f; + } + else + { + VectorCopy(tr.endpos,groundSpot); //last arg is dest + } + } + } + + for (i=0;i groundSpot[2]) + { + testStart[2]=groundSpot[2]+(e.radius-10.0f); + } + else + { + // lets try higher + testStart[2]=groundSpot[2]+8.0f; + Rag_Trace(&tr,testStart,testMins,testMaxs,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0); + if (tr.entityNum==0) + { + VectorAdvance(testStart,.5f,testEnd,tr.endpos); + } + } + + } + if (tr.startsolid) + { + iAmStartSolid=true; + anyStartSolid=true; + // above the origin, so lets slide away + if (e.currentOrigin[2] > groundSpot[2]) + { + if (params) + { + SRagDollEffectorCollision args(e.currentOrigin, tr); + params->EffectorCollision(args); + } + } + else + { + //harumph, we are really screwed + } + } + else + { + vertEffectorTraceFraction = tr.fraction; + if (params && + vertEffectorTraceFraction < .95f && + fabsf(tr.plane.normal[2]) < .707f) + { + SRagDollEffectorCollision args(e.currentOrigin, tr); + args.useTracePlane = true; + params->EffectorCollision(args); + } + } + } + vec3_t effectorGroundSpot; + VectorAdvance(testStart,vertEffectorTraceFraction,testEnd,effectorGroundSpot);// VA(a,t,b,c)-> c := (1-t)a+tb + // trace from the quake origin horzontally to the effector + // gonna choose the maximum of the ground spot or the effector location + // and clamp it to be roughly in the bbox + VectorCopy(groundSpot,testStart); //last arg is dest + if (iAmStartSolid) + { + // we don't have a meaningful ground spot + VectorCopy(e.currentOrigin,testEnd); //last arg is dest + bone.solidCount++; + } + else + { + VectorCopy(effectorGroundSpot,testEnd); //last arg is dest + bone.solidCount = 0; + } + assert( !Q_isnan(testStart[1])); + assert( !Q_isnan(testEnd[1])); + assert( !Q_isnan(testMins[1])); + assert( !Q_isnan(testMaxs[1])); + + float ztest; + + if (testEnd[2]>testStart[2]) + { + ztest=testEnd[2]; + } + else + { + ztest=testStart[2]; + } + if (ztest c := (1-t)a+tb + + float horzontalTraceFraction=0.0f; + vec3_t HorizontalHitSpot={0,0,0}; + { + trace_t tr; + Rag_Trace(&tr,testStart,testMins,testMaxs,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0); + if (tr.entityNum==0) + { + VectorAdvance(testStart,.5f,testEnd,tr.endpos); + } + horzontalTraceFraction=tr.fraction; + if (tr.startsolid) + { + horzontalTraceFraction=1.0f; + // punt + VectorCopy(e.currentOrigin,HorizontalHitSpot); + } + else + { + VectorCopy(tr.endpos,HorizontalHitSpot); + int magicFactor46=0.98f; // shorten percetage to make sure we can go down along a wall + //float magicFactor46=0.98f; // shorten percetage to make sure we can go down along a wall + //rww - An..int? + VectorAdvance(tr.endpos,magicFactor46,testStart,HorizontalHitSpot);// VA(a,t,b,c)-> c := (1-t)a+tb + + // roughly speaking this is a wall + if (horzontalTraceFraction<0.9f) + { + + // roughly speaking this is a wall + if (fabsf(tr.plane.normal[2])<0.7f) + { + SRagDollEffectorCollision args(e.currentOrigin, tr); + args.useTracePlane = true; + params->EffectorCollision(args); + } + } + else if (!iAmStartSolid && + effectorGroundSpot[2] < groundSpot[2] - 8.0f) + { + // this is a situation where we have something dangling below the pelvis, we want to find the plane going downhill away from the origin + // for various reasons, without this correction the body will actually move away from places it can fall off. + //gotta run the trace backwards to get a plane + { + trace_t tr; + VectorCopy(effectorGroundSpot,testStart); + VectorCopy(groundSpot,testEnd); + + // this can be a line trace, we just want the plane normal + Rag_Trace(&tr,testEnd,0,0,testStart,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0); + if (tr.entityNum==0) + { + VectorAdvance(testStart,.5f,testEnd,tr.endpos); + } + horzontalTraceFraction=tr.fraction; + if (!tr.startsolid && tr.fraction< 0.7f) + { + SRagDollEffectorCollision args(e.currentOrigin, tr); + args.useTracePlane = true; + params->EffectorCollision(args); + } + } + } + } + } + vec3_t goalSpot={0,0,0}; + // now lets trace down + VectorCopy(HorizontalHitSpot,testStart); + VectorCopy(testStart,testEnd); //last arg is dest + testEnd[2]=e.currentOrigin[2]-30.0f; + { + trace_t tr; + Rag_Trace(&tr,testStart,NULL,NULL,testEnd,ignoreNum,RAG_MASK,G2_NOCOLLIDE,0); + if (tr.entityNum==0) + { + VectorAdvance(testStart,.5f,testEnd,tr.endpos); + } + if (tr.startsolid) + { + // punt, go to the origin I guess + VectorCopy(currentOrg,goalSpot); + } + else + { + VectorCopy(tr.endpos,goalSpot); + int magicFactor47=0.5f; // shorten percentage to make sure we can go down along a wall + VectorAdvance(tr.endpos,magicFactor47,testStart,goalSpot);// VA(a,t,b,c)-> c := (1-t)a+tb + } + } + + // ok now as the horizontal trace fraction approaches zero, we want to head toward the horizontalHitSpot + //geeze I would like some reasonable trace fractions + assert(horzontalTraceFraction>=0.0f&&horzontalTraceFraction<=1.0f); + VectorAdvance(HorizontalHitSpot,horzontalTraceFraction*horzontalTraceFraction,goalSpot,goalSpot);// VA(a,t,b,c)-> c := (1-t)a+tb +#if 0 + if ((bone.RagFlags & RAG_EFFECTOR) && (bone.RagFlags & RAG_BONE_LIGHTWEIGHT)) + { //new rule - don't even bother unless it's a lightweight effector + //rww - Factor object velocity into the final desired spot.. + //We want the limbs with a "light" weight to drag behind the general mass. + //If we got here, we shouldn't be the pelvis or the root, so we should be + //fine to treat as lightweight. However, we can flag bones as being particularly + //light. They're given less downscale for the reduction factor. + vec3_t givenVelocity; + vec3_t vSpot; + trace_t vtr; + float vSpeed = 0; + float verticalSpeed = 0; + float vReductionFactor = 0.03f; + float verticalSpeedReductionFactor = 0.06f; //want this to be more obvious + float lwVReductionFactor = 0.1f; + float lwVerticalSpeedReductionFactor = 0.3f; //want this to be more obvious + + + VectorCopy(params->velocity, givenVelocity); + vSpeed = VectorNormalize(givenVelocity); + vSpeed = -vSpeed; //go in the opposite direction of velocity + + verticalSpeed = vSpeed; + + if (bone.RagFlags & RAG_BONE_LIGHTWEIGHT) + { + vSpeed *= lwVReductionFactor; + verticalSpeed *= lwVerticalSpeedReductionFactor; + } + else + { + vSpeed *= vReductionFactor; + verticalSpeed *= verticalSpeedReductionFactor; + } + + vSpot[0] = givenVelocity[0]*vSpeed; + vSpot[1] = givenVelocity[1]*vSpeed; + vSpot[2] = givenVelocity[2]*verticalSpeed; + VectorAdd(goalSpot, vSpot, vSpot); + + if (vSpot[0] || vSpot[1] || vSpot[2]) + { + Rag_Trace(&vtr, goalSpot, testMins, testMaxs, vSpot, ignoreNum, RAG_MASK, G2_NOCOLLIDE,0); + if (vtr.fraction == 1) + { + VectorCopy(vSpot, goalSpot); + } + } + } +#endif + + int k; + int magicFactor12=0.8f; // dampening of velocity applied + int magicFactor16=10.0f; // effect multiplier of velocity applied + + if (iAmStartSolid) + { + magicFactor16 = 30.0f; + } + + for (k=0;k<3;k++) + { + e.desiredDirection[k]=goalSpot[k]-e.currentOrigin[k]; + e.desiredDirection[k]+=magicFactor16*bone.velocityEffector[k]; + e.desiredDirection[k]+=flrand(-0.75f,0.75f)*flrand(-0.75f,0.75f); + bone.velocityEffector[k]*=magicFactor12; + } + VectorCopy(e.currentOrigin,bone.lastPosition); // last arg is dest + } + return anyStartSolid; +} +#else + +#if 0 +static inline int G2_RagIndexForBoneNum(int boneNum) +{ + for (int i = 0; i < numRags; i++) + { + // these are used for affecting the end result + if (ragBoneData[i].boneNum == boneNum) + { + return i; + } + } + + return -1; +} +#endif + +#ifdef _RAG_PRINT_TEST +void G2_RagPrintMatrix(mdxaBone_t *mat) +{ + char x[1024]; + x[0] = 0; + int n = 0; + while (n < 3) + { + int o = 0; + while (o < 4) + { + strcat(x, va("%f ", mat->matrix[n][o])); + o++; + } + n++; + } + strcat(x, "\n"); + Com_Printf(x); +} +#endif + +void G2_RagGetBoneBasePoseMatrixLow(CGhoul2Info &ghoul2, int boneNum, mdxaBone_t &boneMatrix, mdxaBone_t &retMatrix, vec3_t scale); +void G2_RagGetAnimMatrix(CGhoul2Info &ghoul2, const int boneNum, mdxaBone_t &matrix, const int frame); + +static inline void G2_RagGetWorldAnimMatrix(CGhoul2Info &ghoul2, boneInfo_t &bone, CRagDollUpdateParams *params, mdxaBone_t &retMatrix) +{ + static mdxaBone_t trueBaseMatrix, baseBoneMatrix; + + //get matrix for the settleFrame to use as an ideal + G2_RagGetAnimMatrix(ghoul2, bone.boneNumber, trueBaseMatrix, params->settleFrame); + assert(bone.hasAnimFrameMatrix == params->settleFrame); + + G2_RagGetBoneBasePoseMatrixLow(ghoul2, bone.boneNumber, + trueBaseMatrix, baseBoneMatrix, params->scale); + + //Use params to multiply world coordinate/dir matrix into the + //bone matrix and give us a useable world position + Mat3x4_Multiply(&retMatrix, &worldMatrix, &baseBoneMatrix); + + assert(!Q_isnan(retMatrix.matrix[2][3])); +} + +//get the current pelvis Z direction and the base anim matrix Z direction +//so they can be compared and used to offset -rww +static inline void G2_RagGetPelvisLumbarOffsets(CGhoul2Info &ghoul2, CRagDollUpdateParams *params, vec3_t& pos, vec3_t& dir, vec3_t& animPos, vec3_t& animDir) +{ + static mdxaBone_t final; + static mdxaBone_t x; + //static mdxaBone_t *unused1, *unused2; + //static vec3_t lumbarPos; + + assert(ghoul2.animModel); + int boneIndex = G2_Find_Bone(&ghoul2, ghoul2.mBlist, "pelvis"); + assert(boneIndex != -1); + + G2_RagGetWorldAnimMatrix(ghoul2, ghoul2.mBlist[boneIndex], params, final); + G2API_GiveMeVectorFromMatrix(final, ORIGIN, animPos); + G2API_GiveMeVectorFromMatrix(final, POSITIVE_X, animDir); + + //We have the anim matrix pelvis pos now, so get the normal one as well + int bolt = G2API_AddBolt(&ghoul2, "pelvis"); + G2_GetBoltMatrixLow(ghoul2, bolt, params->scale, x); + Mat3x4_Multiply(&final, &worldMatrix, &x); + G2API_GiveMeVectorFromMatrix(final, ORIGIN, pos); + G2API_GiveMeVectorFromMatrix(final, POSITIVE_X, dir); + + /* + //now get lumbar + boneIndex = G2_Find_Bone(ghoul2.animModel, ghoul2.mBlist, "lower_lumbar"); + assert(boneIndex != -1); + + G2_RagGetWorldAnimMatrix(ghoul2, ghoul2.mBlist[boneIndex], params, final); + G2API_GiveMeVectorFromMatrix(&final, ORIGIN, lumbarPos); + + VectorSubtract(animPos, lumbarPos, animDir); + VectorNormalize(animDir); + + //We have the anim matrix lumbar dir now, so get the normal one as well + G2_GetBoneMatrixLow(ghoul2, boneIndex, params->scale, final, unused1, unused2); + G2API_GiveMeVectorFromMatrix(&final, ORIGIN, lumbarPos); + + VectorSubtract(pos, lumbarPos, dir); + VectorNormalize(dir); + */ +} + +static bool G2_RagDollSettlePositionNumeroTrois(CGhoul2Info_v &ghoul2V, const vec3_t currentOrg, CRagDollUpdateParams *params, int curTime) +{ //now returns true if any bone was in solid, otherwise false + int ignoreNum = params->me; + static int i; + static vec3_t goalSpot; + static trace_t tr; + static trace_t solidTr; + static int k; + static const float velocityDampening = 1.0f; + static const float velocityMultiplier = 60.0f; + static vec3_t testMins; + static vec3_t testMaxs; + vec3_t velDir; + static bool startSolid; + bool anySolid = false; + static mdxaBone_t worldBaseMatrix; + static vec3_t parentOrigin; + static vec3_t basePos; + static vec3_t entScale; + static bool hasDaddy; + static bool hasBasePos; + static vec3_t animPelvisDir, pelvisDir, animPelvisPos, pelvisPos; + + //Maybe customize per-bone? + static const float gravity = 3.0f; + static const float mass = 0.09f; + static const float bounce = 0.0f;//1.3f; + //Bouncing and stuff unfortunately does not work too well at the moment. + //Need to keep a seperate "physics origin" or make the filthy solve stuff + //better. + + bool inAir = false; + + if (params->velocity[0] || params->velocity[1] || params->velocity[2]) + { + inAir = true; + } + + if (!params->scale[0] && !params->scale[1] && !params->scale[2]) + { + VectorSet(entScale, 1.0f, 1.0f, 1.0f); + } + else + { + VectorCopy(params->scale, entScale); + } + + if (broadsword_ragtobase && + broadsword_ragtobase->integer > 1) + { + //grab the pelvis directions to offset base positions for bones + G2_RagGetPelvisLumbarOffsets(ghoul2V[0], params, pelvisPos, pelvisDir, animPelvisPos, + animPelvisDir); + + //don't care about the pitch offsets + pelvisDir[2] = 0; + animPelvisDir[2] = 0; + + /* + vec3_t upelvisPos, uanimPelvisPos; + vec3_t blah; + VectorCopy(pelvisPos, upelvisPos); + VectorCopy(animPelvisPos, uanimPelvisPos); + upelvisPos[2] += 64; + uanimPelvisPos[2] += 64; + + VectorMA(upelvisPos, 32.0f, pelvisDir, blah); + G2_RagDebugLine(upelvisPos, blah, 50, 0x00ff00, 1); + VectorMA(uanimPelvisPos, 32.0f, animPelvisDir, blah); + G2_RagDebugLine(uanimPelvisPos, blah, 50, 0xff0000, 1); + */ + + //just convert to angles now, that's all we'll ever use them for + vectoangles(pelvisDir, pelvisDir); + vectoangles(animPelvisDir, animPelvisDir); + } + + for (i = 0; i < numRags; i++) + { + boneInfo_t &bone = *ragBoneData[i]; + SRagEffector &e = ragEffectors[i]; + + if (inAir) + { + bone.airTime = curTime + 30; + } + + if (bone.RagFlags & RAG_PCJ_PELVIS) + { + VectorSet(goalSpot, params->position[0], params->position[1], (params->position[2]+DEFAULT_MINS_2)+((bone.radius*entScale[2])+2)); + + VectorSubtract(goalSpot, e.currentOrigin, desiredPelvisOffset); + haveDesiredPelvisOffset = true; + VectorCopy(e.currentOrigin, bone.lastPosition); + continue; + } + + if (!(bone.RagFlags & RAG_EFFECTOR)) + { + continue; + } + + if (bone.hasOverGoal) + { //api call was made to override the goal spot + VectorCopy(bone.overGoalSpot, goalSpot); + bone.solidCount = 0; + for (k = 0; k < 3; k++) + { + e.desiredDirection[k] = (goalSpot[k] - e.currentOrigin[k]); + e.desiredDirection[k] += (velocityMultiplier * bone.velocityEffector[k]); + bone.velocityEffector[k] *= velocityDampening; + } + VectorCopy(e.currentOrigin, bone.lastPosition); + + continue; + } + + VectorSet(testMins, -e.radius*entScale[0], -e.radius*entScale[1], -e.radius*entScale[2]); + VectorSet(testMaxs, e.radius*entScale[0], e.radius*entScale[1], e.radius*entScale[2]); + + assert(ghoul2V[0].mBoneCache); + + //get the parent bone's position + hasDaddy = false; + if (bone.boneNumber) + { + assert(ghoul2V[0].animModel); + assert(ghoul2V[0].aHeader); + + if (bone.parentBoneIndex == -1) + { + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + int bParentIndex, bParentListIndex = -1; + + offsets = (mdxaSkelOffsets_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t) + offsets->offsets[bone.boneNumber]); + + bParentIndex = skel->parent; + + while (bParentIndex > 0) + { //go upward through hierarchy searching for the first parent that is a rag bone + skel = (mdxaSkel_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t) + offsets->offsets[bParentIndex]); + bParentIndex = skel->parent; + bParentListIndex = G2_Find_Bone(ghoul2V[0].animModel, ghoul2V[0].mBlist, skel->name); + + if (bParentListIndex != -1) + { + boneInfo_t &pbone = ghoul2V[0].mBlist[bParentListIndex]; + if (pbone.flags & BONE_ANGLES_RAGDOLL) + { //valid rag bone + break; + } + } + + //didn't work out, reset to -1 again + bParentListIndex = -1; + } + + bone.parentBoneIndex = bParentListIndex; + } + + if (bone.parentBoneIndex != -1) + { + boneInfo_t &pbone = ghoul2V[0].mBlist[bone.parentBoneIndex]; + + if (pbone.flags & BONE_ANGLES_RAGDOLL) + { //has origin calculated for us already + VectorCopy(ragEffectors[pbone.ragIndex].currentOrigin, parentOrigin); + hasDaddy = true; + } + } + } + + //get the position this bone would be in if we were in the desired frame + hasBasePos = false; + if (broadsword_ragtobase && + broadsword_ragtobase->integer) + { + vec3_t v, a; + float f; + + G2_RagGetWorldAnimMatrix(ghoul2V[0], bone, params, worldBaseMatrix); + G2API_GiveMeVectorFromMatrix(worldBaseMatrix, ORIGIN, basePos); + + if (broadsword_ragtobase->integer > 1) + { + float fa = AngleNormalize180(animPelvisDir[YAW]-pelvisDir[YAW]); + float d = fa-bone.offsetRotation; + const float tolerance = 16.0f; + + if (d > tolerance || d < -tolerance) + { //don't update unless x degrees away from the ideal to avoid moving goal spots too much if pelvis rotates + bone.offsetRotation = fa; + } + else + { + fa = bone.offsetRotation; + } + //Rotate the point around the pelvis based on the offsets between pelvis positions + VectorSubtract(basePos, animPelvisPos, v); + f = VectorLength(v); + vectoangles(v, a); + a[YAW] -= fa; + AngleVectors(a, v, 0, 0); + VectorNormalize(v); + VectorMA(animPelvisPos, f, v, basePos); + + //re-orient the position of the bone to the current position of the pelvis + VectorSubtract(basePos, animPelvisPos, v); + //push the spots outward? (to stretch the skeleton more) + //v[0] *= 1.5f; + //v[1] *= 1.5f; + VectorAdd(pelvisPos, v, basePos); + } +#if 0 //for debugging frame skeleton + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + + offsets = (mdxaSkelOffsets_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t)); + skel = (mdxaSkel_t *)((byte *)ghoul2V[0].aHeader + sizeof(mdxaHeader_t) + offsets->offsets[bone.boneNumber]); + + vec3_t pu; + VectorCopy(basePos, pu); + pu[2] += 32; + if (bone.boneNumber < 11) + { + G2_RagDebugLine(basePos, pu, 50, 0xff00ff, 1); + } + else if (skel->name[0] == 'l') + { + G2_RagDebugLine(basePos, pu, 50, 0xffff00, 1); + } + else if (skel->name[0] == 'r') + { + G2_RagDebugLine(basePos, pu, 50, 0xffffff, 1); + } + else + { + G2_RagDebugLine(basePos, pu, 50, 0x00ffff, 1); + } +#endif + hasBasePos = true; + } + + //Are we in solid? + if (hasDaddy) + { + Rag_Trace(&tr, e.currentOrigin, testMins, testMaxs, parentOrigin, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0); + //Rag_Trace(&tr, parentOrigin, testMins, testMaxs, e.currentOrigin, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0); + } + else + { + Rag_Trace(&tr, e.currentOrigin, testMins, testMaxs, params->position, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0); + } + + if (tr.startsolid || tr.allsolid || tr.fraction != 1.0f) + { //currently in solid, see what we can do about it + vec3_t vSub; + + startSolid = true; + anySolid = true; + + if (hasBasePos)// && bone.solidCount < 32) + { //only go to the base pos for slightly in solid bones +#if 0 //over-compensation + float fl; + float floorBase; + + VectorSubtract(basePos, e.currentOrigin, vSub); + fl = VectorNormalize(vSub); + VectorMA(e.currentOrigin, /*fl*8.0f*/64.0f, vSub, goalSpot); + + floorBase = ((params->position[2]-23)-testMins[2])+8; + + if (goalSpot[2] > floorBase) + { + goalSpot[2] = floorBase; + } +#else //just use the spot directly + VectorCopy(basePos, goalSpot); + goalSpot[2] = (params->position[2]-23)-testMins[2]; +#endif + //Com_Printf("%i: %f %f %f\n", bone.boneNumber, basePos[0], basePos[1], basePos[2]); + } + else + { //if deep in solid want to try to rise up out of solid before hinting back to base + VectorSubtract(e.currentOrigin, params->position, vSub); + VectorNormalize(vSub); + VectorMA(params->position, 40.0f, vSub, goalSpot); + + //should be 1 unit above the ground taking bounding box sizes into account + goalSpot[2] = (params->position[2]-23)-testMins[2]; + } + + //Trace from the entity origin in the direction between the origin and current bone position to + //find a good eventual goal position + Rag_Trace(&tr, params->position, testMins, testMaxs, goalSpot, params->me, RAG_MASK, G2_NOCOLLIDE, 0); + VectorCopy(tr.endpos, goalSpot); + } + else + { + startSolid = false; + +#if 1 //do hinting? + //Hint the bone back to the base origin + if (hasDaddy || hasBasePos) + { + if (hasBasePos) + { + VectorSubtract(basePos, e.currentOrigin, velDir); + } + else + { + VectorSubtract(e.currentOrigin, parentOrigin, velDir); + } + } + else + { + VectorSubtract(e.currentOrigin, params->position, velDir); + } + + if (VectorLength(velDir) > 2.0f) + { //don't bother if already close + VectorNormalize(velDir); + VectorScale(velDir, 8.0f, velDir); + velDir[2] = 0; //don't want to nudge on Z, the gravity will take care of things. + VectorAdd(bone.epVelocity, velDir, bone.epVelocity); + } +#endif + + //Factor the object's velocity into the bone's velocity, by pushing the bone + //opposite the velocity to give the apperance the lighter limbs are being "dragged" + //behind those of greater mass. + if (bone.RagFlags & RAG_BONE_LIGHTWEIGHT) + { + vec3_t vel; + float vellen; + + VectorCopy(params->velocity, vel); + + //Scale down since our velocity scale is different from standard game physics + VectorScale(vel, 0.5f, vel); + + vellen = VectorLength(vel); + + if (vellen > 64.0f) + { //cap it off + VectorScale(vel, 64.0f/vellen, vel); + } + + //Invert the velocity so we go opposite the heavier parts and drag behind + VectorInverse(vel); + + if (vel[2]) + { //want to override entirely instead then + VectorCopy(vel, bone.epVelocity); + } + else + { + VectorAdd(bone.epVelocity, vel, bone.epVelocity); + } + } + + //We're not in solid so we can apply physics freely now. + if (!G2_ApplyRealBonePhysics(bone, e, params, goalSpot, NULL, testMins, testMaxs, gravity, mass, bounce)) + { + // if this is the case then somehow we failed to apply physics/get a good goal spot, just use the ent origin + VectorCopy(params->position, goalSpot); + } + } + + //Set this now so we know what to do for angle limiting + if (startSolid) + { + bone.solidCount++; + + Rag_Trace(&solidTr, params->position, testMins, testMaxs, e.currentOrigin, ignoreNum, RAG_MASK, G2_NOCOLLIDE, 0); + + if (solidTr.fraction != 1.0f && + (solidTr.plane.normal[0] || solidTr.plane.normal[1]) && + (solidTr.plane.normal[2] < 0.1f || solidTr.plane.normal[2] > -0.1f))// && //don't do anything against flat around + // e.currentOrigin[2] > pelvisPos[2]) + { //above pelvis, means not "even" with on ground level + SRagDollEffectorCollision args(e.currentOrigin, tr); + args.useTracePlane = false; + params->EffectorCollision(args); + } + +#ifdef _DEBUG_BONE_NAMES + if (bone.solidCount > 64) + { + char *debugBoneName = G2_Get_Bone_Name(&ghoul2V[0], ghoul2V[0].mBlist, bone.boneNumber); + vec3_t absmin, absmax; + + assert(debugBoneName); + + Com_Printf("High bone (%s, %i) solid count: %i\n", debugBoneName, bone.boneNumber, bone.solidCount); + + VectorAdd(e.currentOrigin, testMins, absmin); + VectorAdd(e.currentOrigin, testMaxs, absmax); + G2_RagDebugBox(absmin, absmax, 50); + + G2_RagDebugLine(e.currentOrigin, goalSpot, 50, 0x00ff00, 1); + } +#endif + } + else + { + bone.solidCount = 0; + } + +#if 0 //standard goalSpot capping? + //unless we are really in solid, we should keep adjustments minimal + if (/*bone.epGravFactor < 64 &&*/ bone.solidCount < 2 && + !inAir) + { + vec3_t moveDist; + const float extent = 32.0f; + float len; + + VectorSubtract(goalSpot, e.currentOrigin, moveDist); + len = VectorLength(moveDist); + + if (len > extent) + { //if greater than the extent then scale the vector down to the extent and factor it back into the goalspot + VectorScale(moveDist, extent/len, moveDist); + VectorAdd(e.currentOrigin, moveDist, goalSpot); + } + } +#endif + + //Set the desired direction based on the goal position and other factors. + for (k = 0; k < 3; k++) + { + e.desiredDirection[k] = (goalSpot[k] - e.currentOrigin[k]); + + if (broadsword_dircap && + broadsword_dircap->value) + { + float cap = broadsword_dircap->value; + + if (bone.solidCount > 5) + { + float solidFactor = bone.solidCount*0.2f; + + if (solidFactor > 16.0f) + { //don't go too high or something ugly might happen + solidFactor = 16.0f; + } + + e.desiredDirection[k] *= solidFactor; + cap *= 8; + } + + if (e.desiredDirection[k] > cap) + { + e.desiredDirection[k] = cap; + } + else if (e.desiredDirection[k] < -cap) + { + e.desiredDirection[k] = -cap; + } + } + + e.desiredDirection[k] += (velocityMultiplier * bone.velocityEffector[k]); + e.desiredDirection[k] += (flrand(-0.75f, 0.75f) * flrand(-0.75f, 0.75f)); + + bone.velocityEffector[k] *= velocityDampening; + } + VectorCopy(e.currentOrigin, bone.lastPosition); + } + + return anySolid; +} +#endif + +static float AngleNormZero(float theta) +{ + float ret=fmodf(theta,360.0f); + if (ret<-180.0f) + { + ret+=360.0f; + } + else if (ret>180.0f) + { + ret-=360.0f; + } + assert(ret>=-180.0f&&ret<=180.0f); + return ret; +} + +static inline void G2_BoneSnap(CGhoul2Info_v &ghoul2V, boneInfo_t &bone, CRagDollUpdateParams *params) +{ + /* + if ( !ri.CGVMLoaded() || !params ) + { + return; + } + + ragCallbackBoneSnap_t *callData = (ragCallbackBoneSnap_t *)ri.GetSharedMemory(); + + callData->entNum = params->me; + strcpy(callData->boneName, G2_Get_Bone_Name(&ghoul2V[0], ghoul2V[0].mBlist, bone.boneNumber)); + + ri.CGVM_RagCallback( RAG_CALLBACK_BONESNAP ); + */ +} + +static void G2_RagDollSolve(CGhoul2Info_v &ghoul2V,int g2Index,float decay,int frameNum,const vec3_t currentOrg,bool limitAngles,CRagDollUpdateParams *params) +{ + + int i; + + CGhoul2Info &ghoul2=ghoul2V[g2Index]; + + mdxaBone_t N; + mdxaBone_t P; + mdxaBone_t temp1; + mdxaBone_t temp2; + mdxaBone_t curRot; + mdxaBone_t curRotInv; + mdxaBone_t Gs[3]; + mdxaBone_t Enew[3]; + + assert(ghoul2.mFileName[0]); + boneInfo_v &blist = ghoul2.mBlist; + + + // END this is the objective function thing + for (i=0;i50.0f) + { + bone.velocityRoot[k]=50.0f; + } + if (bone.velocityRoot[k]<-50.0f) + { + bone.velocityRoot[k]=-50.0f; + } + */ + //No -rww + bone.ragOverrideMatrix.matrix[k][3]=bone.velocityRoot[k]; + } + } + } + else + { + vec3_t delAngles; + VectorClear(delAngles); + + for (k=0;k<3;k++) + { + tAngles[k]+=0.5f; + Create_Matrix(tAngles,&temp2); //dest 2nd arg + tAngles[k]-=0.5f; + Mat3x4_Multiply(&temp1,&P,&temp2); //dest first arg + Mat3x4_Multiply(&Gs[k],&temp1,&N); //dest first arg + + } + + int allSolidCount = 0;//bone.solidCount; + + // fixme precompute this + int numDep=G2_GetBoneDependents(ghoul2,bone.boneNumber,tempDependents,MAX_BONES_RAG); + int j; + int numRagDep=0; + for (j=0;jragIndex; + assert(depIndex>i); // these are supposed to be topologically sorted + assert(ragBoneData[depIndex]); + boneInfo_t &depBone=*ragBoneData[depIndex]; + if (depBone.RagFlags & RAG_EFFECTOR) // rag doll effector + { + // this is a dependent of me, and also a rag + numRagDep++; + for (k=0;k<3;k++) + { + Mat3x4_Multiply(&Enew[k],&Gs[k],&ragBones[depIndex]); //dest first arg + vec3_t tPosition; + tPosition[0]=Enew[k].matrix[0][3]; + tPosition[1]=Enew[k].matrix[1][3]; + tPosition[2]=Enew[k].matrix[2][3]; + + vec3_t change; + VectorSubtract(tPosition,ragEffectors[depIndex].currentOrigin,change); // dest is last arg + float goodness=DotProduct(change,ragEffectors[depIndex].desiredDirection); + assert( !Q_isnan(goodness)); + goodness*=depBone.weight; + delAngles[k]+=goodness; // keep bigger stuff more out of wall or something + assert( !Q_isnan(delAngles[k])); + } + allSolidCount += depBone.solidCount; + } + } + + //bone.solidCount = allSolidCount; + allSolidCount += bone.solidCount; + + VectorCopy(bone.currentAngles,bone.lastAngles); + // Update angles + float magicFactor9=0.75f; // dampfactor for angle changes + float magicFactor1=0.40f; //controls the speed of the gradient descent + float magicFactor32 = 1.5f; + float recip=0.0f; + if (numRagDep) + { + recip=sqrt(4.0f/float(numRagDep)); + } + + if (allSolidCount > 32) + { + magicFactor1 = 0.6f; + } + else if (allSolidCount > 10) + { + magicFactor1 = 0.5f; + } + + if (bone.overGradSpeed) + { //api call was made to specify a speed for this bone + magicFactor1 = bone.overGradSpeed; + } + + float fac=decay*recip*magicFactor1; + assert(fac>=0.0f); +#if 0 + if (bone.RagFlags & RAG_PCJ_PELVIS) + { + magicFactor9=.85f; // we don't want this swinging radically, make the whole thing kindof unstable + } +#endif + if (ragState==ERS_DYNAMIC) + { + magicFactor9=.85f; // we don't want this swinging radically, make the whole thing kindof unstable + } + +#if 1 //constraint breaks? + if (bone.RagFlags & RAG_UNSNAPPABLE) + { + magicFactor32 = 1.0f; + } +#endif + + for (k=0;k<3;k++) + { + bone.currentAngles[k]+=delAngles[k]*fac; + + bone.currentAngles[k]=(bone.lastAngles[k]-bone.currentAngles[k])*magicFactor9 + bone.currentAngles[k]; + bone.currentAngles[k]=AngleNormZero(bone.currentAngles[k]); +// bone.currentAngles[k]=flrand(bone.minAngles[k],bone.maxAngles[k]); +#if 1 //constraint breaks? + if (limitAngles && ( allSolidCount < 32 || (bone.RagFlags & RAG_UNSNAPPABLE) )) //32 tries and still in solid? Then we'll let you move freely +#else + if (limitAngles) +#endif + { + if (!bone.snapped || (bone.RagFlags & RAG_UNSNAPPABLE)) + { + //magicFactor32 += (allSolidCount/32); + + if (bone.currentAngles[k]>bone.maxAngles[k]*magicFactor32) + { + bone.currentAngles[k]=bone.maxAngles[k]*magicFactor32; + } + if (bone.currentAngles[k]bone.maxAngles[k]*magicFactor32) + { + isSnapped = true; + break; + } + if (bone.currentAngles[k]ragIndex; + if (!ragBoneData[depIndex]) + { + continue; + } + boneInfo_t &depBone=*ragBoneData[depIndex]; + + if (depBone.RagFlags & RAG_EFFECTOR) + { + // this is a dependent of me, and also a rag + numRagDep++; + for (k=0;k<3;k++) + { + Mat3x4_Multiply(&Enew[k],&Gs[k],&ragBones[depIndex]); //dest first arg + vec3_t tPosition; + tPosition[0]=Enew[k].matrix[0][3]; + tPosition[1]=Enew[k].matrix[1][3]; + tPosition[2]=Enew[k].matrix[2][3]; + + vec3_t change; + VectorSubtract(tPosition,ragEffectors[depIndex].currentOrigin,change); // dest is last arg + float goodness=DotProduct(change,ragEffectors[depIndex].desiredDirection); + assert( !Q_isnan(goodness)); + goodness*=depBone.weight; + delAngles[k]+=goodness; // keep bigger stuff more out of wall or something + assert( !Q_isnan(delAngles[k])); + } + } + } + + VectorCopy(bone.currentAngles, bone.lastAngles); + + // Update angles + float magicFactor9 = 0.75f; // dampfactor for angle changes + float magicFactor1 = bone.ikSpeed; //controls the speed of the gradient descent + float magicFactor32 = 1.0f; + float recip = 0.0f; + bool freeThisBone = false; + + if (!magicFactor1) + { + magicFactor1 = 0.40f; + } + + recip = sqrt(4.0f/1.0f); + + float fac = (decay*recip*magicFactor1); + assert(fac >= 0.0f); + + if (ragState == ERS_DYNAMIC) + { + magicFactor9 = 0.85f; // we don't want this swinging radically, make the whole thing kindof unstable + } + + + if (!bone.maxAngles[0] && !bone.maxAngles[1] && !bone.maxAngles[2] && + !bone.minAngles[0] && !bone.minAngles[1] && !bone.minAngles[2]) + { + freeThisBone = true; + } + + for (k = 0; k < 3; k++) + { + bone.currentAngles[k] += delAngles[k]*fac; + + bone.currentAngles[k] = (bone.lastAngles[k]-bone.currentAngles[k])*magicFactor9 + bone.currentAngles[k]; + bone.currentAngles[k] = AngleNormZero(bone.currentAngles[k]); + if (limitAngles && !freeThisBone) + { + if (bone.currentAngles[k] > bone.maxAngles[k]*magicFactor32) + { + bone.currentAngles[k] = bone.maxAngles[k]*magicFactor32; + } + if (bone.currentAngles[k] < bone.minAngles[k]*magicFactor32) + { + bone.currentAngles[k] = bone.minAngles[k]*magicFactor32; + } + } + } + Create_Matrix(bone.currentAngles, &temp1); + Mat3x4_Multiply(&temp2, &temp1, bone.baseposeInv); + Mat3x4_Multiply(&bone.ragOverrideMatrix, bone.basepose, &temp2); + //bone.ragOverrideMatrix = *bone.basepose * (temp1 * *bone.baseposeInv); + assert( !Q_isnan(bone.ragOverrideMatrix.matrix[2][3])); + + G2_Generate_MatrixRag(blist, ragBlistIndex[bone.boneNumber]); + } +} + +static void G2_DoIK(CGhoul2Info_v &ghoul2V,int g2Index,CRagDollUpdateParams *params) +{ + int i; + + if (!params) + { + assert(0); + return; + } + + int frameNum=G2API_GetTime(0); + CGhoul2Info &ghoul2=ghoul2V[g2Index]; + assert(ghoul2.mFileName[0]); + + float decay=1.0f; + bool resetOrigin=false; + bool anyRendered=false; + + int iters = 12; //since we don't trace or anything, we can afford this. + + if (iters) + { + if (!G2_RagDollSetup(ghoul2,frameNum,resetOrigin,params->position,anyRendered)) + { + return; + } + + // ok, now our data structures are compact and set up in topological order + for (i=0;iangles,params->position,params->scale); + + G2_IKReposition(params->position, params); + + G2_IKSolve(ghoul2V,g2Index,decay*2.0f,frameNum,params->position,true); + } + } + + if (params->me != ENTITYNUM_NONE) + { + G2_RagDollCurrentPosition(ghoul2V,g2Index,frameNum,params->angles,params->position,params->scale); + } +} + +static int G2_Set_Bone_Angles_IK( + CGhoul2Info &ghoul2, + const mdxaHeader_t *mod_a, + boneInfo_v &blist, + const char *boneName, + const int flags, + const float radius, + const vec3_t angleMin=0, + const vec3_t angleMax=0, + const int blendTime=500) +{ + int index = G2_Find_Bone_Rag(&ghoul2, blist, boneName); + + if (index == -1) + { + index = G2_Add_Bone(ghoul2.animModel, blist, boneName); + } + if (index != -1) + { + boneInfo_t &bone=blist[index]; + bone.flags |= BONE_ANGLES_IK; + bone.flags &= ~BONE_ANGLES_RAGDOLL; + + bone.ragStartTime=G2API_GetTime(0); + bone.radius=radius; + bone.weight=1.0f; + + if (angleMin&&angleMax) + { + VectorCopy(angleMin,bone.minAngles); + VectorCopy(angleMax,bone.maxAngles); + } + else + { + VectorCopy(bone.currentAngles,bone.minAngles); // I guess this isn't a rag pcj then + VectorCopy(bone.currentAngles,bone.maxAngles); + } + if (!bone.lastTimeUpdated) + { + static mdxaBone_t id = + { + { + { 1.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f, 0.0f } + } + }; + memcpy(&bone.ragOverrideMatrix,&id, sizeof(mdxaBone_t)); + VectorClear(bone.anglesOffset); + VectorClear(bone.positionOffset); + VectorClear(bone.velocityEffector); // this is actually a velocity now + VectorClear(bone.velocityRoot); // this is actually a velocity now + VectorClear(bone.lastPosition); + VectorClear(bone.lastShotDir); + bone.lastContents=0; + // if this is non-zero, we are in a dynamic state + bone.firstCollisionTime=bone.ragStartTime; + // if this is non-zero, we are in a settling state + bone.restTime=0; + // if they are both zero, we are in a settled state + + bone.firstTime=0; + + bone.RagFlags=flags; + bone.DependentRagIndexMask=0; + + G2_Generate_MatrixRag(blist,index); // set everything to th id + + VectorClear(bone.currentAngles); + VectorCopy(bone.currentAngles,bone.lastAngles); + } + } + return index; +} + +void G2_InitIK(CGhoul2Info_v &ghoul2V, sharedRagDollUpdateParams_t *parms, int time, const mdxaHeader_t *mod_a, int model) +{ + CGhoul2Info &ghoul2=ghoul2V[model]; + int curTime=time; + boneInfo_v &blist = ghoul2.mBlist; + + G2_GenerateWorldMatrix(parms->angles, parms->position); + G2_ConstructGhoulSkeleton(ghoul2V, curTime, false, parms->scale); + + // new base anim, unconscious flop + int pcjFlags; + + //Only need the standard effectors for this. + pcjFlags = RAG_PCJ | RAG_PCJ_POST_MULT | RAG_EFFECTOR; + + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "rhand", pcjFlags, 6.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "lhand", pcjFlags, 6.0f); + // G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"rtarsal",pcjFlags,4.0f); + // G2_Set_Bone_Angles_IK(ghoul2, mod_a,blist,"ltarsal",pcjFlags,4.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "rtibia", pcjFlags, 4.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "ltibia", pcjFlags, 4.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "rtalus", pcjFlags, 4.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "ltalus", pcjFlags, 4.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "rradiusX", pcjFlags, 6.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "lradiusX", pcjFlags, 6.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "rfemurX", pcjFlags, 10.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "lfemurX", pcjFlags, 10.0f); + G2_Set_Bone_Angles_IK(ghoul2, mod_a, blist, "ceyebrow", pcjFlags, 10.0f); +} + +qboolean G2_SetBoneIKState(CGhoul2Info_v &ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params) +{ + model_t *mod_a; + int g2index = 0; + int curTime = time; + CGhoul2Info &g2 = ghoul2[g2index]; + const mdxaHeader_t *rmod_a = G2_GetModA(g2); + + boneInfo_v &blist = g2.mBlist; + mod_a = (model_t *)g2.animModel; + + if (!boneName) + { //null bonename param means it's time to init the ik stuff on this instance + sharedRagDollUpdateParams_t sRDUP; + + if (ikState == IKS_NONE) + { //this means we want to reset the IK state completely.. run through the bone list, and reset all the appropriate flags + size_t i = 0; + while (i < blist.size()) + { //we can't use this method for ragdoll. However, since we expect them to set their anims/angles again on the PCJ + //limb after they reset it gameside, it's reasonable for IK bones. + boneInfo_t &bone = blist[i]; + if (bone.boneNumber != -1) + { + bone.flags &= ~BONE_ANGLES_RAGDOLL; + bone.flags &= ~BONE_ANGLES_IK; + bone.RagFlags = 0; + bone.lastTimeUpdated = 0; + } + i++; + } + return qtrue; + } + assert(params); + + if (!params) + { + return qfalse; + } + + sRDUP.me = 0; + VectorCopy(params->angles, sRDUP.angles); + VectorCopy(params->origin, sRDUP.position); + VectorCopy(params->scale, sRDUP.scale); + VectorClear(sRDUP.velocity); + G2_InitIK(ghoul2, &sRDUP, curTime, rmod_a, g2index); + return qtrue; + } + + if (!rmod_a || !mod_a) + { + return qfalse; + } + + int index = G2_Find_Bone(mod_a, blist, boneName); + + if (index == -1) + { + index = G2_Add_Bone(mod_a, blist, boneName); + } + + if (index == -1) + { //couldn't find or add the bone.. + return qfalse; + } + + boneInfo_t &bone = blist[index]; + + if (ikState == IKS_NONE) + { //remove the bone from the list then, so it has to reinit. I don't think this should hurt anything since + //we don't store bone index handles gameside anywhere. + if (!(bone.flags & BONE_ANGLES_RAGDOLL)) + { //you can't set the ik state to none if it's not a rag/ik bone. + return qfalse; + } + //bone.flags = 0; + //G2_Remove_Bone_Index(blist, index); + //actually, I want to keep it on the rag list, and remove it as an IK bone instead. + bone.flags &= ~BONE_ANGLES_RAGDOLL; + bone.flags |= BONE_ANGLES_IK; + bone.RagFlags &= ~RAG_PCJ_IK_CONTROLLED; + return qtrue; + } + + //need params if we're not resetting. + if (!params) + { + assert(0); + return qfalse; + } + + /* + if (bone.flags & BONE_ANGLES_RAGDOLL) + { //otherwise if the bone is already flagged as rag, then we can't set it again. (non-active ik bones will be BONE_ANGLES_IK, active are considered rag) + return qfalse; + } + */ + + G2_GenerateWorldMatrix(params->angles, params->origin); + G2_ConstructGhoulSkeleton(ghoul2, curTime, false, params->scale); + + int pcjFlags = RAG_PCJ | RAG_PCJ_IK_CONTROLLED | RAG_PCJ_POST_MULT | RAG_EFFECTOR; + + if (params->pcjOverrides) + { + pcjFlags = params->pcjOverrides; + } + + bone.ikSpeed = 0.4f; + VectorClear(bone.ikPosition); + + G2_Set_Bone_Rag(rmod_a, blist, boneName, g2, params->scale, params->origin); + + int startFrame = params->startFrame, endFrame = params->endFrame; + + if (bone.startFrame != startFrame || bone.endFrame != endFrame) + { //if it's already on this anim leave it alone, to allow smooth transitions into IK on the current anim if it is so desired. + G2_Set_Bone_Anim_No_BS(g2, rmod_a, blist, boneName, startFrame, endFrame-1, + BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, + 1.0f, curTime, float(startFrame), 150, 0, true); + } + + G2_ConstructGhoulSkeleton(ghoul2, curTime, false, params->scale); + + bone.lastTimeUpdated = 0; + G2_Set_Bone_Angles_Rag(g2, rmod_a, blist, boneName, pcjFlags, params->radius, params->pcjMins, params->pcjMaxs, params->blendTime); + + if (!G2_RagDollSetup(g2,curTime,true,params->origin,false)) + { + assert(!"failed to add any rag bones"); + return qfalse; + } + + return qtrue; +} + +qboolean G2_IKMove(CGhoul2Info_v &ghoul2, int time, sharedIKMoveParams_t *params) +{ +#if 0 + model_t *mod_a; + int g2index = 0; + int curTime = time; + CGhoul2Info &g2 = ghoul2[g2index]; + + boneInfo_v &blist = g2.mBlist; + mod_a = (model_t *)g2.animModel; + + if (!mod_a) + { + return qfalse; + } + + int index = G2_Find_Bone(mod_a, blist, params->boneName); + + //don't add here if you can't find it.. ik bones should already be there, because they need to have special stuff done to them anyway. + if (index == -1) + { //couldn't find the bone.. + return qfalse; + } + + if (!params) + { + assert(0); + return qfalse; + } + + if (!(blist[index].flags & BONE_ANGLES_RAGDOLL) && !(blist[index].flags & BONE_ANGLES_IK)) + { //no-can-do, buddy + return qfalse; + } + + VectorCopy(params->desiredOrigin, blist[index].ikPosition); + blist[index].ikSpeed = params->movementSpeed; +#else + int g2index = 0; + int curTime = time; + CGhoul2Info &g2 = ghoul2[g2index]; + + //rwwFIXMEFIXME: Doing this on all bones at the moment, fix this later? + if (!G2_RagDollSetup(g2,curTime,true,params->origin,false)) + { //changed models, possibly. + return qfalse; + } + + for (int i=0;idesiredOrigin, bone.ikPosition); + bone.ikSpeed = params->movementSpeed; + } + } +#endif + return qtrue; +} + +// set the bone list to all unused so the bone transformation routine ignores it. +void G2_Init_Bone_List(boneInfo_v &blist, int numBones) +{ + blist.clear(); + blist.reserve(numBones); +} + +void G2_RemoveRedundantBoneOverrides(boneInfo_v &blist, int *activeBones) +{ + // walk the surface list, removing surface overrides or generated surfaces that are pointing at surfaces that aren't active anymore + for (size_t i=0; imFileName)); + model_t *mod_a = R_GetModelByHandle(mod_m->data.glm->header->animIndex); + + return (G2_Find_Bone(mod_a, ghoul2->mBlist, boneName)); +} + +int G2_Get_Bone_Index(CGhoul2Info* ghoul2, const char* boneName, qboolean bAddIfNotFound) +{ + if (bAddIfNotFound) + { + return G2_Add_Bone(ghoul2->animModel, ghoul2->mBlist, boneName); + } + else + { + return G2_Find_Bone(ghoul2, ghoul2->mBlist, boneName); + } +} \ No newline at end of file diff --git a/code/rd-rend2/G2_misc.cpp b/code/rd-rend2/G2_misc.cpp new file mode 100644 index 0000000000..e0bf23b804 --- /dev/null +++ b/code/rd-rend2/G2_misc.cpp @@ -0,0 +1,2003 @@ +#include "server/exe_headers.h" + +#ifndef __Q_SHARED_H + #include "qcommon/q_shared.h" +#endif + +#include "rd-common/tr_common.h" + +#if !defined(TR_LOCAL_H) + #include "tr_local.h" +#endif + +#include "qcommon/matcomp.h" + +#if !defined(G2_H_INC) + #include "ghoul2/G2.h" +#endif + +#if !defined (MINIHEAP_H_INC) + #include "qcommon/MiniHeap.h" +#endif + +#define G2_MODEL_OK(g) ((g)&&(g)->mValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) + +#include "server/server.h" +#include +#include "qcommon/ojk_saved_game_helper.h" + +#ifdef _G2_GORE +#include "G2_gore_r2.h" +//#include "G2_gore_r2.h" + +#define GORE_TAG_UPPER (256) +#define GORE_TAG_MASK (~255) +#define MAX_GORE_RECORDS (500) + +static int CurrentTag=GORE_TAG_UPPER+1; +static int CurrentTagUpper=GORE_TAG_UPPER; + +static std::map GoreRecords; +static std::map,int> GoreTagsTemp; // this is a surface index to gore tag map used only + // temporarily during the generation phase so we reuse gore tags per LOD +int goreModelIndex; + +static cvar_t *cg_g2MarksAllModels=NULL; + + +static inline void DestroyGoreTexCoordinates(int tag) +{ + R2GoreTextureCoordinates* gTC = FindR2GoreRecord(tag); + if (!gTC) + { + return; + } + gTC->~R2GoreTextureCoordinates(); + //I don't know what's going on here, it should call the destructor for + //this when it erases the record but sometimes it doesn't. -rww +} + + +int AllocR2GoreRecord() +{ + while (GoreRecords.size()>MAX_GORE_RECORDS) + { + int tagHigh=(*GoreRecords.begin()).first&GORE_TAG_MASK; + std::map::iterator it; + R2GoreTextureCoordinates* gTC; + + it = GoreRecords.begin(); + gTC = &(*it).second; + + if (gTC) + { + gTC->~R2GoreTextureCoordinates(); + } + GoreRecords.erase(GoreRecords.begin()); + while (GoreRecords.size()) + { + if (((*GoreRecords.begin()).first&GORE_TAG_MASK)!=tagHigh) + { + break; + } + it = GoreRecords.begin(); + gTC = &(*it).second; + + if (gTC) + { + gTC->~R2GoreTextureCoordinates(); + } + GoreRecords.erase(GoreRecords.begin()); + } + } + int ret=CurrentTag; + GoreRecords[CurrentTag] = R2GoreTextureCoordinates(); + CurrentTag++; + return ret; +} + +void ResetGoreTag() +{ + GoreTagsTemp.clear(); + CurrentTag=CurrentTagUpper; + CurrentTagUpper+=GORE_TAG_UPPER; +} + +R2GoreTextureCoordinates* FindR2GoreRecord(int tag) +{ + std::map::iterator i=GoreRecords.find(tag); + if (i!=GoreRecords.end()) + { + return &(*i).second; + } + return 0; +} + +GoreTextureCoordinates* FindGoreRecord(int tag) +{ + std::map::iterator i = GoreRecords.find(tag); + if (i != GoreRecords.end()) + { + return (GoreTextureCoordinates*)(&(*i).second); + } + return 0; +} + +void *G2_GetGoreRecord(int tag) +{ + return FindGoreRecord(tag); +} + +void DeleteR2GoreRecord(int tag) +{ + DeleteGoreRecord(tag); +} + +void DeleteGoreRecord(int tag) +{ + DestroyGoreTexCoordinates(tag); + GoreRecords.erase(tag); +} + +static int CurrentGoreSet=1; // this is a UUID for gore sets +static std::map GoreSets; // map from uuid to goreset + +CGoreSet *FindGoreSet(int goreSetTag) +{ + std::map::iterator f=GoreSets.find(goreSetTag); + if (f!=GoreSets.end()) + { + return (*f).second; + } + return 0; +} + +CGoreSet *NewGoreSet() +{ + CGoreSet *ret=new CGoreSet(CurrentGoreSet++); + GoreSets[ret->mMyGoreSetTag]=ret; + ret->mRefCount = 1; + return ret; +} + +void DeleteGoreSet(int goreSetTag) +{ + std::map::iterator f = GoreSets.find(goreSetTag); + if (f != GoreSets.end()) + { + if ( (*f).second->mRefCount == 0 || (*f).second->mRefCount - 1 == 0 ) + { + delete (*f).second; + GoreSets.erase(f); + } + else + { + (*f).second->mRefCount--; + } + } +} + + +CGoreSet::~CGoreSet() +{ + std::multimap::iterator i; + for (i=mGoreRecords.begin();i!=mGoreRecords.end();i++) + { + DeleteGoreRecord((*i).second.mGoreTag); + } +}; +#endif // _SOF2 + +extern mdxaBone_t worldMatrix; +extern mdxaBone_t worldMatrixInv; + +const mdxaBone_t &EvalBoneCache(int index,CBoneCache *boneCache); + +#ifdef _MSC_VER +#pragma warning(disable : 4512) //assignment op could not be genereated +#endif +class CTraceSurface +{ +public: + int surfaceNum; + surfaceInfo_v& rootSList; + const model_t* currentModel; + const int lod; + vec3_t rayStart; + vec3_t rayEnd; + CCollisionRecord* collRecMap; + const int entNum; + const int modelIndex; + const skin_t* skin; +#ifdef _WIN32 + const struct shader_t* cust_shader; +#else + const shader_t* cust_shader; +#endif + intptr_t* TransformedVertsArray; + const EG2_Collision eG2TraceType; + bool hitOne; + float m_fRadius; + +#ifdef _G2_GORE + //gore application thing + float ssize; + float tsize; + float theta; + int goreShader; + CGhoul2Info* ghoul2info; + + // Procedural-gore application things + SSkinGoreData* gore; +#endif + + CTraceSurface( + int initsurfaceNum, + surfaceInfo_v& initrootSList, + const model_t* initcurrentModel, + int initlod, + vec3_t initrayStart, + vec3_t initrayEnd, + CCollisionRecord* initcollRecMap, + int initentNum, + int initmodelIndex, + const skin_t* initskin, + const shader_t* initcust_shader, + intptr_t* initTransformedVertsArray, + const EG2_Collision einitG2TraceType, +#ifdef _G2_GORE + float fRadius, + float initssize, + float inittsize, + float inittheta, + int initgoreShader, + CGhoul2Info* initghoul2info, + SSkinGoreData* initgore +#else + float fRadius +#endif + ) + : surfaceNum(initsurfaceNum) + , rootSList(initrootSList) + , currentModel(initcurrentModel) + , lod(initlod) + , collRecMap(initcollRecMap) + , entNum(initentNum) + , modelIndex(initmodelIndex) + , skin(initskin) + , cust_shader(initcust_shader) + , TransformedVertsArray(initTransformedVertsArray) + , eG2TraceType(einitG2TraceType) + , hitOne(false) +#ifdef _G2_GORE + , m_fRadius(fRadius) + , ssize(initssize) + , tsize(inittsize) + , theta(inittheta) + , goreShader(initgoreShader) + , ghoul2info(initghoul2info) + , gore(initgore) +#else + , m_fRadius(fRadius) +#endif + { + VectorCopy(initrayStart, rayStart); + VectorCopy(initrayEnd, rayEnd); + } + +}; + +// assorted Ghoul 2 functions. +// list all surfaces associated with a model +void G2_List_Model_Surfaces(const char *fileName) +{ + int i, x; + model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); + mdxmSurfHierarchy_t *surf; + mdxmHeader_t *mdxm = mod_m->data.glm->header; + + surf = (mdxmSurfHierarchy_t *) ( (byte *)mdxm + mdxm->ofsSurfHierarchy ); + mdxmSurface_t *surface = (mdxmSurface_t *)((byte *)mdxm + mdxm->ofsLODs + sizeof(mdxmLOD_t)); + + for ( x = 0 ; x < mdxm->numSurfaces ; x++) + { + Com_Printf("Surface %i Name %s\n", x, surf->name); + if ( r_verbose->integer ) + { + Com_Printf("Num Descendants %i\n", surf->numChildren); + for (i=0; inumChildren; i++) + { + Com_Printf("Descendant %i\n", surf->childIndexes[i]); + } + } + // find the next surface + surf = (mdxmSurfHierarchy_t *)( (byte *)surf + (size_t)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surf->numChildren ] )); + surface =(mdxmSurface_t *)( (byte *)surface + surface->ofsEnd ); + } + +} + +// list all bones associated with a model +void G2_List_Model_Bones(const char *fileName, int frame) +{ + int x, i; + mdxaSkel_t *skel; + mdxaSkelOffsets_t *offsets; + model_t *mod_m = R_GetModelByHandle(RE_RegisterModel(fileName)); + model_t *mod_a = R_GetModelByHandle(mod_m->data.glm->header->animIndex); +// mdxaFrame_t *aframe=0; +// int frameSize; + mdxaHeader_t *header = mod_a->data.gla; + + // figure out where the offset list is + offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t)); + +// frameSize = (size_t)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] ); + +// aframe = (mdxaFrame_t *)((byte *)header + header->ofsFrames + (frame * frameSize)); + // walk each bone and list it's name + for (x=0; x< header->numBones; x++) + { + skel = (mdxaSkel_t *)((byte *)header + sizeof(mdxaHeader_t) + offsets->offsets[x]); + Com_Printf("Bone %i Name %s\n", x, skel->name); + + Com_Printf("X pos %f, Y pos %f, Z pos %f\n", skel->BasePoseMat.matrix[0][3], skel->BasePoseMat.matrix[1][3], skel->BasePoseMat.matrix[2][3]); + + // if we are in verbose mode give us more details + if (r_verbose->integer) + { + Com_Printf("Num Descendants %i\n", skel->numChildren); + for (i=0; inumChildren; i++) + { + Com_Printf("Num Descendants %i\n", skel->numChildren); + } + } + } +} + + +/************************************************************************************************ + * G2_GetAnimFileName + * obtain the .gla filename for a model + * + * Input + * filename of model + * + * Output + * true if we successfully obtained a filename, false otherwise + * + ************************************************************************************************/ +qboolean G2_GetAnimFileName(const char *fileName, char **filename) +{ + // find the model we want + model_t *mod = R_GetModelByHandle(RE_RegisterModel(fileName)); + + if (mod) + { + mdxmHeader_t *mdxm = mod->data.glm->header; + if (mdxm && mdxm->animName[0] != 0) + { + *filename = mdxm->animName; + return qtrue; + } + } + return qfalse; +} + + +///////////////////////////////////////////////////////////////////// +// +// Code for collision detection for models gameside +// +///////////////////////////////////////////////////////////////////// + +int G2_DecideTraceLod(CGhoul2Info &ghoul2, int useLod) +{ + int returnLod = useLod; + + // if we are overriding the LOD at top level, then we can afford to only check this level of model + if (ghoul2.mLodBias > returnLod) + { + returnLod = ghoul2.mLodBias; + } + assert(G2_MODEL_OK(&ghoul2)); + + mdxmHeader_t *mdxm = ghoul2.currentModel->data.glm->header; + assert(ghoul2.currentModel); + assert(mdxm); + //what about r_lodBias? + + // now ensure that we haven't selected a lod that doesn't exist for this model + if ( returnLod >= mdxm->numLODs ) + { + returnLod = mdxm->numLODs - 1; + } + + return returnLod; +} + +void R_TransformEachSurface( const mdxmSurface_t *surface, vec3_t scale, CMiniHeap *G2VertSpace, intptr_t *TransformedVertsArray, CBoneCache *boneCache) +{ + int j, k; + mdxmVertex_t *v; + float *TransformedVerts; + + // + // deform the vertexes by the lerped bones + // + int *piBoneReferences = (int*) ((byte*)surface + surface->ofsBoneReferences); + + // alloc some space for the transformed verts to get put in + TransformedVerts = (float *)G2VertSpace->MiniHeapAlloc(surface->numVerts * 5 * 4); + TransformedVertsArray[surface->thisSurfaceIndex] = (intptr_t)TransformedVerts; + if (!TransformedVerts) + { + assert(TransformedVerts); + Com_Error(ERR_DROP, "Ran out of transform space for Ghoul2 Models. Adjust G2_MINIHEAP_SIZE in sv_init.cpp.\n"); + } + + // whip through and actually transform each vertex + const int numVerts = surface->numVerts; + v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts); + mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts]; + + // optimisation issue + if ((scale[0] != 1.0) || (scale[1] != 1.0) || (scale[2] != 1.0)) + { + for ( j = 0; j < numVerts; j++ ) + { + vec3_t tempVert, tempNormal; +// mdxmWeight_t *w; + + VectorClear( tempVert ); + VectorClear( tempNormal ); +// w = v->weights; + + const int iNumWeights = G2_GetVertWeights( v ); + + float fTotalWeight = 0.0f; + for ( k = 0 ; k < iNumWeights ; k++ ) + { + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights ); + + const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex],boneCache); + + tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); + tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); + tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + + tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal ); + tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal ); + tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal ); + } + int pos = j * 5; + + // copy tranformed verts into temp space + TransformedVerts[pos++] = tempVert[0] * scale[0]; + TransformedVerts[pos++] = tempVert[1] * scale[1]; + TransformedVerts[pos++] = tempVert[2] * scale[2]; + // we will need the S & T coors too for hitlocation and hitmaterial stuff + TransformedVerts[pos++] = pTexCoords[j].texCoords[0]; + TransformedVerts[pos] = pTexCoords[j].texCoords[1]; + + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + } + } + else + { + int pos = 0; + for ( j = 0; j < numVerts; j++ ) + { + vec3_t tempVert, tempNormal; +// const mdxmWeight_t *w; + + VectorClear( tempVert ); + VectorClear( tempNormal ); +// w = v->weights; + + const int iNumWeights = G2_GetVertWeights( v ); + + float fTotalWeight = 0.0f; + for ( k = 0 ; k < iNumWeights ; k++ ) + { + int iBoneIndex = G2_GetVertBoneIndex( v, k ); + float fBoneWeight = G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights ); + + const mdxaBone_t &bone=EvalBoneCache(piBoneReferences[iBoneIndex], boneCache); + + tempVert[0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] ); + tempVert[1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] ); + tempVert[2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] ); + + tempNormal[0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal ); + tempNormal[1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal ); + tempNormal[2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal ); + } + + // copy tranformed verts into temp space + TransformedVerts[pos++] = tempVert[0]; + TransformedVerts[pos++] = tempVert[1]; + TransformedVerts[pos++] = tempVert[2]; + // we will need the S & T coors too for hitlocation and hitmaterial stuff + TransformedVerts[pos++] = pTexCoords[j].texCoords[0]; + TransformedVerts[pos++] = pTexCoords[j].texCoords[1]; + + v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights]; + } + } +} + +void G2_TransformSurfaces(int surfaceNum, surfaceInfo_v &rootSList, + CBoneCache *boneCache, const model_t *currentModel, int lod, vec3_t scale, CMiniHeap *G2VertSpace, intptr_t* TransformedVertArray, bool secondTimeAround) +{ + int i; + assert(currentModel); + assert(currentModel->data.glm && currentModel->data.glm->header); + // back track and get the surfinfo struct for this surface + const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(currentModel, surfaceNum, lod); + const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)currentModel->data.glm->header + sizeof(mdxmHeader_t)); + const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); + + // see if we have an override surface in the surface list + const surfaceInfo_t *surfOverride = G2_FindOverrideSurface(surfaceNum, rootSList); + + // really, we should use the default flags for this surface unless it's been overriden + int offFlags = surfInfo->flags; + + if (surfOverride) + { + offFlags = surfOverride->offFlags; + } + // if this surface is not off, add it to the shader render list + if (!offFlags) + { + + R_TransformEachSurface(surface, scale, G2VertSpace, TransformedVertArray, boneCache); + } + + // if we are turning off all descendants, then stop this recursion now + if (offFlags & G2SURFACEFLAG_NODESCENDANTS) + { + return; + } + + // now recursively call for the children + for (i=0; i< surfInfo->numChildren; i++) + { + G2_TransformSurfaces(surfInfo->childIndexes[i], rootSList, boneCache, currentModel, lod, scale, G2VertSpace, TransformedVertArray, secondTimeAround); + } +} + +// main calling point for the model transform for collision detection. At this point all of the skeleton has been transformed. +#ifdef _G2_GORE +void G2_TransformModel(CGhoul2Info_v& ghoul2, const int frameNum, vec3_t scale, CMiniHeap* G2VertSpace, int useLod, bool ApplyGore, SSkinGoreData* gore) +#else +void G2_TransformModel(CGhoul2Info_v& ghoul2, const int frameNum, vec3_t scale, CMiniHeap* G2VertSpace, int useLod) +#endif +{ + int i, lod; + vec3_t correctScale; + +#if !defined(JK2_MODE) || defined(_G2_GORE) + qboolean firstModelOnly = qfalse; +#endif // !JK2_MODE || _G2_GORE + +#ifndef JK2_MODE + if ( cg_g2MarksAllModels == NULL ) + { + cg_g2MarksAllModels = ri.Cvar_Get( "cg_g2MarksAllModels", "0", 0 ); + } + + if (cg_g2MarksAllModels == NULL || !cg_g2MarksAllModels->integer ) + { + firstModelOnly = qtrue; + } +#endif + +#ifdef _G2_GORE + if (gore && gore->firstModel > 0) + { + firstModelOnly = qfalse; + } +#endif + + VectorCopy(scale, correctScale); + // check for scales of 0 - that's the default I believe + if (!scale[0]) + { + correctScale[0] = 1.0; + } + if (!scale[1]) + { + correctScale[1] = 1.0; + } + if (!scale[2]) + { + correctScale[2] = 1.0; + } + + // walk each possible model for this entity and try rendering it out + for (i=0; i=g.currentModel->numLods) + { + g.mTransformedVertsArray = 0; + if (firstModelOnly) + { + // we don't really need to do multiple models for gore. + return; + } + //do the rest + continue; + } + } + else + { +#endif + lod = G2_DecideTraceLod(g, useLod); +#ifdef _G2_GORE + } +#endif + + mdxmHeader_t *mdxm = g.currentModel->data.glm->header; +#ifndef REND2_SP + // give us space for the transformed vertex array to be put in + if (!(g.mFlags & GHOUL2_ZONETRANSALLOC)) + { +#endif + //do not stomp if we're using zone space + g.mTransformedVertsArray = (intptr_t*)G2VertSpace->MiniHeapAlloc(mdxm->numSurfaces * sizeof(intptr_t)); + if (!g.mTransformedVertsArray) + { + Com_Error(ERR_DROP, "Ran out of transform space for Ghoul2 Models. Adjust G2_MINIHEAP_SIZE in sv_init.cpp.\n"); + } +#ifndef REND2_SP + } +#endif + memset(g.mTransformedVertsArray, 0,mdxm->numSurfaces * sizeof (intptr_t)); + + G2_FindOverrideSurface(-1,g.mSlist); //reset the quick surface override lookup; + // recursively call the model surface transform + + G2_TransformSurfaces(g.mSurfaceRoot, g.mSlist, g.mBoneCache, g.currentModel, lod, correctScale, G2VertSpace, g.mTransformedVertsArray, false); + +#ifdef _G2_GORE + if (ApplyGore && firstModelOnly) + { + // we don't really need to do multiple models for gore. + break; + } +#endif + } +} + + +// work out how much space a triangle takes +static float G2_AreaOfTri(const vec3_t A, const vec3_t B, const vec3_t C) +{ + vec3_t cross, ab, cb; + VectorSubtract(A, B, ab); + VectorSubtract(C, B, cb); + + CrossProduct(ab, cb, cross); + + return VectorLength(cross); +} + +// actually determine the S and T of the coordinate we hit in a given poly +static void G2_BuildHitPointST( const vec3_t A, const float SA, const float TA, + const vec3_t B, const float SB, const float TB, + const vec3_t C, const float SC, const float TC, + const vec3_t P, float *s, float *t,float &bary_i,float &bary_j) +{ + float areaABC = G2_AreaOfTri(A, B, C); + + float i = G2_AreaOfTri(P, B, C) / areaABC; + bary_i=i; + float j = G2_AreaOfTri(A, P, C) / areaABC; + bary_j=j; + float k = G2_AreaOfTri(A, B, P) / areaABC; + + *s = SA * i + SB * j + SC * k; + *t = TA * i + TB * j + TC * k; + + *s=fmod(*s, 1); + if (*s< 0) + { + *s+= 1.0; + } + + *t=fmod(*t, 1); + if (*t< 0) + { + *t+= 1.0; + } + +} + + +// routine that works out given a ray whether or not it hits a poly +static inline qboolean G2_SegmentTriangleTest( const vec3_t start, const vec3_t end, + const vec3_t A, const vec3_t B, const vec3_t C, + qboolean backFaces,qboolean frontFaces,vec3_t returnedPoint,vec3_t returnedNormal, float *denom) +{ + static const float tiny=1E-10f; + vec3_t returnedNormalT; + vec3_t edgeAC; + + VectorSubtract(C, A, edgeAC); + VectorSubtract(B, A, returnedNormalT); + + CrossProduct(returnedNormalT, edgeAC, returnedNormal); + + vec3_t ray; + VectorSubtract(end, start, ray); + + *denom=DotProduct(ray, returnedNormal); + + if (Q_fabs(*denom)0)|| // not accepting back faces + (!frontFaces && *denom<0)) //not accepting front faces + { + return qfalse; + } + + vec3_t toPlane; + VectorSubtract(A, start, toPlane); + + float t=DotProduct(toPlane, returnedNormal)/ *denom; + + if (t<0.0f||t>1.0f) + { + return qfalse; // off segment + } + + VectorScale(ray, t, ray); + + VectorAdd(ray, start, returnedPoint); + + vec3_t edgePA; + VectorSubtract(A, returnedPoint, edgePA); + + vec3_t edgePB; + VectorSubtract(B, returnedPoint, edgePB); + + vec3_t edgePC; + VectorSubtract(C, returnedPoint, edgePC); + + vec3_t temp; + + CrossProduct(edgePA, edgePB, temp); + if (DotProduct(temp, returnedNormal)<0.0f) + { + return qfalse; // off triangle + } + + CrossProduct(edgePC, edgePA, temp); + if (DotProduct(temp,returnedNormal)<0.0f) + { + return qfalse; // off triangle + } + + CrossProduct(edgePB, edgePC, temp); + if (DotProduct(temp, returnedNormal)<0.0f) + { + return qfalse; // off triangle + } + return qtrue; +} + +#ifdef _G2_GORE +struct SVertexTemp +{ + int flags; + int touch; + int newindex; + float tex[2]; + SVertexTemp() + { + touch=0; + } +}; + +#define MAX_GORE_VERTS (3000) +static SVertexTemp GoreVerts[MAX_GORE_VERTS]; +static int GoreIndexCopy[MAX_GORE_VERTS]; +static int GoreTouch=1; + +#define MAX_GORE_INDECIES (6000) +static int GoreIndecies[MAX_GORE_INDECIES]; + +#define GORE_MARGIN (0.0f) +int G2API_GetTime(int argTime); + +// now we at poly level, check each model space transformed poly against the model world transfomed ray +static void G2_GorePolys(const mdxmSurface_t* surface, CTraceSurface& TS, const mdxmSurfHierarchy_t* surfInfo) +{ + int j; + vec3_t basis1; + vec3_t basis2; + vec3_t taxis; + vec3_t saxis; + + if (!TS.gore) + { + return; + } + + if (!TS.gore->useTheta) + { + VectorCopy(TS.gore->uaxis, basis2); + CrossProduct(TS.rayEnd, basis2, basis1); + if (DotProduct(basis1, basis1) < 0.005f) + { //shot dir and slash dir are too close + return; + } + } + + if (TS.gore->useTheta) + { + basis2[0] = 0.0f; + basis2[1] = 0.0f; + basis2[2] = 1.0f; + + CrossProduct(TS.rayEnd, basis2, basis1); + + if (DotProduct(basis1, basis1) < .1f) + { + basis2[0] = 0.0f; + basis2[1] = 1.0f; + basis2[2] = 0.0f; + CrossProduct(TS.rayEnd, basis2, basis1); + } + CrossProduct(TS.rayEnd, basis1, basis2); + } + + // Give me a shot direction not a bunch of zeros :) -Gil + assert(DotProduct(basis1, basis1) > .0001f); + assert(DotProduct(basis2, basis2) > .0001f); + + VectorNormalize(basis1); + VectorNormalize(basis2); + + float c = cos(TS.theta); + float s = sin(TS.theta); + + VectorScale(basis1, .5f * c / TS.tsize, taxis); + VectorMA(taxis, .5f * s / TS.tsize, basis2, taxis); + + VectorScale(basis1, -.5f * s / TS.ssize, saxis); + VectorMA(saxis, .5f * c / TS.ssize, basis2, saxis); + + + float* verts = (float*)TS.TransformedVertsArray[surface->thisSurfaceIndex]; + int numVerts = surface->numVerts; + int flags = 15; + assert(numVerts < MAX_GORE_VERTS); + for (j = 0; j < numVerts; j++) + { + int pos = j * 5; + vec3_t delta; + delta[0] = verts[pos + 0] - TS.rayStart[0]; + delta[1] = verts[pos + 1] - TS.rayStart[1]; + delta[2] = verts[pos + 2] - TS.rayStart[2]; + float s = DotProduct(delta, saxis) + 0.5f; + float t = DotProduct(delta, taxis) + 0.5f; + float depth = DotProduct(delta, TS.rayEnd); + int vflags = 0; + if (s > GORE_MARGIN) + { + vflags |= 1; + } + if (s < 1.0f - GORE_MARGIN) + { + vflags |= 2; + } + if (t > GORE_MARGIN) + { + vflags |= 4; + } + if (t < 1.0f - GORE_MARGIN) + { + vflags |= 8; + } + if (depth < TS.gore->depthEnd) + { + vflags |= 32; + } + vflags = (~vflags); + flags &= vflags; + GoreVerts[j].flags = vflags; + GoreVerts[j].tex[0] = s; + GoreVerts[j].tex[1] = t; + } + if (flags) + { + return; // completely off the gore splotch. + } + + ///////////////////////////////////////////////////////////////// + + int numTris, newNumTris, newNumVerts; + numTris = surface->numTriangles; + mdxmTriangle_t* tris; + tris = (mdxmTriangle_t*)((byte*)surface + surface->ofsTriangles); + verts = (float*)TS.TransformedVertsArray[surface->thisSurfaceIndex]; + newNumTris = 0; + newNumVerts = 0; + GoreTouch++; + + for (j = 0; j < numTris; j++) + { + assert(tris[j].indexes[0] >= 0 && tris[j].indexes[0] < numVerts); + assert(tris[j].indexes[1] >= 0 && tris[j].indexes[1] < numVerts); + assert(tris[j].indexes[2] >= 0 && tris[j].indexes[2] < numVerts); + flags = 15 & + GoreVerts[tris[j].indexes[0]].flags & + GoreVerts[tris[j].indexes[1]].flags & + GoreVerts[tris[j].indexes[2]].flags; + + if (flags) + { + continue; + } + + if (!TS.gore->frontFaces || !TS.gore->backFaces) + { + // we need to back/front face cull + vec3_t e1, e2, n; + + VectorSubtract(&verts[tris[j].indexes[1] * 5], &verts[tris[j].indexes[0] * 5], e1); + VectorSubtract(&verts[tris[j].indexes[2] * 5], &verts[tris[j].indexes[0] * 5], e2); + CrossProduct(e1, e2, n); + + if (DotProduct(TS.rayEnd, n) > 0.0f) + { + if (!TS.gore->frontFaces) + { + continue; + } + } + else + { + if (!TS.gore->backFaces) + { + continue; + } + } + + } + + int k; + + assert(newNumTris * 3 + 3 < MAX_GORE_INDECIES); + for (k = 0; k < 3; k++) + { + if (GoreVerts[tris[j].indexes[k]].touch == GoreTouch) + { + GoreIndecies[newNumTris * 3 + k] = GoreVerts[tris[j].indexes[k]].newindex; + } + else + { + GoreVerts[tris[j].indexes[k]].touch = GoreTouch; + GoreVerts[tris[j].indexes[k]].newindex = newNumVerts; + GoreIndecies[newNumTris * 3 + k] = newNumVerts; + GoreIndexCopy[newNumVerts] = tris[j].indexes[k]; + newNumVerts++; + } + } + newNumTris++; + } + if (!newNumVerts) + { + return; + } + + int newTag; + std::map, int>::iterator f = GoreTagsTemp.find(std::make_pair(goreModelIndex, TS.surfaceNum)); + + if (f == GoreTagsTemp.end()) // need to generate a record + { + newTag = AllocR2GoreRecord(); + CGoreSet* goreSet = 0; + if (TS.ghoul2info->mGoreSetTag) + { + goreSet = FindGoreSet(TS.ghoul2info->mGoreSetTag); + } + if (!goreSet) + { + goreSet = NewGoreSet(); + TS.ghoul2info->mGoreSetTag = goreSet->mMyGoreSetTag; + } + assert(goreSet); + SGoreSurface add; + add.shader = TS.goreShader; + add.mDeleteTime = 0; + if (TS.gore->lifeTime) + { + add.mDeleteTime = G2API_GetTime(0) + TS.gore->lifeTime; + } + add.mFadeTime = TS.gore->fadeOutTime; + add.mFadeRGB = TS.gore->fadeRGB; + add.mGoreTag = newTag; + + add.mGoreGrowStartTime = G2API_GetTime(0); + if (TS.gore->growDuration == -1) + { + add.mGoreGrowEndTime = -1; // set this to -1 to disable growing + } + else + { + add.mGoreGrowEndTime = G2API_GetTime(0) + TS.gore->growDuration; + } + + assert(TS.gore->growDuration != 0); + add.mGoreGrowFactor = (1.0f - TS.gore->goreScaleStartFraction) / (float)(TS.gore->growDuration); //curscale = (curtime-mGoreGrowStartTime)*mGoreGrowFactor; + add.mGoreGrowOffset = TS.gore->goreScaleStartFraction; + + goreSet->mGoreRecords.insert(std::make_pair(TS.surfaceNum, add)); + GoreTagsTemp[std::make_pair(goreModelIndex, TS.surfaceNum)] = newTag; + } + else + { + newTag = (*f).second; + } + + // AntiAnti. R2GoreTextureCoordinates now contains both raw data and structured srfG2GoreSurface_t + // Just not sure what to remove. + R2GoreTextureCoordinates* gore = FindR2GoreRecord(newTag); + if (gore) + { + // fill srfG2GoreSurface_t + //------------------------------------------------------------------------------------------------------------------ + + srfG2GoreSurface_t* goreSurface = (srfG2GoreSurface_t*)R_Malloc(sizeof(srfG2GoreSurface_t), TAG_GHOUL2_GORE, qtrue); + + // cleanup old data + if (gore->tex[TS.lod]) + { + if (gore->tex[TS.lod]->verts) + Z_Free(gore->tex[TS.lod]->verts); + if (gore->tex[TS.lod]->indexes) + Z_Free(gore->tex[TS.lod]->indexes); + Z_Free(gore->tex[TS.lod]); + } + + // set pointer to allocated memory + gore->tex[TS.lod] = (srfG2GoreSurface_t*)goreSurface; + goreSurface->numVerts = newNumVerts; + // allocate space for vertices + goreSurface->verts = (g2GoreVert_t*)R_Malloc(sizeof(g2GoreVert_t) * newNumVerts, TAG_GHOUL2_GORE, qtrue); + + // and set uv for vertices + for (j = 0; j < newNumVerts; j++) + { + goreSurface->verts[j].texCoords[0] = GoreVerts[GoreIndexCopy[j]].tex[0]; + goreSurface->verts[j].texCoords[1] = GoreVerts[GoreIndexCopy[j]].tex[1]; + } + + // set skinning mesh data (weights for bones) for our vertices + mdxmVertex_t* v = (mdxmVertex_t*)((byte*)surface + surface->ofsVerts); + int* boneRef = (int*)((byte*)surface + surface->ofsBoneReferences); + for (j = 0; j < newNumVerts; j++) + { + mdxmVertex_t currentVert = v[GoreIndexCopy[j]]; + VectorCopy(currentVert.vertCoords, goreSurface->verts[j].position); + goreSurface->verts[j].normal = R_VboPackNormal(currentVert.normal); + + int numWeights = G2_GetVertWeights(¤tVert); + float fTotalWeight = 0.0f; + for (int w = 0; w < numWeights; w++) + { + float weight = G2_GetVertBoneWeight(¤tVert, w, fTotalWeight, numWeights); + goreSurface->verts[j].weights[w] = (byte)(weight * 255.0f); + int packedIndex = G2_GetVertBoneIndex(¤tVert, w); + goreSurface->verts[j].bonerefs[w] = boneRef[packedIndex]; + } + } + + // allocate memory for indices and fill them + goreSurface->indexes = (glIndex_t*)R_Malloc(sizeof(glIndex_t) * newNumTris * 3, TAG_GHOUL2_GORE, qtrue); + for (j = 0; j < newNumTris * 3; j++) + { + goreSurface->indexes[j] = GoreIndecies[j] + tr.goreVBOCurrentIndex; + } + goreSurface->numIndexes = newNumTris * 3; + + RB_UpdateGoreVBO(goreSurface); + + } +} +#else +struct SVertexTemp +{ + int flags; +// int touch; +// int newindex; +// float tex[2]; + SVertexTemp() + { +// touch=0; + } +}; + +#define MAX_GORE_VERTS (3000) +static SVertexTemp GoreVerts[MAX_GORE_VERTS]; +#endif + +// now we're at poly level, check each model space transformed poly against the model world transfomed ray +static bool G2_TracePolys(const mdxmSurface_t *surface, const mdxmSurfHierarchy_t *surfInfo, CTraceSurface &TS) +{ + int j, numTris; + + // whip through and actually transform each vertex + const mdxmTriangle_t *tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles); + const float *verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex]; + numTris = surface->numTriangles; + for ( j = 0; j < numTris; j++ ) + { + float face; + vec3_t hitPoint, normal; + // determine actual coords for this triangle + const float *point1 = &verts[(tris[j].indexes[0] * 5)]; + const float *point2 = &verts[(tris[j].indexes[1] * 5)]; + const float *point3 = &verts[(tris[j].indexes[2] * 5)]; + // did we hit it? + int i; + if (G2_SegmentTriangleTest(TS.rayStart, TS.rayEnd, point1, point2, point3, qtrue, qtrue, hitPoint, normal, &face)) + { // find space in the collision records for this record + for (i=0; ithisSurfaceIndex; + newCol.mModelIndex = TS.modelIndex; + if (face>0) + { + newCol.mFlags = G2_FRONTFACE; + } + else + { + newCol.mFlags = G2_BACKFACE; + } + + VectorSubtract(hitPoint, TS.rayStart, distVect); + newCol.mDistance = VectorLength(distVect); + assert(!Q_isnan(newCol.mDistance)); + + // put the hit point back into world space + TransformAndTranslatePoint(hitPoint, newCol.mCollisionPosition, &worldMatrix); + + // transform normal (but don't translate) into world angles + TransformPoint(normal, newCol.mCollisionNormal, &worldMatrix); + VectorNormalize(newCol.mCollisionNormal); + + newCol.mMaterial = newCol.mLocation = 0; + + // Determine our location within the texture, and barycentric coordinates + G2_BuildHitPointST(point1, point1[3], point1[4], + point2, point2[3], point2[4], + point3, point3[3], point3[4], + hitPoint, &x_pos, &y_pos,newCol.mBarycentricI,newCol.mBarycentricJ); + +/* + const shader_t *shader = 0; + // now, we know what surface this hit belongs to, we need to go get the shader handle so we can get the correct hit location and hit material info + if ( cust_shader ) + { + shader = cust_shader; + } + else if ( skin ) + { + int j; + + // match the surface name to something in the skin file + shader = tr.defaultShader; + for ( j = 0 ; j < skin->numSurfaces ; j++ ) + { + // the names have both been lowercased + if ( !strcmp( skin->surfaces[j]->name, surfInfo->name ) ) + { + shader = skin->surfaces[j]->shader; + break; + } + } + } + else + { + shader = R_GetShaderByHandle( surfInfo->shaderIndex ); + } + + // do we even care to decide what the hit or location area's are? If we don't have them in the shader there is little point + if ((shader->hitLocation) || (shader->hitMaterial)) + { + // ok, we have a floating point position. - determine location in data we need to look at + if (shader->hitLocation) + { + newCol.mLocation = *(hitMatReg[shader->hitLocation].loc + + ((int)(y_pos * hitMatReg[shader->hitLocation].height) * hitMatReg[shader->hitLocation].width) + + ((int)(x_pos * hitMatReg[shader->hitLocation].width))); + Com_Printf("G2_TracePolys hit location: %d\n", newCol.mLocation); + } + + if (shader->hitMaterial) + { + newCol.mMaterial = *(hitMatReg[shader->hitMaterial].loc + + ((int)(y_pos * hitMatReg[shader->hitMaterial].height) * hitMatReg[shader->hitMaterial].width) + + ((int)(x_pos * hitMatReg[shader->hitMaterial].width))); + } + } +*/ + // exit now if we should + if (TS.eG2TraceType == EG2_Collision::G2_RETURNONHIT) + { + TS.hitOne = true; + return true; + } + + break; + } + } + if (i == MAX_G2_COLLISIONS) + { + //It happens. And the assert is bugging me. + TS.hitOne = true; //force stop recursion + return true; // return true to avoid wasting further time, but no hit will result without a record + } + } + } + return false; +} + + +// now we're at poly level, check each model space transformed poly against the model world transfomed ray +static bool G2_RadiusTracePolys( + const mdxmSurface_t *surface, + CTraceSurface &TS + ) +{ + int j; + vec3_t basis1; + vec3_t basis2; + vec3_t taxis; + vec3_t saxis; + + basis2[0]=0.0f; + basis2[1]=0.0f; + basis2[2]=1.0f; + + vec3_t v3RayDir; + VectorSubtract(TS.rayEnd, TS.rayStart, v3RayDir); + + CrossProduct(v3RayDir,basis2,basis1); + + if (DotProduct(basis1,basis1)<.1f) + { + basis2[0]=0.0f; + basis2[1]=1.0f; + basis2[2]=0.0f; + CrossProduct(v3RayDir,basis2,basis1); + } + + CrossProduct(v3RayDir,basis1,basis2); + // Give me a shot direction not a bunch of zeros :) -Gil +// assert(DotProduct(basis1,basis1)>.0001f); +// assert(DotProduct(basis2,basis2)>.0001f); + + VectorNormalize(basis1); + VectorNormalize(basis2); + + const float c=cos(0.0f);//theta + const float s=sin(0.0f);//theta + + VectorScale(basis1, 0.5f * c / TS.m_fRadius,taxis); + VectorMA(taxis, 0.5f * s / TS.m_fRadius,basis2,taxis); + + VectorScale(basis1,-0.5f * s /TS.m_fRadius,saxis); + VectorMA( saxis, 0.5f * c /TS.m_fRadius,basis2,saxis); + + const float * const verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex]; + const int numVerts = surface->numVerts; + + int flags=63; + //rayDir/=lengthSquared(raydir); + const float f = VectorLengthSquared(v3RayDir); + v3RayDir[0]/=f; + v3RayDir[1]/=f; + v3RayDir[2]/=f; + + for ( j = 0; j < numVerts; j++ ) + { + const int pos=j*5; + vec3_t delta; + delta[0]=verts[pos+0]-TS.rayStart[0]; + delta[1]=verts[pos+1]-TS.rayStart[1]; + delta[2]=verts[pos+2]-TS.rayStart[2]; + const float s=DotProduct(delta,saxis)+0.5f; + const float t=DotProduct(delta,taxis)+0.5f; + const float u=DotProduct(delta,v3RayDir); + int vflags=0; + + if (s>0) + { + vflags|=1; + } + if (s<1) + { + vflags|=2; + } + if (t>0) + { + vflags|=4; + } + if (t<1) + { + vflags|=8; + } + if (u>0) + { + vflags|=16; + } + if (u<1) + { + vflags|=32; + } + + vflags=(~vflags); + flags&=vflags; + GoreVerts[j].flags=vflags; + } + + if (flags) + { + return false; // completely off the gore splotch (so presumably hit nothing? -Ste) + } + const int numTris = surface->numTriangles; + const mdxmTriangle_t * const tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles); + + for ( j = 0; j < numTris; j++ ) + { + assert(tris[j].indexes[0]>=0&&tris[j].indexes[0]=0&&tris[j].indexes[1]=0&&tris[j].indexes[2]thisSurfaceIndex; + newCol.mModelIndex = TS.modelIndex; +// if (face>0) +// { + newCol.mFlags = G2_FRONTFACE; +// } +// else +// { +// newCol.mFlags = G2_BACKFACE; +// } + + //get normal from triangle + const float *A = &verts[(tris[j].indexes[0] * 5)]; + const float *B = &verts[(tris[j].indexes[1] * 5)]; + const float *C = &verts[(tris[j].indexes[2] * 5)]; + vec3_t normal; + vec3_t edgeAC, edgeBA; + + VectorSubtract(C, A, edgeAC); + VectorSubtract(B, A, edgeBA); + CrossProduct(edgeBA, edgeAC, normal); + + // transform normal (but don't translate) into world angles + TransformPoint(normal, newCol.mCollisionNormal, &worldMatrix); + VectorNormalize(newCol.mCollisionNormal); + + newCol.mMaterial = newCol.mLocation = 0; + // exit now if we should + if (TS.eG2TraceType == EG2_Collision::G2_RETURNONHIT) + { + TS.hitOne = true; + return true; + } + + vec3_t distVect; +#if 0 + //i don't know the hitPoint, but let's just assume it's the first vert for now... + float *hitPoint = (float *)A; +#else + //yeah, I want the collision point. Let's work out the impact point on the triangle. -rww + vec3_t hitPoint; + float side, side2; + float dist; + float third = -(A[0]*(B[1]*C[2] - C[1]*B[2]) + B[0]*(C[1]*A[2] - A[1]*C[2]) + C[0]*(A[1]*B[2] - B[1]*A[2]) ); + + VectorSubtract(TS.rayEnd, TS.rayStart, distVect); + side = normal[0]*TS.rayStart[0] + normal[1]*TS.rayStart[1] + normal[2]*TS.rayStart[2] + third; + side2 = normal[0]*distVect[0] + normal[1]*distVect[1] + normal[2]*distVect[2]; + if (fabsf(side2) < 1E-8f) + { + //i don't know the hitPoint, but let's just assume it's the first vert for now... + VectorSubtract(A, TS.rayStart, distVect); + dist = VectorLength(distVect); + VectorSubtract(TS.rayEnd, TS.rayStart, distVect); + VectorMA(TS.rayStart, dist / VectorLength(distVect), distVect, hitPoint); + } + else + { + dist = side / side2; + VectorMA(TS.rayStart, -dist, distVect, hitPoint); + } +#endif + + VectorSubtract(hitPoint, TS.rayStart, distVect); + newCol.mDistance = VectorLength(distVect); + assert(!Q_isnan(newCol.mDistance)); + + // put the hit point back into world space + TransformAndTranslatePoint(hitPoint, newCol.mCollisionPosition, &worldMatrix); + newCol.mBarycentricI = newCol.mBarycentricJ = 0.0f; + + break; + } + } + if (i==MAX_G2_COLLISIONS) + { + //assert(i!=MAX_G2_COLLISIONS); // run out of collision record space - happens OFTEN + TS.hitOne = true; //force stop recursion + return true; // return true to avoid wasting further time, but no hit will result without a record + } + } + } + + return false; +} + + +// look at a surface and then do the trace on each poly +static void G2_TraceSurfaces(CTraceSurface &TS) +{ + int i; + // back track and get the surfinfo struct for this surface + assert(TS.currentModel); + assert(TS.currentModel->data.glm && TS.currentModel->data.glm->header); + const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(TS.currentModel, TS.surfaceNum, TS.lod); + const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)TS.currentModel->data.glm->header + sizeof(mdxmHeader_t)); + const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); + + // see if we have an override surface in the surface list + const surfaceInfo_t *surfOverride = G2_FindOverrideSurface(TS.surfaceNum, TS.rootSList); + + // don't allow recursion if we've already hit a polygon + if (TS.hitOne) + { + return; + } + + // really, we should use the default flags for this surface unless it's been overriden + int offFlags = surfInfo->flags; + + // set the off flags if we have some + if (surfOverride) + { + offFlags = surfOverride->offFlags; + } + + // if this surface is not off, try to hit it + if (!offFlags) + { +#ifdef _G2_GORE + if (TS.collRecMap) + { +#endif + if (!(Q_fabs(TS.m_fRadius) < 0.1)) // if not a point-trace + { + // .. then use radius check + // + if (G2_RadiusTracePolys(surface, // const mdxmSurface_t *surface, + TS + ) + && (TS.eG2TraceType == EG2_Collision::G2_RETURNONHIT) + ) + { + TS.hitOne = true; + return; + } + } + else + { + // go away and trace the polys in this surface + if (G2_TracePolys(surface, surfInfo, TS) + && (TS.eG2TraceType == EG2_Collision::G2_RETURNONHIT) + ) + { + // ok, we hit one, *and* we want to return instantly because the returnOnHit is set + // so indicate we've hit one, so other surfaces don't get hit and return + TS.hitOne = true; + return; + } + } +#ifdef _G2_GORE + } + else + { + G2_GorePolys(surface, TS, surfInfo); + } +#endif + } + + // if we are turning off all descendants, then stop this recursion now + if (offFlags & G2SURFACEFLAG_NODESCENDANTS) + { + return; + } + + // now recursively call for the children + for (i=0; i< surfInfo->numChildren && !TS.hitOne; i++) + { + TS.surfaceNum = surfInfo->childIndexes[i]; + G2_TraceSurfaces(TS); + } +} + +#ifdef _G2_GORE +void G2_TraceModels(CGhoul2Info_v& ghoul2, vec3_t rayStart, vec3_t rayEnd, CCollisionRecord* collRecMap, int entNum, EG2_Collision eG2TraceType, int useLod, float fRadius, float ssize, float tsize, float theta, int shader, SSkinGoreData* gore, qboolean skipIfLODNotMatch) +#else +void G2_TraceModels(CGhoul2Info_v& ghoul2, vec3_t rayStart, vec3_t rayEnd, CCollisionRecord* collRecMap, int entNum, EG2_Collision eG2TraceType, int useLod, float fRadius) +#endif +{ + int i, lod; + skin_t* skin; + shader_t* cust_shader; +#if !defined(JK2_MODE) || defined(_G2_GORE) + qboolean firstModelOnly = qfalse; +#endif // !JK2_MODE || _G2_GORE + int firstModel = 0; + +#ifndef JK2_MODE + if ( cg_g2MarksAllModels == NULL ) + { + cg_g2MarksAllModels = ri.Cvar_Get( "cg_g2MarksAllModels", "0", 0 ); + } + + if (cg_g2MarksAllModels == NULL + || !cg_g2MarksAllModels->integer ) + { + firstModelOnly = qtrue; + } +#endif // !JK2_MODE + +#ifdef _G2_GORE + if (gore && gore->firstModel > 0) + { + firstModel = gore->firstModel; + firstModelOnly = qfalse; + } +#endif + + // walk each possible model for this entity and try tracing against it + for (i = firstModel; i < ghoul2.size(); i++) + { + CGhoul2Info& g = ghoul2[i]; +#ifdef _G2_GORE + goreModelIndex = i; + // don't bother with models that we don't care about. + if (g.mModelindex == -1) + { + continue; + } +#endif + // don't bother with models that we don't care about. + if (!g.mValid) + { + continue; + } + assert(G2_MODEL_OK(&ghoul2[i])); + // do we really want to collide with this object? + if (g.mFlags & GHOUL2_NOCOLLIDE) + { + continue; + } + + cust_shader = (g.mCustomShader) + ? (shader_t*)R_GetShaderByHandle(g.mCustomShader) + : NULL; + + // figure out the custom skin thing + skin = (g.mSkin > 0 && g.mSkin < tr.numSkins) + ? R_GetSkinByHandle(g.mSkin) + : NULL; + + lod = G2_DecideTraceLod(ghoul2[i],useLod); + +#ifndef JK2_MODE + if ( skipIfLODNotMatch ) + {//we only want to hit this SPECIFIC LOD... + if ( lod != useLod ) + {//doesn't match, skip this model + continue; + } + } +#endif + //reset the quick surface override lookup + G2_FindOverrideSurface(-1, ghoul2[i].mSlist); + +#ifdef _G2_GORE + CTraceSurface TS(g.mSurfaceRoot, ghoul2[i].mSlist, (model_t *)ghoul2[i].currentModel, lod, rayStart, rayEnd, collRecMap, entNum, i, skin, cust_shader, ghoul2[i].mTransformedVertsArray, eG2TraceType, fRadius, ssize, tsize, theta, shader, &g, gore); +#else + CTraceSurface TS(g.mSurfaceRoot, ghoul2[i].mSlist, (model_t *)ghoul2[i].currentModel, lod, rayStart, rayEnd, collRecMap, entNum, i, skin, cust_shader, ghoul2[i].mTransformedVertsArray, eG2TraceType, fRadius); +#endif + // start the surface recursion loop + G2_TraceSurfaces(TS); + + // if we've hit one surface on one model, don't bother doing the rest + if (TS.hitOne) + { + break; + } +#ifdef _G2_GORE + if (!collRecMap&&firstModelOnly) + { + // we don't really need to do multiple models for gore. + break; + } +#endif + } +} + +void TransformPoint (const vec3_t in, vec3_t out, mdxaBone_t *mat) { + for (int i=0;i<3;i++) + { + out[i]= in[0]*mat->matrix[i][0] + in[1]*mat->matrix[i][1] + in[2]*mat->matrix[i][2]; + } +} + +void TransformAndTranslatePoint (const vec3_t in, vec3_t out, mdxaBone_t *mat) { + + for (int i=0;i<3;i++) + { + out[i]= in[0]*mat->matrix[i][0] + in[1]*mat->matrix[i][1] + in[2]*mat->matrix[i][2] + mat->matrix[i][3]; + } +} + + +// create a matrix using a set of angles +void Create_Matrix(const float *angle, mdxaBone_t *matrix) +{ + vec3_t axis[3]; + + // convert angles to axis + AnglesToAxis( angle, axis ); + matrix->matrix[0][0] = axis[0][0]; + matrix->matrix[1][0] = axis[0][1]; + matrix->matrix[2][0] = axis[0][2]; + + matrix->matrix[0][1] = axis[1][0]; + matrix->matrix[1][1] = axis[1][1]; + matrix->matrix[2][1] = axis[1][2]; + + matrix->matrix[0][2] = axis[2][0]; + matrix->matrix[1][2] = axis[2][1]; + matrix->matrix[2][2] = axis[2][2]; + + matrix->matrix[0][3] = 0; + matrix->matrix[1][3] = 0; + matrix->matrix[2][3] = 0; + + +} + +// given a matrix, generate the inverse of that matrix +void Inverse_Matrix(mdxaBone_t *src, mdxaBone_t *dest) +{ + int i, j; + + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + dest->matrix[i][j]=src->matrix[j][i]; + } + } + for (i = 0; i < 3; i++) + { + dest->matrix[i][3]=0; + for (j = 0; j < 3; j++) + { + dest->matrix[i][3]-=dest->matrix[i][j]*src->matrix[j][3]; + } + } +} + +// generate the world matrix for a given set of angles and origin - called from lots of places +void G2_GenerateWorldMatrix(const vec3_t angles, const vec3_t origin) +{ + Create_Matrix(angles, &worldMatrix); + worldMatrix.matrix[0][3] = origin[0]; + worldMatrix.matrix[1][3] = origin[1]; + worldMatrix.matrix[2][3] = origin[2]; + + Inverse_Matrix(&worldMatrix, &worldMatrixInv); +} + +// go away and determine what the pointer for a specific surface definition within the model definition is +void* G2_FindSurface(const model_s* mod, int index, int lod) +{ + // damn include file dependancies + mdxmHeader_t *mdxm = mod->data.glm->header; + + // point at first lod list + byte *current = (byte*)((size_t)mdxm + (size_t)mdxm->ofsLODs); + int i; + + //walk the lods + for (i=0; iofsEnd; + } + + // avoid the lod pointer data structure + current += sizeof(mdxmLOD_t); + + mdxmLODSurfOffset_t *indexes = (mdxmLODSurfOffset_t *)current; + // we are now looking at the offset array + current += indexes->offsets[index]; + + return (void *)current; +} + +#define SURFACE_SAVE_BLOCK_SIZE sizeof(surfaceInfo_t) +#define BOLT_SAVE_BLOCK_SIZE (sizeof(boltInfo_t) - sizeof(mdxaBone_t)) +#define BONE_SAVE_BLOCK_SIZE sizeof(boneInfo_t) + +void G2_SaveGhoul2Models(CGhoul2Info_v& ghoul2) +{ + ojk::SavedGameHelper saved_game(::ri.saved_game); + + saved_game.reset_buffer(); + + // is there anything to save? + if (!ghoul2.IsValid() || ghoul2.size() == 0) + { + const int zero_size = 0; +#ifdef JK2_MODE + saved_game.write( + zero_size); + + saved_game.write_chunk_and_size( + INT_ID('G', 'L', '2', 'S'), + INT_ID('G', 'H', 'L', '2')); +#else + saved_game.write_chunk(INT_ID('G', 'H', 'L', '2'), zero_size); //write out a zero buffer +#endif // JK2_MODE + + return; + } + + + // save out how many ghoul2 models we have + const int model_count = static_cast(ghoul2.size()); + + saved_game.write(model_count); + + for (int i = 0; i < model_count; ++i) + { + // first save out the ghoul2 details themselves + ghoul2[i].sg_export(saved_game); + + // save out how many surfaces we have + const int surface_count = static_cast(ghoul2[i].mSlist.size()); + saved_game.write(surface_count); + + // now save the all the surface list info + for (int x = 0; x < surface_count; ++x) + { + ghoul2[i].mSlist[x].sg_export( + saved_game); + } + + // save out how many bones we have + const int bone_count = static_cast(ghoul2[i].mBlist.size()); + saved_game.write(bone_count); + + // now save the all the bone list info + for (int x = 0; x < bone_count; ++x) + { + ghoul2[i].mBlist[x].sg_export(saved_game); + } + + // save out how many bolts we have + const int bolt_count = static_cast(ghoul2[i].mBltlist.size()); + saved_game.write(bolt_count); + + // lastly save the all the bolt list info + for (int x = 0; x < bolt_count; ++x) + { + ghoul2[i].mBltlist[x].sg_export(saved_game); + } + } + +#ifdef JK2_MODE + saved_game.write_chunk_and_size( + INT_ID('G', 'L', '2', 'S'), + INT_ID('G', 'H', 'L', '2')); +#else + saved_game.write_chunk( + INT_ID('G', 'H', 'L', '2')); +#endif // JK2_MODE +} + +// have to free space malloced in the save system here because the game DLL can't. +void G2_FreeSaveBuffer(char *buffer) +{ + Z_Free(buffer); +} + +void G2_LoadGhoul2Model( + CGhoul2Info_v& ghoul2, + char* buffer) +{ + static_cast(buffer); + + ojk::SavedGameHelper saved_game(::ri.saved_game); + + // first thing, lets see how many ghoul2 models we have, and resize our buffers accordingly + int model_count = 0; +#ifdef JK2_MODE + if (saved_game.get_buffer_size() > 0) + { +#endif // JK2_MODE + + saved_game.read( + model_count); + +#ifdef JK2_MODE + } +#endif // JK2_MODE + ghoul2.resize(model_count); + + // did we actually resize to a value? + if (model_count == 0) + { + // no, ok, well, done then. + return; + } + + // now we have enough instances, lets go through each one and load up the relevant details + for (decltype(model_count) i = 0; i < model_count; ++i) + { + ghoul2[i].mSkelFrameNum = 0; + ghoul2[i].mModelindex = -1; + ghoul2[i].mFileName[0] = 0; + ghoul2[i].mValid = false; + + // load the ghoul2 info from the buffer + ghoul2[i].sg_import(saved_game); + + if (ghoul2[i].mModelindex != -1 && ghoul2[i].mFileName[0]) + { + ghoul2[i].mModelindex = i; + + ::G2_SetupModelPointers(&ghoul2[i]); + } + + // give us enough surfaces to load up the data + int surface_count = 0; + + saved_game.read(surface_count); + ghoul2[i].mSlist.resize(surface_count); + + // now load all the surfaces + for (decltype(surface_count) x = 0; x < surface_count; ++x) + { + ghoul2[i].mSlist[x].sg_import(saved_game); + } + + // give us enough bones to load up the data + int bone_count = 0; + + saved_game.read(bone_count); + + ghoul2[i].mBlist.resize(bone_count); + + // now load all the bones + for (decltype(bone_count) x = 0; x < bone_count; ++x) + { + ghoul2[i].mBlist[x].sg_import(saved_game); + } + + // give us enough bolts to load up the data + int bolt_count = 0; + + saved_game.read(bolt_count); + + ghoul2[i].mBltlist.resize(bolt_count); + + // now load all the bolts + for (decltype(bolt_count) x = 0; x < bolt_count; ++x) + { + ghoul2[i].mBltlist[x].sg_import(saved_game); + } + } + + saved_game.ensure_all_data_read(); +} + +void G2_LerpAngles(CGhoul2Info_v &ghoul2,CGhoul2Info_v &nextGhoul2, float interpolation) +{ + // loop each model + for (int i=0; i< ghoul2.size(); i++) + { + if (ghoul2[i].mModelindex != -1) + { + // now walk the bone list + for (size_t x = 0; x < ghoul2[i].mBlist.size(); x++) + { + boneInfo_t &bone = ghoul2[i].mBlist[x]; + // sure we have one to lerp to? + if ((nextGhoul2.size() > i) && + (nextGhoul2[i].mModelindex != -1) && + (nextGhoul2[i].mBlist.size() > x) && + (nextGhoul2[i].mBlist[x].boneNumber != -1)) + { + boneInfo_t &nextBone = nextGhoul2[i].mBlist[x]; + // does this bone override actually have anything in it, and if it does, is it a bone angles override? + if ((bone.boneNumber != -1) && ((bone.flags) & (BONE_ANGLES_TOTAL))) + { + float *nowMatrix = (float*) &bone.matrix; + float *nextMatrix = (float*) &nextBone.matrix; + float *newMatrix = (float*) &bone.newMatrix; + // now interpolate the matrix + for (int z=0; z < 12; z++) + { + newMatrix[z] = nowMatrix[z] + interpolation * ( nextMatrix[z] - nowMatrix[z] ); + } + } + } + else + { + memcpy(&ghoul2[i].mBlist[x].newMatrix, &ghoul2[i].mBlist[x].matrix, sizeof(mdxaBone_t)); + } + } + } + } +} diff --git a/code/rd-rend2/G2_surfaces.cpp b/code/rd-rend2/G2_surfaces.cpp new file mode 100644 index 0000000000..1aad99e383 --- /dev/null +++ b/code/rd-rend2/G2_surfaces.cpp @@ -0,0 +1,645 @@ +/* +=========================================================================== +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +#include "server/exe_headers.h" + +#ifndef __Q_SHARED_H + #include "qcommon/q_shared.h" +#endif + +#if !defined(TR_LOCAL_H) + #include "tr_local.h" +#endif + +#if !defined(G2_H_INC) + #include "ghoul2/G2.h" +#endif + +#include "rd-common/tr_types.h" +#include "tr_local.h" +#ifdef _MSC_VER +#pragma warning(disable : 4512) //assignment op could not be genereated +#endif + +#define G2_MODEL_OK(g) ((g)&&(g)->mValid&&(g)->aHeader&&(g)->currentModel&&(g)->animModel) + +class CConstructBoneList +{ +public: + int surfaceNum; + int* boneUsedList; + surfaceInfo_v& rootSList; + model_t* currentModel; + boneInfo_v& boneList; + + CConstructBoneList( + int initsurfaceNum, + int* initboneUsedList, + surfaceInfo_v& initrootSList, + model_t* initcurrentModel, + boneInfo_v& initboneList + ) + : surfaceNum(initsurfaceNum) + , boneUsedList(initboneUsedList) + , rootSList(initrootSList) + , currentModel(initcurrentModel) + , boneList(initboneList) + { } +}; + +class CQuickOverride +{ + int mOverride[512]; + int mAt[512]; + int mCurrentTouch; +public: + CQuickOverride() + { + mCurrentTouch = 1; + memset(mOverride, 0, sizeof(int) * 512); + } + void Invalidate() + { + mCurrentTouch++; + } + void Set(int index, int pos) + { + if (index == 10000) + { + return; + } + assert(index >= 0 && index < 512); + mOverride[index] = mCurrentTouch; + mAt[index] = pos; + } + int Test(int index) + { + assert(index >= 0 && index < 512); + return (mOverride[index] == mCurrentTouch) + ? mAt[index] + : -1; + } +}; + +// functions preferinition +extern void G2_ConstructUsedBoneList(CConstructBoneList &CBL); +extern int G2_DecideTraceLod(CGhoul2Info& ghoul2, int useLod); + +//===================================================================================================================== +// Surface List handling routines - so entities can determine what surfaces attached to a model are operational or not. + +// find a particular surface in the surface override list +static CQuickOverride QuickOverride; + + +const surfaceInfo_t *G2_FindOverrideSurface(int surfaceNum, const surfaceInfo_v &surfaceList) +{ + if (surfaceNum < 0) + { + // starting a new lookup + QuickOverride.Invalidate(); + for (size_t i = 0; i < surfaceList.size(); i++) + { + if (surfaceList[i].surface >= 0) + { + QuickOverride.Set(surfaceList[i].surface, i); + } + } + return NULL; + } + int idx = QuickOverride.Test(surfaceNum); + if (idx < 0) + { + if (surfaceNum == 10000) + { + for (size_t i = 0; i < surfaceList.size(); i++) + { + if (surfaceList[i].surface == surfaceNum) + { + return &surfaceList[i]; + } + } + } +#if _DEBUG + // look through entire list + size_t i; + for (i = 0; i < surfaceList.size(); i++) + { + if (surfaceList[i].surface == surfaceNum) + { + break; + } + } + // didn't find it. + assert(i == surfaceList.size()); // our quickoverride is not working right +#endif + return NULL; + } + assert(idx >= 0 && idx < (int)surfaceList.size()); + assert(surfaceList[idx].surface == surfaceNum); + return &surfaceList[idx]; +} + + +// given a surface name, lets see if it's legal in the model +int G2_IsSurfaceLegal(const model_s* mod_m, const char* surfaceName, uint32_t* flags) +{ + assert(mod_m); + assert(mod_m->data.glm->header); + // damn include file dependancies + mdxmSurfHierarchy_t* surf; + surf = (mdxmSurfHierarchy_t*)((byte*)mod_m->data.glm->header + mod_m->data.glm->header->ofsSurfHierarchy); + + for (int i = 0; i < mod_m->data.glm->header->numSurfaces; i++) + { + if (!Q_stricmp(surfaceName, surf->name)) + { + *flags = surf->flags; + return i; + } + // find the next surface + surf = (mdxmSurfHierarchy_t*)((byte*)surf + (intptr_t)(&((mdxmSurfHierarchy_t*)0)->childIndexes[surf->numChildren])); + } + return -1; +} + + +/************************************************************************************************ + * G2_FindSurface + * find a surface in a ghoul2 surface override list based on it's name + * + * Input + * filename of model, surface list of model instance, name of surface, int to be filled in + * with the index of this surface (defaults to NULL) + * + * Output + * pointer to surface if successful, false otherwise + * + ************************************************************************************************/ +mdxmSurface_t *G2_FindSurface(CGhoul2Info *ghlInfo, surfaceInfo_v &slist, const char *surfaceName, + int *surfIndex/*NULL*/) +{ + int i = 0; + // find the model we want + model_t *mod = (model_t *)ghlInfo->currentModel; + mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->data.glm->header + sizeof(mdxmHeader_t)); + + // did we find a ghoul 2 model or not? + if (!mod->data.glm || !mod->data.glm->header) + { + assert(0); + if (surfIndex) + { + *surfIndex = -1; + } + return 0; + } + + // first find if we already have this surface in the list + for (i = slist.size() - 1; i >= 0; i--) + { + if ((slist[i].surface != 10000) && (slist[i].surface != -1)) + { + mdxmSurface_t *surf = (mdxmSurface_t *)G2_FindSurface(mod, slist[i].surface, 0); + // back track and get the surfinfo struct for this surface + const mdxmSurfHierarchy_t* surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); + + // are these the droids we're looking for? + if (!Q_stricmp (surfInfo->name, surfaceName)) + { + // yup + if (surfIndex) + { + *surfIndex = i; + } + return surf; + } + } + } + // didn't find it + if (surfIndex) + { + *surfIndex = -1; + } + return 0; +} + +qboolean G2_SetSurfaceOnOff(CGhoul2Info* ghlInfo, surfaceInfo_v& slist, const char* surfaceName, const int offFlags) +{ + int surfIndex = -1; + surfaceInfo_t temp_slist_entry; + + // find the model we want + model_t *mod = (model_t *)ghlInfo->currentModel; + + // did we find a ghoul 2 model or not? + if (!mod->data.glm || !mod->data.glm->header) + { + assert(0); + return qfalse; + } + + // first find if we already have this surface in the list + const mdxmSurface_t* surf = G2_FindSurface(ghlInfo, slist, surfaceName, &surfIndex); + if (surf) + { + // set descendants value + + // slist[surfIndex].offFlags = offFlags; + // seems to me that we shouldn't overwrite the other flags. + // the only bit we really care about in the incoming flags is the off bit + slist[surfIndex].offFlags &= ~(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); + slist[surfIndex].offFlags |= offFlags & (G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); + return qtrue; + } + else + { + // ok, not in the list already - in that case, lets verify this surface exists in the model mesh + uint32_t flags; + int surfaceNum = G2_IsSurfaceLegal(mod, surfaceName, &flags); + if (surfaceNum != -1) + { + uint32_t newflags = flags; + // the only bit we really care about in the incoming flags is the off bit + newflags &= ~(G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); + newflags |= offFlags & (G2SURFACEFLAG_OFF | G2SURFACEFLAG_NODESCENDANTS); + + if (newflags != flags) + { // insert here then because it changed, no need to add an override otherwise + temp_slist_entry.offFlags = newflags; + temp_slist_entry.surface = surfaceNum; + + slist.push_back(temp_slist_entry); + } + return qtrue; + } + } + return qfalse; +} + +// set a named surface offFlags - if it doesn't find a surface with this name in the list then it will add one. +qboolean G2_SetSurfaceOnOff(CGhoul2Info* ghlInfo, const char* surfaceName, const int offFlags) +{ + return G2_SetSurfaceOnOff(ghlInfo, ghlInfo->mSlist, surfaceName, offFlags); +} + +void G2_SetSurfaceOnOffFromSkin(CGhoul2Info *ghlInfo, qhandle_t renderSkin) +{ + const skin_t *skin = R_GetSkinByHandle( renderSkin ); + + //FIXME: using skin handles means we have to increase the numsurfs in a skin, but reading directly would cause file hits, we need another way to cache or just deal with the larger skin_t + + if (skin) + { + ghlInfo->mSlist.clear(); // remove any overrides we had before. + ghlInfo->mMeshFrameNum = 0; + + for (int j = 0; j < skin->numSurfaces; j++) + { + uint32_t flags; + int surfaceNum = G2_IsSurfaceLegal(ghlInfo->currentModel, skin->surfaces[j]->name, &flags); + + // the names have both been lowercased + if (!(flags & G2SURFACEFLAG_OFF) && !strcmp(((shader_t*)(skin->surfaces[j]->shader))->name, "*off")) + { + G2_SetSurfaceOnOff(ghlInfo, skin->surfaces[j]->name, G2SURFACEFLAG_OFF); + } + else + { + // only turn on if it's not an "_off" surface + if ((surfaceNum != -1) && (!(flags & G2SURFACEFLAG_OFF))) + { + //G2_SetSurfaceOnOff(ghlInfo, skin->surfaces[j]->name, 0); + } + } + } + } +} + +// return a named surfaces off flags - should tell you if this surface is on or off. +/* +int G2_IsSurfaceOff (CGhoul2Info *ghlInfo, surfaceInfo_v &slist, const char *surfaceName) +{ + model_t *mod = (model_t *)ghlInfo->currentModel; + int surfIndex = -1; + mdxmSurface_t *surf = 0; + mdxmHeader_t *mdxm = mod->data.glm->header; + + // did we find a ghoul 2 model or not? + if (!mdxm) + { + return 0; + } + + // first find if we already have this surface in the list + surf = G2_FindSurface(ghlInfo, slist, surfaceName, &surfIndex); + if (surf) + { + // set descendants value + return slist[surfIndex].offFlags; + } + // ok, we didn't find it in the surface list. Lets look at the original surface then. + + mdxmSurfHierarchy_t *surface = (mdxmSurfHierarchy_t *) ( (byte *)mdxm + mdxm->ofsSurfHierarchy ); + + for ( int i = 0 ; i < mdxm->numSurfaces ; i++) + { + if (!Q_stricmp(surfaceName, surface->name)) + { + return surface->flags; + } + // find the next surface + surface = (mdxmSurfHierarchy_t *)( (byte *)surface + (intptr_t)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surface->numChildren ] )); + } + + assert(0); + return 0; +} +*/ + +void G2_FindRecursiveSurface(const model_t* currentModel, int surfaceNum, surfaceInfo_v& rootList, int* activeSurfaces) +{ + assert(currentModel); + assert(currentModel->data.glm); + int i; + mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(currentModel, surfaceNum, 0); + mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)currentModel->data.glm->header + sizeof(mdxmHeader_t)); + mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); + + // see if we have an override surface in the surface list + const surfaceInfo_t *surfOverride = G2_FindOverrideSurface(surfaceNum, rootList); + + // really, we should use the default flags for this surface unless it's been overriden + int offFlags = surfInfo->flags; + + // set the off flags if we have some + if (surfOverride) + { + offFlags = surfOverride->offFlags; + } + + // if this surface is not off, indicate as such in the active surface list + if (!(offFlags & G2SURFACEFLAG_OFF)) + { + activeSurfaces[surfaceNum] = 1; + } + else + // if we are turning off all descendants, then stop this recursion now + if (offFlags & G2SURFACEFLAG_NODESCENDANTS) + { + return; + } + + // now recursively call for the children + for (i=0; i< surfInfo->numChildren; i++) + { + surfaceNum = surfInfo->childIndexes[i]; + G2_FindRecursiveSurface(currentModel, surfaceNum, rootList, activeSurfaces); + } + +} + +void G2_RemoveRedundantGeneratedSurfaces(surfaceInfo_v &slist, int *activeSurfaces) +{ + // walk the surface list, removing surface overrides or generated surfaces that are pointing at surfaces that aren't active anymore + for (size_t i=0; i= 0 && modelIndex < ghoul2.size()); + assert(ghoul2[modelIndex].currentModel && ghoul2[modelIndex].animModel); + + model_t *mod_m = (model_t *)ghoul2[modelIndex].currentModel; + model_t *mod_a = (model_t *)ghoul2[modelIndex].animModel; + mdxmHeader_t *mdxm = mod_m->data.glm->header; + mdxaHeader_t *mdxa = mod_a->data.gla; + + // did we find a ghoul 2 model or not? + if (!mdxm) + { + return qfalse; + } + + // first find if we already have this surface in the list + surf = G2_IsSurfaceLegal(mod_m, surfaceName, &flags); + if (surf != -1) + { + // set the root surface + ghoul2[modelIndex].mSurfaceRoot = surf; + + return qtrue; + } + assert(0); + return qfalse; +} + + +int G2_AddSurface(CGhoul2Info *ghoul2, int surfaceNumber, int polyNumber, float BarycentricI, float BarycentricJ, int lod ) +{ + + surfaceInfo_t temp_slist_entry; + + // decide if LOD is legal + lod = G2_DecideTraceLod(*ghoul2, lod); + + // first up, see if we have a free one already set up - look only from the end of the constant surfaces onwards + size_t i; + for (i=0; imSlist.size(); i++) + { + // is the surface count -1? That would indicate it's free + if (ghoul2->mSlist[i].surface == -1) + { + break; + } + } + + if (i == ghoul2->mSlist.size()) + { + ghoul2->mSlist.push_back(surfaceInfo_t()); + } + + ghoul2->mSlist[i].offFlags = G2SURFACEFLAG_GENERATED; + ghoul2->mSlist[i].surface = 10000; // no model will ever have 10000 surfaces + ghoul2->mSlist[i].genBarycentricI = BarycentricI; + ghoul2->mSlist[i].genBarycentricJ = BarycentricJ; + ghoul2->mSlist[i].genPolySurfaceIndex = ((polyNumber & 0xffff) << 16) | (surfaceNumber & 0xffff); + ghoul2->mSlist[i].genLod = lod; + + return i; +} + +qboolean G2_RemoveSurface(surfaceInfo_v &slist, const int index) +{ + // did we find it? + if (index != -1) + { + // set us to be the 'not active' state + slist[index].surface = -1; + + unsigned int newSize = slist.size(); + // now look through the list from the back and see if there is a block of -1's we can resize off the end of the list + for (int i=slist.size()-1; i>-1; i--) + { + if (slist[i].surface == -1) + { + newSize = i; + } + // once we hit one that isn't a -1, we are done. + else + { + break; + } + } + // do we need to resize? + if (newSize != slist.size()) + { + // yes, so lets do it + slist.resize(newSize); + } + + return qtrue; + } + + assert(0); + + // no + return qfalse; +} + + +int G2_GetParentSurface(CGhoul2Info *ghlInfo, const int index) +{ + model_t *mod = (model_t *)ghlInfo->currentModel; + mdxmSurface_t *surf = 0; + mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mod->data.glm->header + sizeof(mdxmHeader_t)); + mdxmSurfHierarchy_t *surfInfo = 0; + + // walk each surface and see if this index is listed in it's children + surf = (mdxmSurface_t *)G2_FindSurface(mod, index, 0); + surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surf->thisSurfaceIndex]); + + return surfInfo->parentIndex; + +} + +int G2_GetSurfaceIndex(CGhoul2Info *ghlInfo, const char *surfaceName) +{ + model_t *mod = (model_t *)ghlInfo->currentModel; + uint32_t flags; + + return G2_IsSurfaceLegal(mod, surfaceName, &flags); +} + +int G2_IsSurfaceRendered(CGhoul2Info *ghlInfo, const char *surfaceName, surfaceInfo_v &slist) +{ + uint32_t flags = 0;//, surfFlags = 0; + int surfIndex = 0; + assert(ghlInfo->currentModel); + assert(ghlInfo->currentModel->data.glm && ghlInfo->currentModel->data.glm->header); + if (!ghlInfo->currentModel->data.glm || !ghlInfo->currentModel->data.glm->header) + { + return -1; + } + + // now travel up the skeleton to see if any of it's ancestors have a 'no descendants' turned on + + // find the original surface in the surface list + int surfNum = G2_IsSurfaceLegal((model_t *)ghlInfo->currentModel, surfaceName, &flags); + if ( surfNum != -1 ) + {//must be legal + const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)ghlInfo->currentModel->data.glm->header + sizeof(mdxmHeader_t)); + const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surfNum]); + surfNum = surfInfo->parentIndex; + // walk the surface hierarchy up until we hit the root + while (surfNum != -1) + { + const mdxmSurface_t* parentSurf; + uint32_t parentFlags; + const mdxmSurfHierarchy_t* parentSurfInfo; + + parentSurfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surfNum]); + + // find the original surface in the surface list + //G2 was bug, above comment was accurate, but we don't want the original flags, we want the parent flags + G2_IsSurfaceLegal((model_t *)ghlInfo->currentModel, parentSurfInfo->name, &parentFlags); + + // now see if we already have overriden this surface in the slist + parentSurf = G2_FindSurface(ghlInfo, slist, parentSurfInfo->name, &surfIndex); + if (parentSurf) + { + // set descendants value + parentFlags = slist[surfIndex].offFlags; + } + // now we have the parent flags, lets see if any have the 'no descendants' flag set + if (parentFlags & G2SURFACEFLAG_NODESCENDANTS) + { + flags |= G2SURFACEFLAG_OFF; + break; + } + // set up scan of next parent + surfNum = parentSurfInfo->parentIndex; + } + } + else + { + return -1; + } + if ( flags == 0 ) + {//it's not being overridden by a parent + // now see if we already have overriden this surface in the slist + const mdxmSurface_t *surf = G2_FindSurface(ghlInfo, slist, surfaceName, &surfIndex); + if (surf) + { + // set descendants value + flags = slist[surfIndex].offFlags; + } + // ok, at this point in flags we have what this surface is set to, and the index of the surface itself + } + return flags; +} diff --git a/code/rd-rend2/tr_draw.cpp b/code/rd-rend2/tr_draw.cpp new file mode 100644 index 0000000000..67c42b3c7e --- /dev/null +++ b/code/rd-rend2/tr_draw.cpp @@ -0,0 +1,846 @@ +/* +=========================================================================== +Copyright (C) 1999 - 2005, Id Software, Inc. +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2005 - 2015, ioquake3 contributors +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// tr_draw.c +#include "tr_local.h" + +void RE_GetScreenShot(byte *buffer, int w, int h) +{ + byte *source, *allsource; + byte *src, *dst; + size_t offset = 0, memcount; + int padlen; + + int x, y; + int r, g, b; + float xScale, yScale; + int xx, yy; + + allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen); + source = allsource + offset; + + memcount = (glConfig.vidWidth * 3 + padlen) * glConfig.vidHeight; + + // resample from source + xScale = glConfig.vidWidth / (4.0*w); + yScale = glConfig.vidHeight / (3.0*h); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + r = g = b = 0; + for (yy = 0; yy < 3; yy++) { + for (xx = 0; xx < 4; xx++) { + src = source + 3 * (glConfig.vidWidth * (int)((y * 3 + yy)*yScale) + (int)((x * 4 + xx)*xScale)); + r += src[0]; + g += src[1]; + b += src[2]; + } + } + dst = buffer + 4 * (h * w - y * w + x); + dst[0] = r / 12; + dst[1] = g / 12; + dst[2] = b / 12; + } + } + + // gamma correct + if (glConfig.deviceSupportsGamma) + R_GammaCorrect(buffer, w * h * 4); + + Hunk_FreeTempMemory(allsource); +} + +// this is just a chunk of code from RE_TempRawImage_ReadFromFile() below, subroutinised so I can call it +// from the screen dissolve code as well... +// +static byte *RE_ReSample(byte *pbLoadedPic, int iLoadedWidth, int iLoadedHeight, + byte *pbReSampleBuffer, int *piWidth, int *piHeight + ) +{ + byte *pbReturn = NULL; + + // if not resampling, just return some values and return... + // + if ( pbReSampleBuffer == NULL || (iLoadedWidth == *piWidth && iLoadedHeight == *piHeight) ) + { + // if not resampling, we're done, just return the loaded size... + // + *piWidth = iLoadedWidth; + *piHeight= iLoadedHeight; + pbReturn = pbLoadedPic; + } + else + { + // resample from pbLoadedPic to pbReSampledBuffer... + // + float fXStep = (float)iLoadedWidth / (float)*piWidth; + float fYStep = (float)iLoadedHeight/ (float)*piHeight; + int iTotPixelsPerDownSample = (int)ceil(fXStep) * (int)ceil(fYStep); + + int r,g,b; + + byte *pbDst = pbReSampleBuffer; + + for ( int y=0; y<*piHeight; y++ ) + { + for ( int x=0; x<*piWidth; x++ ) + { + r=g=b=0; + + for ( float yy = (float)y*fYStep; yy < (float)(y+1)*fYStep ; yy+=1 ) + { + for ( float xx = (float)x*fXStep; xx < (float)(x+1)*fXStep ; xx+=1 ) + { + byte *pbSrc = pbLoadedPic + 4 * ( ((int)yy * iLoadedWidth) + (int)xx ); + + assert(pbSrc < pbLoadedPic + (iLoadedWidth * iLoadedHeight * 4) ); + + r += pbSrc[0]; + g += pbSrc[1]; + b += pbSrc[2]; + } + } + + assert(pbDst < pbReSampleBuffer + (*piWidth * *piHeight * 4)); + + pbDst[0] = r / iTotPixelsPerDownSample; + pbDst[1] = g / iTotPixelsPerDownSample; + pbDst[2] = b / iTotPixelsPerDownSample; + pbDst[3] = 255; + pbDst += 4; + } + } + + // set return value... + // + pbReturn = pbReSampleBuffer; + } + + return pbReturn; +} + + +// this is so the server (or anyone else) can get access to raw pixels if they really need to, +// currently it's only used by the server so that savegames can embed a graphic in the auto-save files +// (which can't do a screenshot since they're saved out before the level is drawn). +// +// by default, the pic will be returned as the original dims, but if pbReSampleBuffer != NULL then it's assumed to +// be a big enough buffer to hold the resampled image, which also means that the width and height params are read as +// inputs (as well as still being inherently outputs) and the pic is scaled to that size, and to that buffer. +// +// the return value is either NULL, or a pointer to the pixels to use (which may be either the pbReSampleBuffer param, +// or the local ptr below). +// +// In either case, you MUST call the free-up function afterwards ( RE_TempRawImage_CleanUp() ) to get rid of any temp +// memory after you've finished with the pic. +// +// Note: ALWAYS use the return value if != NULL, even if you passed in a declared resample buffer. This is because the +// resample will get skipped if the values you want are the same size as the pic that it loaded, so it'll return a +// different buffer. +// +// the vertflip param is used for those functions that expect things in OpenGL's upside-down pixel-read format (sigh) +// +// (not brilliantly fast, but it's only used for weird stuff anyway) +// +byte* pbLoadedPic = NULL; + +byte* RE_TempRawImage_ReadFromFile(const char *psLocalFilename, int *piWidth, int *piHeight, byte *pbReSampleBuffer, qboolean qbVertFlip) +{ + RE_TempRawImage_CleanUp(); // jic + + byte *pbReturn = NULL; + + if (psLocalFilename && piWidth && piHeight) + { + int iLoadedWidth, iLoadedHeight; + + R_LoadImage( psLocalFilename, &pbLoadedPic, &iLoadedWidth, &iLoadedHeight); + if ( pbLoadedPic ) + { + pbReturn = RE_ReSample( pbLoadedPic, iLoadedWidth, iLoadedHeight, + pbReSampleBuffer, piWidth, piHeight); + } + } + + if (pbReturn && qbVertFlip) + { + unsigned int *pSrcLine = (unsigned int *) pbReturn; + unsigned int *pDstLine = (unsigned int *) pbReturn + (*piHeight * *piWidth ); // *4 done by compiler (longs) + pDstLine-= *piWidth; // point at start of last line, not first after buffer + + for (int iLineCount=0; iLineCount<*piHeight/2; iLineCount++) + { + for (int x=0; x<*piWidth; x++) + { + unsigned int l = pSrcLine[x]; + pSrcLine[x] = pDstLine[x]; + pDstLine[x] = l; + } + pSrcLine += *piWidth; + pDstLine -= *piWidth; + } + } + + return pbReturn; +} + +void RE_TempRawImage_CleanUp(void) +{ + if ( pbLoadedPic ) + { + R_Free( pbLoadedPic ); + pbLoadedPic = NULL; + } +} + +#ifdef REND2_SP +typedef enum +{ + eDISSOLVE_RT_TO_LT = 0, + eDISSOLVE_LT_TO_RT, + eDISSOLVE_TP_TO_BT, + eDISSOLVE_BT_TO_TP, + eDISSOLVE_CIRCULAR_OUT, // new image comes out from centre + // + eDISSOLVE_RAND_LIMIT, // label only, not valid to select + // + // any others... + // + eDISSOLVE_CIRCULAR_IN, // new image comes in from edges + // + eDISSOLVE_NUMBEROF + +} Dissolve_e; + +typedef struct +{ + int iWidth; + int iHeight; + int iUploadWidth; + int iUploadHeight; + int iScratchPadNumber; + image_t *pImage; // old image screen + image_t *pDissolve; // fuzzy thing + image_t *pBlack; // small black image for clearing + int iStartTime; // 0 = not processing + Dissolve_e eDissolveType; + qboolean bTouchNeeded; + +} Dissolve_t; + +static int PowerOf2(int iArg) +{ + if ( (iArg & (iArg-1)) != 0) + { + int iShift=0; + while (iArg) + { + iArg>>=1; + iShift++; + } + + iArg = 1<width)*(float)iDissolvePercentage)/100.0f); + + // blit the fuzzy-dissolve sprite... + // + x0 = fXScaleFactor * fXboundary; + y0 = 0.0f; + x1 = fXScaleFactor * (fXboundary + Dissolve.pDissolve->width); + y1 = 0.0f; + x2 = x1; + y2 = fYScaleFactor * Dissolve.iHeight; + x3 = x0; + y3 = y2; + + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pDissolve, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, true); + + // blit a blank thing over the area the old screen is to be displayed on to enable screen-writing... + // (to the left of fXboundary) + // + x0 = 0.0f; + y0 = 0.0f; + x1 = fXScaleFactor * (fXboundary + iSAFETY_SPRITE_OVERLAP); + y1 = 0.0f; + x2 = x1; + y2 = fYScaleFactor * Dissolve.iHeight; + x3 = x0; + y3 = y2; + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, false); + } + break; + + case eDISSOLVE_LT_TO_RT: + { + float fXboundary = (((float)(Dissolve.iWidth+(2*Dissolve.pDissolve->width))*(float)iDissolvePercentage)/100.0f) - Dissolve.pDissolve->width; + + // blit the fuzzy-dissolve sprite... + // + x0 = fXScaleFactor * (fXboundary + Dissolve.pDissolve->width); + y0 = 0.0f; + x1 = fXScaleFactor * fXboundary; + y1 = 0.0f; + x2 = x1; + y2 = fYScaleFactor * Dissolve.iHeight; + x3 = x0; + y3 = y2; + + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pDissolve, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, true); + + // blit a blank thing over the area the old screen is to be displayed on to enable screen-writing... + // (to the right of fXboundary) + // + x0 = fXScaleFactor * (( fXboundary + Dissolve.pDissolve->width) - iSAFETY_SPRITE_OVERLAP); + y0 = 0.0f; + x1 = fXScaleFactor * Dissolve.iWidth; + y0 = 0.0f; + x2 = x1; + y2 = fYScaleFactor * Dissolve.iHeight; + x3 = x0; + y3 = y2; + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, false); + } + break; + + case eDISSOLVE_TP_TO_BT: + { + float fYboundary = (((float)(Dissolve.iHeight+(2*Dissolve.pDissolve->width))*(float)iDissolvePercentage)/100.0f) - Dissolve.pDissolve->width; + + // blit the fuzzy-dissolve sprite... + // + x0 = 0.0f; + y0 = fYScaleFactor * (fYboundary + Dissolve.pDissolve->width); + x1 = x0; + y1 = fYScaleFactor * fYboundary; + x2 = fXScaleFactor * Dissolve.iWidth; + y2 = y1; + x3 = x2; + y3 = y0; + + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pDissolve, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, true); + + // blit a blank thing over the area the old screen is to be displayed on to enable screen-writing... + // (underneath fYboundary) + // + x0 = 0.0f; + y0 = fYScaleFactor * ( (fYboundary + Dissolve.pDissolve->width) - iSAFETY_SPRITE_OVERLAP); + x1 = fXScaleFactor * Dissolve.iWidth; + y1 = y0; + x2 = x1; + y2 = fYScaleFactor * Dissolve.iHeight; + x3 = x0; + y3 = y2; + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, false); + } + break; + + case eDISSOLVE_BT_TO_TP: + { + float fYboundary = Dissolve.iHeight - (((float)(Dissolve.iHeight+Dissolve.pDissolve->width)*(float)iDissolvePercentage)/100.0f); + + // blit the fuzzy-dissolve sprite... + // + x0 = 0.0f; + y0 = fYScaleFactor * fYboundary; + x1 = x0; + y1 = fYScaleFactor * (fYboundary + Dissolve.pDissolve->width); + x2 = fXScaleFactor * Dissolve.iWidth; + y2 = y1; + x3 = x2; + y3 = y0; + + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pDissolve, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, true); + + // blit a blank thing over the area the old screen is to be displayed on to enable screen-writing... + // (above fYboundary) + // + x0 = 0.0f; + y0 = 0.0f; + x1 = fXScaleFactor * Dissolve.iWidth; + y1 = y0; + x2 = x1; + y2 = fYScaleFactor * (fYboundary + iSAFETY_SPRITE_OVERLAP); + x3 = x0; + y3 = y2; + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, false); + } + break; + + case eDISSOLVE_CIRCULAR_IN: + { + float fDiagZoom = ( ((float)Dissolve.iWidth*0.8) * (100-iDissolvePercentage))/100.0f; + + // + // blit circular graphic... + // + x0 = fXScaleFactor * ((Dissolve.iWidth/2) - fDiagZoom); + y0 = fYScaleFactor * ((Dissolve.iHeight/2)- fDiagZoom); + x1 = fXScaleFactor * ((Dissolve.iWidth/2) + fDiagZoom); + y1 = y0; + x2 = x1; + y2 = fYScaleFactor * ((Dissolve.iHeight/2)+ fDiagZoom); + x3 = x0; + y3 = y2; + + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pDissolve, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, true); + } + break; + + case eDISSOLVE_CIRCULAR_OUT: + { + float fDiagZoom = ( ((float)Dissolve.iWidth*0.8) * iDissolvePercentage)/100.0f; + + // + // blit circular graphic... + // + x0 = fXScaleFactor * ((Dissolve.iWidth/2) - fDiagZoom); + y0 = fYScaleFactor * ((Dissolve.iHeight/2)- fDiagZoom); + x1 = fXScaleFactor * ((Dissolve.iWidth/2) + fDiagZoom); + y1 = y0; + x2 = x1; + y2 = fYScaleFactor * ((Dissolve.iHeight/2)+ fDiagZoom); + x3 = x0; + y3 = y2; + + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pDissolve, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, true); + // now blit the 4 black squares around it to mask off the rest of the screen... + // + // LHS, top to bottom... + // + RE_Blit(0,0, // x0,y0 + x0+iSAFETY_SPRITE_OVERLAP,0, // x1,y1 + x0+iSAFETY_SPRITE_OVERLAP,(fYScaleFactor * Dissolve.iHeight),// x2,y2 + 0,(fYScaleFactor * Dissolve.iHeight), // x3,y3, + Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, + false + ); + + // RHS top to bottom... + // + RE_Blit(x1-iSAFETY_SPRITE_OVERLAP,0, // x0,y0 + (fXScaleFactor * Dissolve.iWidth),0, // x1,y1 + (fXScaleFactor * Dissolve.iWidth),(fYScaleFactor * Dissolve.iHeight),// x2,y2 + x1-iSAFETY_SPRITE_OVERLAP,(fYScaleFactor * Dissolve.iHeight), // x3,y3, + Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, + false + ); + + // top... + // + RE_Blit(x0-iSAFETY_SPRITE_OVERLAP,0, // x0,y0 + x1+iSAFETY_SPRITE_OVERLAP,0, // x1,y1 + x1+iSAFETY_SPRITE_OVERLAP,y0 + iSAFETY_SPRITE_OVERLAP, // x2,y2 + x0-iSAFETY_SPRITE_OVERLAP,y0 + iSAFETY_SPRITE_OVERLAP, // x3,y3 + Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, + false + ); + + // bottom... + // + RE_Blit(x0-iSAFETY_SPRITE_OVERLAP,y3-iSAFETY_SPRITE_OVERLAP, // x0,y0 + x1+iSAFETY_SPRITE_OVERLAP,y2-iSAFETY_SPRITE_OVERLAP, // x1,y1 + x1+iSAFETY_SPRITE_OVERLAP,(fYScaleFactor * Dissolve.iHeight), // x2,y2 + x0-iSAFETY_SPRITE_OVERLAP,(fYScaleFactor * Dissolve.iHeight), // x3,y3 + Dissolve.pBlack, GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE, + false + ); + } + break; + + default: + { + assert(0); + iDissolvePercentage = 101; // force a dissolve-kill + break; + } + } + + // re-check in case we hit the default case above... + // + if (iDissolvePercentage <= 100) + { + // still dissolving, so now (finally), blit old image over top... + // + x0 = 0.0f; + y0 = 0.0f; + x1 = fXScaleFactor * Dissolve.pImage->width; + y1 = y0; + x2 = x1; + y2 = fYScaleFactor * Dissolve.pImage->height; + x3 = x0; + y3 = y2; + + RE_Blit(x0,y0,x1,y1,x2,y2,x3,y3, Dissolve.pImage, GLS_DEPTHFUNC_EQUAL, false); + } + } + + if (iDissolvePercentage > 100) + { + RE_KillDissolve(); + } + } + + return qfalse; +} + +// return = qtrue(success) else fail, for those interested... +// +qboolean RE_InitDissolve(qboolean bForceCircularExtroWipe) +{ + R_IssuePendingRenderCommands(); + +// ri.Printf( PRINT_ALL, "RE_InitDissolve()\n"); + qboolean bReturn = qfalse; + + if (//Dissolve.iStartTime == 0 // no point in interruping an existing one + //&& + tr.registered == qtrue // ... stops it crashing during first cinematic before the menus... :-) + ) + { + RE_KillDissolve(); // kill any that are already running + + int iPow2VidWidth = PowerOf2( glConfig.vidWidth ); + int iPow2VidHeight = PowerOf2( glConfig.vidHeight); + + int iBufferBytes = iPow2VidWidth * iPow2VidHeight * 4; + byte *pBuffer = (byte *) R_Malloc( iBufferBytes, TAG_TEMP_WORKSPACE, qfalse); + if (pBuffer) + { + // read current screen image... (GL_RGBA should work even on 3DFX in that the RGB parts will be valid at least) + // + qglReadPixels (0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGBA, GL_UNSIGNED_BYTE, pBuffer ); + // + // now expand the pic over the top of itself so that it has a stride value of {PowerOf2(glConfig.vidWidth)} + // (for GL power-of-2 rules) + // + byte *pbSrc = &pBuffer[ glConfig.vidWidth * glConfig.vidHeight * 4]; + byte *pbDst = &pBuffer[ iPow2VidWidth * glConfig.vidHeight * 4]; + // + // ( clear to end, since we've got pbDst nicely setup here) + // + int iClearBytes = &pBuffer[iBufferBytes] - pbDst; + memset(pbDst, 0, iClearBytes); + // + // work out copy/stride vals... + // + iClearBytes = ( iPow2VidWidth - glConfig.vidWidth ) * 4; + int iCopyBytes = glConfig.vidWidth * 4; + // + // do it... + // + for (int y = 0; y < glConfig.vidHeight; y++) + { + pbDst -= iClearBytes; + memset(pbDst,0,iClearBytes); + pbDst -= iCopyBytes; + pbSrc -= iCopyBytes; + memmove(pbDst, pbSrc, iCopyBytes); + } + // + // ok, now we've got the screen image in the top left of the power-of-2 texture square, + // but of course the damn thing's upside down (thanks, GL), so invert it, but only within + // the picture pixels, NOT the upload texture as a whole... + // + byte *pbSwapLineBuffer = (byte *)R_Malloc( iCopyBytes, TAG_TEMP_WORKSPACE, qfalse); + pbSrc = &pBuffer[0]; + pbDst = &pBuffer[(glConfig.vidHeight-1) * iPow2VidWidth * 4]; + for (int y = 0; y < glConfig.vidHeight/2; y++) + { + memcpy(pbSwapLineBuffer, pbDst, iCopyBytes); + memcpy(pbDst, pbSrc, iCopyBytes); + memcpy(pbSrc, pbSwapLineBuffer, iCopyBytes); + pbDst -= iPow2VidWidth*4; + pbSrc += iPow2VidWidth*4; + } + R_Free(pbSwapLineBuffer); + + // + // Now, in case of busted drivers, 3DFX cards, etc etc we stomp the alphas to 255... + // + byte *pPix = pBuffer; + for (int i=0; i iTexSize) { + Dissolve.iUploadWidth = iTexSize; + } + + if (Dissolve.iUploadHeight > iTexSize) { + Dissolve.iUploadHeight = iTexSize; + } + + // alloc resample buffer... (note slight optimisation to avoid spurious alloc) + // + byte *pbReSampleBuffer = ( iPow2VidWidth == Dissolve.iUploadWidth && + iPow2VidHeight == Dissolve.iUploadHeight + )? + NULL : + (byte*) R_Malloc( iPow2VidWidth * iPow2VidHeight * 4, TAG_TEMP_WORKSPACE, qfalse); + + // re-sample screen... + // + byte *pbScreenSprite = RE_ReSample( pBuffer, // byte *pbLoadedPic + iPow2VidWidth, // int iLoadedWidth + iPow2VidHeight, // int iLoadedHeight + // + pbReSampleBuffer, // byte *pbReSampleBuffer + &Dissolve.iUploadWidth, // int *piWidth + &Dissolve.iUploadHeight // int *piHeight + ); + + Dissolve.pImage = R_CreateImage("*DissolveImage", // const char *name + pbScreenSprite, // const byte *pic + Dissolve.iUploadWidth, // int width + Dissolve.iUploadHeight, // int height + IMGTYPE_COLORALPHA, + IMGFLAG_CLAMPTOEDGE, + GL_RGBA8); + + + static byte bBlack[8*8*4]={0}; + for (int j=0; j<8*8*4; j+=4) // itu? + bBlack[j+3]=255; // + + Dissolve.pBlack = R_CreateImage("*DissolveBlack", // const char *name + bBlack, // const byte *pic + 8, // int width + 8, // int height + IMGTYPE_COLORALPHA, + IMGFLAG_CLAMPTOEDGE, + GL_RGBA8); + + if (pbReSampleBuffer) + { + R_Free(pbReSampleBuffer); + } + R_Free(pBuffer); + + // pick dissolve type... + // +#if 0 + // cycles through every dissolve type, for testing... + // + static Dissolve_e eDissolve = (Dissolve_e) 0; + Dissolve.eDissolveType = eDissolve; + eDissolve = (Dissolve_e) (eDissolve+1); + if (eDissolve == eDISSOLVE_RAND_LIMIT) + eDissolve = (Dissolve_e) (eDissolve+1); + if (eDissolve >= eDISSOLVE_NUMBEROF) + eDissolve = (Dissolve_e) 0; +#else + // final (& random) version... + // + Dissolve.eDissolveType = (Dissolve_e) Q_irand( 0, eDISSOLVE_RAND_LIMIT-1); +#endif + + if (bForceCircularExtroWipe) + { + Dissolve.eDissolveType = eDISSOLVE_CIRCULAR_IN; + } + + // ... and load appropriate graphics... + // + + // special tweak, although this code is normally called just before client spawns into world (and + // is therefore pretty much immune to precache issues) I also need to make sure that the inverse + // iris graphic is loaded so for the special case of doing a circular wipe at the end of the last + // level doesn't stall on loading the image. So I'll load it here anyway - to prime the image - + // then allow the random wiper to overwrite the ptr if needed. This way the end of level call + // will be instant. Downside: every level has one extra 256x256 texture. + // Trying to decipher these comments - looks like no problem taking this out. I want the RAM. + imgType_t type = IMGTYPE_COLORALPHA; + int flags = IMGFLAG_CLAMPTOEDGE; + { + Dissolve.pDissolve = R_FindImageFile("gfx/2d/iris_mono_rev", type, flags); + } + + extern cvar_t *com_buildScript; + if (com_buildScript->integer) + { + // register any/all of the possible CASE statements below... + // + + Dissolve.pDissolve = R_FindImageFile( "gfx/2d/iris_mono", type, flags); + Dissolve.pDissolve = R_FindImageFile( "textures/common/dissolve", type, flags); + } + + switch (Dissolve.eDissolveType) + { + case eDISSOLVE_CIRCULAR_IN: + { + Dissolve.pDissolve = R_FindImageFile( "gfx/2d/iris_mono_rev", type, flags); + } + break; + + case eDISSOLVE_CIRCULAR_OUT: + { + Dissolve.pDissolve = R_FindImageFile( "gfx/2d/iris_mono", type, flags); + } + break; + + default: + { + Dissolve.pDissolve = R_FindImageFile( "textures/common/dissolve", type, IMGFLAG_NONE); + } + break; + } + + // all good?... + // + if (Dissolve.pDissolve) // test if image was found, if not, don't do dissolves + { + Dissolve.iStartTime = ri.Milliseconds(); // gets overwritten first time, but MUST be set to NZ + Dissolve.bTouchNeeded = qtrue; + bReturn = qtrue; + } + else + { + RE_KillDissolve(); + } + } + } + + return bReturn; +} +#endif \ No newline at end of file diff --git a/code/rd-rend2/tr_init.cpp b/code/rd-rend2/tr_init.cpp new file mode 100644 index 0000000000..f8287d6b5b --- /dev/null +++ b/code/rd-rend2/tr_init.cpp @@ -0,0 +1,2482 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_init.c -- functions that are not called every frame + +#include "tr_local.h" +//#include "tr_stl.h" +//#include "ghoul2/g2_local.h" +#include "tr_cache.h" +#include "tr_allocator.h" +#include "tr_weather.h" +#include + +#ifdef _G2_GORE +#include "G2_gore_r2.h" +#endif + +static size_t STATIC_UNIFORM_BUFFER_SIZE = 1 * 1024 * 1024; +static size_t FRAME_UNIFORM_BUFFER_SIZE = 8*1024*1024; +static size_t FRAME_VERTEX_BUFFER_SIZE = 12*1024*1024; +static size_t FRAME_INDEX_BUFFER_SIZE = 4*1024*1024; + +#if defined(_WIN32) +extern "C" { + __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +} +#endif + +glconfig_t glConfig; +glconfigExt_t glConfigExt; +glRefConfig_t glRefConfig; +glstate_t glState; +window_t window; + +cvar_t *se_language; +#ifdef JK2_MODE +cvar_t *sp_language; // JK2 +#endif + +cvar_t *r_verbose; +cvar_t *r_ignore; + +cvar_t *r_detailTextures; + +cvar_t *r_znear; +cvar_t *r_zproj; +cvar_t *r_stereoSeparation; + +cvar_t *r_skipBackEnd; + +cvar_t *r_stereo; +cvar_t *r_anaglyphMode; + +cvar_t *r_greyscale; + +cvar_t *r_measureOverdraw; + +cvar_t *r_inGameVideo; +cvar_t *r_fastsky; +cvar_t *r_drawSun; +cvar_t *r_dynamiclight; + +cvar_t *r_lodbias; +cvar_t *r_lodscale; +cvar_t *r_autolodscalevalue; + +cvar_t *r_norefresh; +cvar_t *r_drawentities; +cvar_t *r_drawworld; +cvar_t *r_drawfog; +cvar_t *r_speeds; +cvar_t *r_fullbright; +cvar_t *r_novis; +cvar_t *r_nocull; +cvar_t *r_facePlaneCull; +cvar_t *r_showcluster; +cvar_t *r_nocurves; + +cvar_t *r_allowExtensions; + +cvar_t *r_ext_compressed_textures; +cvar_t *r_ext_multitexture; +cvar_t *r_ext_compiled_vertex_array; +cvar_t *r_ext_texture_env_add; +cvar_t *r_ext_texture_filter_anisotropic; +cvar_t *r_ext_preferred_tc_method; + +cvar_t *r_ext_draw_range_elements; +cvar_t *r_ext_multi_draw_arrays; +cvar_t *r_ext_texture_float; +cvar_t *r_arb_half_float_pixel; +cvar_t *r_ext_framebuffer_multisample; +cvar_t *r_arb_seamless_cube_map; +cvar_t *r_arb_vertex_type_2_10_10_10_rev; +cvar_t *r_arb_buffer_storage; + +cvar_t *r_mergeMultidraws; +cvar_t *r_mergeLeafSurfaces; + +cvar_t *r_cameraExposure; + +cvar_t *r_externalGLSL; + +cvar_t *r_hdr; +cvar_t *r_floatLightmap; + +cvar_t *r_toneMap; +cvar_t *r_forceToneMap; +cvar_t *r_forceToneMapMin; +cvar_t *r_forceToneMapAvg; +cvar_t *r_forceToneMapMax; + +cvar_t *r_autoExposure; +cvar_t *r_forceAutoExposure; +cvar_t *r_forceAutoExposureMin; +cvar_t *r_forceAutoExposureMax; + +cvar_t *r_depthPrepass; +cvar_t *r_ssao; + +cvar_t *r_normalMapping; +cvar_t *r_specularMapping; +cvar_t *r_deluxeMapping; +cvar_t *r_deluxeSpecular; +cvar_t *r_parallaxMapping; +cvar_t *r_forceParallaxBias; +cvar_t *r_cubeMapping; +cvar_t *r_cubeMappingBounces; +cvar_t *r_baseNormalX; +cvar_t *r_baseNormalY; +cvar_t *r_baseParallax; +cvar_t *r_baseSpecular; +cvar_t *r_dlightMode; +cvar_t *r_pshadowDist; +cvar_t *r_imageUpsample; +cvar_t *r_imageUpsampleMaxSize; +cvar_t *r_imageUpsampleType; +cvar_t *r_genNormalMaps; +cvar_t *r_forceSun; +cvar_t *r_forceSunMapLightScale; +cvar_t *r_forceSunLightScale; +cvar_t *r_forceSunAmbientScale; +cvar_t *r_sunlightMode; +cvar_t *r_drawSunRays; +cvar_t *r_sunShadows; +cvar_t *r_shadowFilter; +cvar_t *r_shadowMapSize; +cvar_t *r_shadowCascadeZNear; +cvar_t *r_shadowCascadeZFar; +cvar_t *r_shadowCascadeZBias; +cvar_t *r_ignoreDstAlpha; + +cvar_t *r_ignoreGLErrors; +cvar_t *r_logFile; + +cvar_t *r_texturebits; + +cvar_t *r_drawBuffer; +cvar_t *r_lightmap; +cvar_t *r_vertexLight; +cvar_t *r_uiFullScreen; +cvar_t *r_shadows; +cvar_t *r_flares; +cvar_t *r_nobind; +cvar_t *r_singleShader; +cvar_t *r_roundImagesDown; +cvar_t *r_colorMipLevels; +cvar_t *r_picmip; +cvar_t *r_showtris; +cvar_t *r_showsky; +cvar_t *r_shownormals; +cvar_t *r_finish; +cvar_t *r_clear; +cvar_t *r_markcount; +cvar_t *r_textureMode; +cvar_t *r_offsetFactor; +cvar_t *r_offsetUnits; +cvar_t *r_shadowOffsetFactor; +cvar_t *r_shadowOffsetUnits; +cvar_t *r_gamma; +cvar_t *r_intensity; +cvar_t *r_lockpvs; +cvar_t *r_noportals; +cvar_t *r_portalOnly; + +cvar_t *r_subdivisions; +cvar_t *r_lodCurveError; + + + +cvar_t *r_overBrightBits; +cvar_t *r_mapOverBrightBits; + +cvar_t *r_debugSurface; +cvar_t *r_simpleMipMaps; + +cvar_t *r_showImages; + +cvar_t *r_ambientScale; +cvar_t *r_directedScale; +cvar_t *r_debugLight; +cvar_t *r_debugSort; +cvar_t *r_printShaders; +cvar_t *r_saveFontData; + +#ifdef _DEBUG +cvar_t *r_noPrecacheGLA; +#endif + +cvar_t *r_noServerGhoul2; // In SP renderer CVAR is actually r_noghoul2! +cvar_t *r_Ghoul2AnimSmooth=0; +cvar_t *r_Ghoul2UnSqashAfterSmooth=0; +//cvar_t *r_Ghoul2UnSqash; +//cvar_t *r_Ghoul2TimeBase=0; from single player +//cvar_t *r_Ghoul2NoLerp; +//cvar_t *r_Ghoul2NoBlend; +//cvar_t *r_Ghoul2BlendMultiplier=0; + +cvar_t *broadsword=0; +cvar_t *broadsword_kickbones=0; +cvar_t *broadsword_kickorigin=0; +cvar_t *broadsword_playflop=0; +cvar_t *broadsword_dontstopanim=0; +cvar_t *broadsword_waitforshot=0; +cvar_t *broadsword_smallbbox=0; +cvar_t *broadsword_extra1=0; +cvar_t *broadsword_extra2=0; + +cvar_t *broadsword_effcorr=0; +cvar_t *broadsword_ragtobase=0; +cvar_t *broadsword_dircap=0; + +cvar_t *r_marksOnTriangleMeshes; + +cvar_t *r_aviMotionJpegQuality; +cvar_t *r_screenshotJpegQuality; +cvar_t *r_surfaceSprites; + +// the limits apply to the sum of all scenes in a frame -- +// the main view, all the 3D icons, etc +#define DEFAULT_MAX_POLYS 600 +#define DEFAULT_MAX_POLYVERTS 3000 +cvar_t *r_maxpolys; +cvar_t *r_maxpolyverts; +int max_polys; +int max_polyverts; + +cvar_t *r_dynamicGlow; +cvar_t *r_dynamicGlowPasses; +cvar_t *r_dynamicGlowDelta; +cvar_t *r_dynamicGlowIntensity; +cvar_t *r_dynamicGlowSoft; +cvar_t *r_dynamicGlowWidth; +cvar_t *r_dynamicGlowHeight; + +cvar_t *r_debugContext; +cvar_t *r_debugWeather; + +cvar_t *r_aspectCorrectFonts; + +cvar_t* com_buildScript; +cvar_t* sv_mapname; +cvar_t* sv_mapChecksum; +cvar_t* r_modelpoolmegs; +cvar_t* r_environmentMapping; +cvar_t* r_ext_compressed_lightmaps; + +#define ri_Cvar_Get_NoComm(varname, value, flag, comment) ri.Cvar_Get(varname, value, flag) + +#ifdef REND2_SP +static qboolean gbAllowScreenDissolve = qtrue; +extern qboolean RE_InitDissolve(qboolean bForceCircularExtroWipe); +extern qboolean RE_ProcessDissolve(void); +#endif // REND2_SP + +// STUBS, REPLACEME + +void stub_R_InitWorldEffects(void) {} +bool stub_R_SetTempGlobalFogColor(vec3_t color) { return qfalse; } + +float tr_distortionAlpha = 1.0f; //opaque +float tr_distortionStretch = 0.0f; //no stretch override +qboolean tr_distortionPrePost = qfalse; //capture before postrender phase? +qboolean tr_distortionNegate = qfalse; //negative blend mode +float* stub_get_tr_distortionAlpha(void) { return &tr_distortionAlpha; } +float* stub_get_tr_distortionStretch(void) { return &tr_distortionStretch; } +qboolean* stub_get_tr_distortionPrePost(void) { return &tr_distortionPrePost; } +qboolean* stub_get_tr_distortionNegate(void) { return &tr_distortionNegate; } + +extern void RB_SetGL2D (void); +static void R_Splash() +{ + const GLfloat black[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + qglViewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); + qglClearBufferfv(GL_COLOR, 0, black); + qglClear(GL_DEPTH_BUFFER_BIT); + + GLSL_InitSplashScreenShader(); + + GL_Cull(CT_TWO_SIDED); + + image_t *pImage = R_FindImageFile( "menu/splash", IMGTYPE_COLORALPHA, IMGFLAG_NONE); + if (pImage ) + GL_Bind( pImage ); + + GL_State(GLS_DEPTHTEST_DISABLE); + GLSL_BindProgram(&tr.splashScreenShader); + RB_InstantTriangle(); + + ri.WIN_Present(&window); +} + +/* +** GLW_CheckForExtension + + Cannot use strstr directly to differentiate between (for eg) reg_combiners and reg_combiners2 +*/ +bool GL_CheckForExtension(const char *ext) +{ + const char *ptr = Q_stristr( glConfigExt.originalExtensionString, ext ); + if (ptr == NULL) + return false; + ptr += strlen(ext); + return ((*ptr == ' ') || (*ptr == '\0')); // verify it's complete string. +} + + +void GLW_InitTextureCompression( void ) +{ + bool newer_tc, old_tc; + + // Check for available tc methods. + newer_tc = GL_CheckForExtension("ARB_texture_compression") && GL_CheckForExtension("EXT_texture_compression_s3tc"); + old_tc = GL_CheckForExtension("GL_S3_s3tc"); + + if ( old_tc ) + { + Com_Printf ("...GL_S3_s3tc available\n" ); + } + + if ( newer_tc ) + { + Com_Printf ("...GL_EXT_texture_compression_s3tc available\n" ); + } + + if ( !r_ext_compressed_textures->value ) + { + // Compressed textures are off + glConfig.textureCompression = TC_NONE; + Com_Printf ("...ignoring texture compression\n" ); + } + else if ( !old_tc && !newer_tc ) + { + // Requesting texture compression, but no method found + glConfig.textureCompression = TC_NONE; + Com_Printf ("...no supported texture compression method found\n" ); + Com_Printf (".....ignoring texture compression\n" ); + } + else + { + // some form of supported texture compression is avaiable, so see if the user has a preference + if ( r_ext_preferred_tc_method->integer == TC_NONE ) + { + // No preference, so pick the best + if ( newer_tc ) + { + Com_Printf ("...no tc preference specified\n" ); + Com_Printf (".....using GL_EXT_texture_compression_s3tc\n" ); + glConfig.textureCompression = TC_S3TC_DXT; + } + else + { + Com_Printf ("...no tc preference specified\n" ); + Com_Printf (".....using GL_S3_s3tc\n" ); + glConfig.textureCompression = TC_S3TC; + } + } + else + { + // User has specified a preference, now see if this request can be honored + if ( old_tc && newer_tc ) + { + // both are avaiable, so we can use the desired tc method + if ( r_ext_preferred_tc_method->integer == TC_S3TC ) + { + Com_Printf ("...using preferred tc method, GL_S3_s3tc\n" ); + glConfig.textureCompression = TC_S3TC; + } + else + { + Com_Printf ("...using preferred tc method, GL_EXT_texture_compression_s3tc\n" ); + glConfig.textureCompression = TC_S3TC_DXT; + } + } + else + { + // Both methods are not available, so this gets trickier + if ( r_ext_preferred_tc_method->integer == TC_S3TC ) + { + // Preferring to user older compression + if ( old_tc ) + { + Com_Printf ("...using GL_S3_s3tc\n" ); + glConfig.textureCompression = TC_S3TC; + } + else + { + // Drat, preference can't be honored + Com_Printf ("...preferred tc method, GL_S3_s3tc not available\n" ); + Com_Printf (".....falling back to GL_EXT_texture_compression_s3tc\n" ); + glConfig.textureCompression = TC_S3TC_DXT; + } + } + else + { + // Preferring to user newer compression + if ( newer_tc ) + { + Com_Printf ("...using GL_EXT_texture_compression_s3tc\n" ); + glConfig.textureCompression = TC_S3TC_DXT; + } + else + { + // Drat, preference can't be honored + Com_Printf ("...preferred tc method, GL_EXT_texture_compression_s3tc not available\n" ); + Com_Printf (".....falling back to GL_S3_s3tc\n" ); + glConfig.textureCompression = TC_S3TC; + } + } + } + } + } +} + +// Truncates the GL extensions string by only allowing up to 'maxExtensions' extensions in the string. +static const char *TruncateGLExtensionsString (const char *extensionsString, int maxExtensions) +{ + const char *p = extensionsString; + const char *q; + int numExtensions = 0; + size_t extensionsLen = strlen (extensionsString); + + char *truncatedExtensions; + + while ( (q = strchr (p, ' ')) != NULL && numExtensions < maxExtensions ) + { + p = q + 1; + numExtensions++; + } + + if ( q != NULL ) + { + // We still have more extensions. We'll call this the end + + extensionsLen = p - extensionsString - 1; + } + + truncatedExtensions = (char *)R_Malloc(extensionsLen + 1, TAG_GENERAL); + Q_strncpyz (truncatedExtensions, extensionsString, extensionsLen + 1); + + return truncatedExtensions; +} + +static const char *GetGLExtensionsString() +{ + GLint numExtensions; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + size_t extensionStringLen = 0; + + for ( int i = 0; i < numExtensions; i++ ) + { + extensionStringLen += strlen((const char *)qglGetStringi(GL_EXTENSIONS, i)) + 1; + } + + char *extensionString = (char *)R_Malloc(extensionStringLen + 1, TAG_GENERAL); + char *p = extensionString; + for ( int i = 0; i < numExtensions; i++ ) + { + const char *extension = (const char *)qglGetStringi(GL_EXTENSIONS, i); + while ( *extension != '\0' ) + *p++ = *extension++; + + *p++ = ' '; + } + + *p = '\0'; + assert((p - extensionString) == extensionStringLen); + + return extensionString; +} + +/* +** InitOpenGL +** +** This function is responsible for initializing a valid OpenGL subsystem. This +** is done by calling GLimp_Init (which gives us a working OGL subsystem) then +** setting variables, checking GL constants, and reporting the gfx system config +** to the user. +*/ +static void InitOpenGL( void ) +{ + // + // initialize OS specific portions of the renderer + // + // GLimp_Init directly or indirectly references the following cvars: + // - r_fullscreen + // - r_mode + // - r_(color|depth|stencil)bits + // - r_ignorehwgamma + // - r_gamma + // + + if ( glConfig.vidWidth == 0 ) + { + windowDesc_t windowDesc = {}; + memset(&glConfig, 0, sizeof(glConfig)); + + windowDesc.api = GRAPHICS_API_OPENGL; + windowDesc.gl.majorVersion = 3; + windowDesc.gl.minorVersion = 2; + windowDesc.gl.profile = GLPROFILE_CORE; + if ( r_debugContext->integer ) + windowDesc.gl.contextFlags = GLCONTEXT_DEBUG; + + window = ri.WIN_Init(&windowDesc, &glConfig); + + GLimp_InitCoreFunctions(); + + Com_Printf( "GL_RENDERER: %s\n", (char *)qglGetString (GL_RENDERER) ); + + // get our config strings + glConfig.vendor_string = (const char *)qglGetString (GL_VENDOR); + glConfig.renderer_string = (const char *)qglGetString (GL_RENDERER); + glConfig.version_string = (const char *)qglGetString (GL_VERSION); + glConfig.extensions_string = GetGLExtensionsString(); + + glConfigExt.originalExtensionString = glConfig.extensions_string; + glConfig.extensions_string = TruncateGLExtensionsString(glConfigExt.originalExtensionString, 128); + + // OpenGL driver constants + qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &glConfig.maxTextureSize ); + + // Determine GPU IHV + if ( Q_stristr( glConfig.vendor_string, "ATI Technologies Inc." ) ) + { + glRefConfig.hardwareVendor = IHV_AMD; + } + else if ( Q_stristr( glConfig.vendor_string, "NVIDIA" ) ) + { + glRefConfig.hardwareVendor = IHV_NVIDIA; + } + else if ( Q_stristr( glConfig.vendor_string, "INTEL") ) + { + glRefConfig.hardwareVendor = IHV_INTEL; + } + else + { + glRefConfig.hardwareVendor = IHV_UNKNOWN; + } + + // stubbed or broken drivers may have reported 0... + glConfig.maxTextureSize = Q_max(0, glConfig.maxTextureSize); + + // initialize extensions + GLimp_InitExtensions(); + + // Create the default VAO + GLuint vao; + qglGenVertexArrays(1, &vao); + qglBindVertexArray(vao); + tr.globalVao = vao; + + // set default state + GL_SetDefaultState(); + + R_Splash(); //get something on screen asap + } + else + { + // set default state + GL_SetDefaultState(); + } +} + +/* +================== +GL_CheckErrors +================== +*/ +void GL_CheckErrs( const char *file, int line ) { +#if defined(_DEBUG) + GLenum err; + char s[64]; + + err = qglGetError(); + if ( err == GL_NO_ERROR ) { + return; + } + if ( r_ignoreGLErrors->integer ) { + return; + } + switch( err ) { + case GL_INVALID_ENUM: + strcpy( s, "GL_INVALID_ENUM" ); + break; + case GL_INVALID_VALUE: + strcpy( s, "GL_INVALID_VALUE" ); + break; + case GL_INVALID_OPERATION: + strcpy( s, "GL_INVALID_OPERATION" ); + break; + case GL_OUT_OF_MEMORY: + strcpy( s, "GL_OUT_OF_MEMORY" ); + break; + default: + Com_sprintf( s, sizeof(s), "%i", err); + break; + } + + ri.Error( ERR_FATAL, "GL_CheckErrors: %s in %s at line %d", s , file, line); +#endif +} + +/* +============================================================================== + + SCREEN SHOTS + +NOTE TTimo +some thoughts about the screenshots system: +screenshots get written in fs_homepath + fs_gamedir +vanilla q3 .. baseq3/screenshots/ *.tga +team arena .. missionpack/screenshots/ *.tga + +two commands: "screenshot" and "screenshotJPEG" +we use statics to store a count and start writing the first screenshot/screenshot????.tga (.jpg) available +(with FS_FileExists / FS_FOpenFileWrite calls) +FIXME: the statics don't get a reinit between fs_game changes + +============================================================================== +*/ + +/* +================== +RB_ReadPixels + +Reads an image but takes care of alignment issues for reading RGB images. + +Reads a minimum offset for where the RGB data starts in the image from +integer stored at pointer offset. When the function has returned the actual +offset was written back to address offset. This address will always have an +alignment of packAlign to ensure efficient copying. + +Stores the length of padding after a line of pixels to address padlen + +Return value must be freed with R2_Hunk_FreeTempMemory() +================== +*/ + +byte *RB_ReadPixels( + int x, int y, int width, int height, size_t *offset, int *padlen) +{ + byte *buffer, *bufstart; + int padwidth, linelen; + GLint packAlign; + + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + linelen = width * 3; + padwidth = PAD(linelen, packAlign); + + // Allocate a few more bytes so that we can choose an alignment we like + buffer = (byte *)Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); + + bufstart = (byte*)(PADP((intptr_t) buffer + *offset, packAlign)); + qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); + + *offset = bufstart - buffer; + *padlen = padwidth - linelen; + + return buffer; +} + +static void ConvertRGBtoBGR( + byte *dst, const byte *src, int stride, int width, int height) +{ + const byte *row = src; + for (int y = 0; y < height; ++y) + { + const byte *pixelRGB = row; + for (int x = 0; x < width; ++x) + { + // swap rgb to bgr + const byte temp = pixelRGB[0]; + *dst++ = pixelRGB[2]; + *dst++ = pixelRGB[1]; + *dst++ = temp; + + pixelRGB += 3; + } + + row += stride; + } +} + +static void R_SaveTGA( + const char *filename, + const byte *pixels, + int width, + int height, + int stride) +{ + const size_t headerSize = 18; + const size_t pixelBufferSize = stride * height; + const size_t bufferSize = headerSize + pixelBufferSize; + + byte *buffer = (byte *)Hunk_AllocateTempMemory(bufferSize); + + // Write TGA header + Com_Memset(buffer, 0, headerSize); + buffer[2] = 2; // uncompressed type + buffer[12] = width & 255; + buffer[13] = width >> 8; + buffer[14] = height & 255; + buffer[15] = height >> 8; + buffer[16] = 24; // pixel size + + ConvertRGBtoBGR(buffer + headerSize, pixels, stride, width, height); + + ri.FS_WriteFile(filename, buffer, bufferSize); + Hunk_FreeTempMemory(buffer); +} + +/* +================== +R_SaveScreenshotTGA +================== +*/ +static void R_SaveScreenshotTGA( + const screenshotReadback_t *screenshotReadback, byte *pixels) +{ + R_SaveTGA( + screenshotReadback->filename, + pixels, + screenshotReadback->width, + screenshotReadback->height, + screenshotReadback->strideInBytes); +} + +/* +================== +R_SaveScreenshotPNG +================== +*/ +static void R_SaveScreenshotPNG( + const screenshotReadback_t *screenshotReadback, byte *pixels) +{ + RE_SavePNG( + screenshotReadback->filename, + pixels, + screenshotReadback->width, + screenshotReadback->height, + 3); +} + +/* +================== +R_SaveScreenshotJPG +================== +*/ +static void R_SaveScreenshotJPG( + const screenshotReadback_t *screenshotReadback, byte *pixels) +{ + RE_SaveJPG( + screenshotReadback->filename, + r_screenshotJpegQuality->integer, + screenshotReadback->width, + screenshotReadback->height, + pixels, + screenshotReadback->strideInBytes - screenshotReadback->rowInBytes); +} + +void R_SaveScreenshot(screenshotReadback_t *screenshotReadback) +{ + qglBindBuffer(GL_PIXEL_PACK_BUFFER, screenshotReadback->pbo); + + byte *pixelBuffer = static_cast( + qglMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY)); + + if (pixelBuffer == nullptr) + { + ri.Printf( + PRINT_ALL, + S_COLOR_RED "Failed to read screenshot data from GPU\n"); + } + else + { + const int height = screenshotReadback->height; + const int stride = screenshotReadback->strideInBytes; + const size_t pixelBufferSize = stride * height; + + byte *pixels = (byte *)Hunk_AllocateTempMemory(pixelBufferSize); + Com_Memcpy(pixels, pixelBuffer, pixelBufferSize); + qglUnmapBuffer(GL_PIXEL_PACK_BUFFER); + + if (glConfig.deviceSupportsGamma) + R_GammaCorrect(pixels, pixelBufferSize); + + switch (screenshotReadback->format) + { + case SSF_JPEG: + R_SaveScreenshotJPG(screenshotReadback, pixels); + break; + + case SSF_TGA: + R_SaveScreenshotTGA(screenshotReadback, pixels); + break; + + case SSF_PNG: + R_SaveScreenshotPNG(screenshotReadback, pixels); + break; + } + + Hunk_FreeTempMemory(pixels); + } + + qglDeleteBuffers(1, &screenshotReadback->pbo); + screenshotReadback->pbo = 0; +} + +/* +================== +R_TakeScreenshotCmd +================== +*/ +const void *RB_TakeScreenshotCmd( const void *data ) { + const screenshotCommand_t *cmd; + + cmd = (const screenshotCommand_t *)data; + + // finish any 2D drawing if needed + if (tess.numIndexes) + RB_EndSurface(); + + const int frameNumber = backEndData->realFrameNumber; + gpuFrame_t *thisFrame = &backEndData->frames[frameNumber % MAX_FRAMES]; + screenshotReadback_t *screenshot = &thisFrame->screenshotReadback; + + GLint packAlign; + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + const int linelen = cmd->width * 3; + const int strideInBytes = PAD(linelen, packAlign); + + qglGenBuffers(1, &screenshot->pbo); + qglBindBuffer(GL_PIXEL_PACK_BUFFER, screenshot->pbo); + qglBufferData( + GL_PIXEL_PACK_BUFFER, + strideInBytes * cmd->height, + nullptr, + GL_STATIC_COPY); + qglReadPixels( + cmd->x, cmd->y, cmd->width, cmd->height, GL_RGB, GL_UNSIGNED_BYTE, 0); + + screenshot->strideInBytes = strideInBytes; + screenshot->rowInBytes = linelen; + screenshot->width = cmd->width; + screenshot->height = cmd->height; + screenshot->format = cmd->format; + Q_strncpyz( + screenshot->filename, cmd->fileName, sizeof(screenshot->filename)); + + return (const void *)(cmd + 1); +} + +/* +================== +R_TakeScreenshot +================== +*/ +void R_TakeScreenshot( int x, int y, int width, int height, char *name, screenshotFormat_t format ) { + static char fileName[MAX_OSPATH]; // bad things if two screenshots per frame? + screenshotCommand_t *cmd; + + cmd = (screenshotCommand_t *)R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_SCREENSHOT; + + cmd->x = x; + cmd->y = y; + cmd->width = width; + cmd->height = height; + Q_strncpyz( fileName, name, sizeof(fileName) ); + cmd->fileName = fileName; + cmd->format = format; +} + +/* +================== +R_ScreenshotFilename +================== +*/ +void R_ScreenshotFilename( char *buf, int bufSize, const char *ext ) { + time_t rawtime; + char timeStr[32] = {0}; // should really only reach ~19 chars + + time( &rawtime ); + strftime( timeStr, sizeof( timeStr ), "%Y-%m-%d_%H-%M-%S", localtime( &rawtime ) ); // or gmtime + + Com_sprintf( buf, bufSize, "screenshots/shot%s%s", timeStr, ext ); +} + +/* +==================== +R_LevelShot + +levelshots are specialized 256*256 thumbnails for +the menu system, sampled down from full screen distorted images +==================== +*/ +#define LEVELSHOTSIZE 256 +static void R_LevelShot( void ) { + char checkname[MAX_OSPATH]; + byte *buffer; + byte *source, *allsource; + byte *src, *dst; + size_t offset = 0; + int padlen; + int x, y; + int r, g, b; + float xScale, yScale; + int xx, yy; + + Com_sprintf( checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName ); + + allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen); + source = allsource + offset; + + buffer = (byte *)Hunk_AllocateTempMemory(LEVELSHOTSIZE * LEVELSHOTSIZE*3 + 18); + Com_Memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = LEVELSHOTSIZE & 255; + buffer[13] = LEVELSHOTSIZE >> 8; + buffer[14] = LEVELSHOTSIZE & 255; + buffer[15] = LEVELSHOTSIZE >> 8; + buffer[16] = 24; // pixel size + + // resample from source + xScale = glConfig.vidWidth / (4.0*LEVELSHOTSIZE); + yScale = glConfig.vidHeight / (3.0*LEVELSHOTSIZE); + for ( y = 0 ; y < LEVELSHOTSIZE ; y++ ) { + for ( x = 0 ; x < LEVELSHOTSIZE ; x++ ) { + r = g = b = 0; + for ( yy = 0 ; yy < 3 ; yy++ ) { + for ( xx = 0 ; xx < 4 ; xx++ ) { + src = source + 3 * ( glConfig.vidWidth * (int)( (y*3+yy)*yScale ) + (int)( (x*4+xx)*xScale ) ); + r += src[0]; + g += src[1]; + b += src[2]; + } + } + dst = buffer + 18 + 3 * ( y * LEVELSHOTSIZE + x ); + dst[0] = b / 12; + dst[1] = g / 12; + dst[2] = r / 12; + } + } + + // gamma correct + if ( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) { + R_GammaCorrect( buffer + 18, LEVELSHOTSIZE * LEVELSHOTSIZE * 3 ); + } + + ri.FS_WriteFile( checkname, buffer, LEVELSHOTSIZE * LEVELSHOTSIZE*3 + 18 ); + + Hunk_FreeTempMemory( buffer ); + Hunk_FreeTempMemory( allsource ); + + ri.Printf( PRINT_ALL, "Wrote %s\n", checkname ); +} + +/* +================== +R_ScreenShotTGA_f + +screenshot +screenshot [silent] +screenshot [levelshot] +screenshot [filename] + +Doesn't print the pacifier message if there is a second arg +================== +*/ +void R_ScreenShotTGA_f (void) { + char checkname[MAX_OSPATH] = {0}; + qboolean silent = qfalse; + + if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { + R_LevelShot(); + return; + } + + if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) + silent = qtrue; + + if ( ri.Cmd_Argc() == 2 && !silent ) { + // explicit filename + Com_sprintf( checkname, sizeof( checkname ), "screenshots/%s.tga", ri.Cmd_Argv( 1 ) ); + } + else { + // timestamp the file + R_ScreenshotFilename( checkname, sizeof( checkname ), ".tga" ); + + if ( ri.FS_FileExists( checkname ) ) { + Com_Printf( "ScreenShot: Couldn't create a file\n"); + return; + } + } + + R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, SSF_TGA ); + + if ( !silent ) + ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); +} + +void R_ScreenShotPNG_f (void) { + char checkname[MAX_OSPATH] = {0}; + qboolean silent = qfalse; + + if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { + R_LevelShot(); + return; + } + + if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) + silent = qtrue; + + if ( ri.Cmd_Argc() == 2 && !silent ) { + // explicit filename + Com_sprintf( checkname, sizeof( checkname ), "screenshots/%s.png", ri.Cmd_Argv( 1 ) ); + } + else { + // timestamp the file + R_ScreenshotFilename( checkname, sizeof( checkname ), ".png" ); + + if ( ri.FS_FileExists( checkname ) ) { + Com_Printf( "ScreenShot: Couldn't create a file\n"); + return; + } + } + + R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, SSF_PNG ); + + if ( !silent ) + ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); +} + +void R_ScreenShotJPEG_f (void) { + char checkname[MAX_OSPATH] = {0}; + qboolean silent = qfalse; + + if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { + R_LevelShot(); + return; + } + + if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) + silent = qtrue; + + if ( ri.Cmd_Argc() == 2 && !silent ) { + // explicit filename + Com_sprintf( checkname, sizeof( checkname ), "screenshots/%s.jpg", ri.Cmd_Argv( 1 ) ); + } + else { + // timestamp the file + R_ScreenshotFilename( checkname, sizeof( checkname ), ".jpg" ); + + if ( ri.FS_FileExists( checkname ) ) { + Com_Printf( "ScreenShot: Couldn't create a file\n"); + return; + } + } + + R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, SSF_JPEG ); + + if ( !silent ) + ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); +} + +//============================================================================ + +/* +================== +RB_TakeVideoFrameCmd +================== +*/ + +const void *RB_TakeVideoFrameCmd( const void *data ) +{ + const videoFrameCommand_t *cmd; + byte *cBuf; + size_t memcount, linelen; + int padwidth, avipadwidth, padlen, avipadlen; + GLint packAlign; + + // finish any 2D drawing if needed + if (tess.numIndexes) + RB_EndSurface(); + + cmd = (const videoFrameCommand_t *)data; +#ifndef REND2_SP + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + linelen = cmd->width * 3; + + // Alignment stuff for glReadPixels + padwidth = PAD(linelen, packAlign); + padlen = padwidth - linelen; + // AVI line padding + avipadwidth = PAD(linelen, AVI_LINE_PADDING); + avipadlen = avipadwidth - linelen; + + cBuf = (byte*)(PADP(cmd->captureBuffer, packAlign)); + + qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, + GL_UNSIGNED_BYTE, cBuf); + + memcount = padwidth * cmd->height; + + // gamma correct + if(glConfig.deviceSupportsGamma) + R_GammaCorrect(cBuf, memcount); + + if(cmd->motionJpeg) + { + memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height, + r_aviMotionJpegQuality->integer, + cmd->width, cmd->height, cBuf, padlen); + ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); + } + else + { + byte *lineend, *memend; + byte *srcptr, *destptr; + + srcptr = cBuf; + destptr = cmd->encodeBuffer; + memend = srcptr + memcount; + + // swap R and B and remove line paddings + while(srcptr < memend) + { + lineend = srcptr + linelen; + while(srcptr < lineend) + { + *destptr++ = srcptr[2]; + *destptr++ = srcptr[1]; + *destptr++ = srcptr[0]; + srcptr += 3; + } + + Com_Memset(destptr, '\0', avipadlen); + destptr += avipadlen; + + srcptr += padlen; + } + + ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height); + } +#endif + return (const void *)(cmd + 1); +} + + +//============================================================================ + +/* +** GL_SetDefaultState +*/ +void GL_SetDefaultState( void ) +{ + qglClearDepth( 1.0f ); + + qglCullFace(GL_FRONT); + + // initialize downstream texture unit if we're running + // in a multitexture environment + GL_SelectTexture( 1 ); + GL_TextureMode( r_textureMode->string ); + GL_SelectTexture( 0 ); + + GL_TextureMode( r_textureMode->string ); + + //qglShadeModel( GL_SMOOTH ); + qglDepthFunc( GL_LEQUAL ); + + Com_Memset(&glState, 0, sizeof(glState)); + + // + // make sure our GL state vector is set correctly + // + glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE; + glState.maxDepth = 1.0f; + qglDepthRange(0.0f, 1.0f); + + qglUseProgram(0); + + qglBindBuffer(GL_ARRAY_BUFFER, 0); + qglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglDepthMask( GL_TRUE ); + qglDisable( GL_DEPTH_TEST ); + qglEnable( GL_SCISSOR_TEST ); + qglEnable(GL_PROGRAM_POINT_SIZE); + qglDisable( GL_CULL_FACE ); + qglDisable( GL_BLEND ); + + qglEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + + qglStencilFunc(GL_ALWAYS, 0, 0xff); + qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP); + qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP); + + // set default vertex color + qglVertexAttrib4f(ATTR_INDEX_COLOR, 1.0f, 1.0f, 1.0f, 1.0f); +} + +/* +================ +R_PrintLongString + +Workaround for ri.Printf's 1024 characters buffer limit. +================ +*/ +void R_PrintLongString(const char *string) { + char buffer[1024]; + const char *p; + int size = strlen(string); + + p = string; + while(size > 0) + { + Q_strncpyz(buffer, p, sizeof (buffer) ); + ri.Printf( PRINT_ALL, "%s", buffer ); + p += 1023; + size -= 1023; + } +} + +/* +================ +GfxInfo_f +================ +*/ +static void GfxInfo_f( void ) +{ + const char *enablestrings[] = + { + "disabled", + "enabled" + }; + const char *fsstrings[] = + { + "windowed", + "fullscreen" + }; + const char *noborderstrings[] = + { + "", + "noborder " + }; + + int fullscreen = ri.Cvar_VariableIntegerValue("r_fullscreen"); + int noborder = ri.Cvar_VariableIntegerValue("r_noborder"); + + ri.Printf( PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string ); + ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); + ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); + ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); + R_PrintLongString( glConfigExt.originalExtensionString ); + ri.Printf( PRINT_ALL, "\n" ); + ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); + ri.Printf( PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); + ri.Printf( PRINT_ALL, "MODE: %d, %d x %d %s%s hz:", + ri.Cvar_VariableIntegerValue("r_mode"), + glConfig.vidWidth, glConfig.vidHeight, + fullscreen == 0 ? noborderstrings[noborder == 1] : noborderstrings[0], + fsstrings[fullscreen == 1] ); + if ( glConfig.displayFrequency ) + { + ri.Printf( PRINT_ALL, "%d\n", glConfig.displayFrequency ); + } + else + { + ri.Printf( PRINT_ALL, "N/A\n" ); + } + if ( glConfig.deviceSupportsGamma ) + { + ri.Printf( PRINT_ALL, "GAMMA: hardware w/ %d overbright bits\n", tr.overbrightBits ); + } + else + { + ri.Printf( PRINT_ALL, "GAMMA: software w/ %d overbright bits\n", tr.overbrightBits ); + } + + ri.Printf( PRINT_ALL, "texturemode: %s\n", r_textureMode->string ); + ri.Printf( PRINT_ALL, "picmip: %d\n", r_picmip->integer ); + ri.Printf( PRINT_ALL, "texture bits: %d\n", r_texturebits->integer ); + + if ( r_vertexLight->integer ) + { + ri.Printf( PRINT_ALL, "HACK: using vertex lightmap approximation\n" ); + } + int displayRefresh = ri.Cvar_VariableIntegerValue("r_displayRefresh"); + if ( displayRefresh ) { + ri.Printf( PRINT_ALL, "Display refresh set to %d\n", displayRefresh ); + } + + if ( r_finish->integer ) { + ri.Printf( PRINT_ALL, "Forcing glFinish\n" ); + } + + ri.Printf( PRINT_ALL, "Dynamic Glow: %s\n", enablestrings[r_dynamicGlow->integer != 0] ); +} + +/* +================ +GfxMemInfo_f +================ +*/ +void GfxMemInfo_f( void ) +{ + switch (glRefConfig.memInfo) + { + case MI_NONE: + { + ri.Printf(PRINT_ALL, "No extension found for GPU memory info.\n"); + } + break; + case MI_NVX: + { + int value; + + qglGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: %ikb\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: %ikb\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: %ikb\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTION_COUNT_NVX: %i\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTED_MEMORY_NVX: %ikb\n", value); + } + break; + case MI_ATI: + { + // GL_ATI_meminfo + int value[4]; + + qglGetIntegerv(GL_VBO_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "VBO_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + + qglGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "TEXTURE_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + + qglGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "RENDERBUFFER_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + } + break; + } +} + +static void R_CaptureFrameData_f() +{ + int argc = ri.Cmd_Argc(); + if ( argc <= 1 ) + { + ri.Printf( PRINT_ALL, "Usage: %s \n", ri.Cmd_Argv(0)); + return; + } + + + const char *cmd = ri.Cmd_Argv(1); + if ( Q_stricmp(cmd, "single") == 0 ) + tr.numFramesToCapture = 1; + else if ( Q_stricmp(cmd, "multi") == 0 ) + tr.numFramesToCapture = atoi(ri.Cmd_Argv(1)); + + int len = ri.FS_FOpenFileByMode("rend2.log", &tr.debugFile, FS_APPEND); + if ( len == -1 || !tr.debugFile ) + { + ri.Printf( PRINT_ERROR, "Failed to open rend2 log file\n" ); + tr.numFramesToCapture = 0; + } +} + +typedef struct consoleCommand_s { + const char *cmd; + xcommand_t func; +} consoleCommand_t; + +static consoleCommand_t commands[] = { + { "imagelist", R_ImageList_f }, + { "shaderlist", R_ShaderList_f }, + { "skinlist", R_SkinList_f }, + { "fontlist", R_FontList_f }, + { "screenshot", R_ScreenShotJPEG_f }, + { "screenshot_png", R_ScreenShotPNG_f }, + { "screenshot_tga", R_ScreenShotTGA_f }, + { "gfxinfo", GfxInfo_f }, + { "gfxmeminfo", GfxMemInfo_f }, + { "r_we", R_WorldEffect_f }, + //{ "imagecacheinfo", RE_RegisterImages_Info_f }, + { "modellist", R_Modellist_f }, + //{ "modelcacheinfo", RE_RegisterModels_Info_f }, + { "vbolist", R_VBOList_f }, + { "capframes", R_CaptureFrameData_f }, +}; + +static const size_t numCommands = ARRAY_LEN( commands ); + + +/* +=============== +R_Register +=============== +*/ +void R_Register( void ) +{ + // + // latched and archived variables + // + r_allowExtensions = ri_Cvar_Get_NoComm( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH, "Allow GL extensions" ); + r_ext_compressed_textures = ri_Cvar_Get_NoComm( "r_ext_compress_textures", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable texture compression" ); + r_ext_compressed_lightmaps = ri.Cvar_Get("r_ext_compress_lightmaps", "0", CVAR_ARCHIVE_ND | CVAR_LATCH); + r_ext_multitexture = ri_Cvar_Get_NoComm( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH, "Unused" ); + r_ext_compiled_vertex_array = ri_Cvar_Get_NoComm( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH, "Unused" ); + r_ext_texture_env_add = ri_Cvar_Get_NoComm( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH, "Unused" ); + r_ext_preferred_tc_method = ri_Cvar_Get_NoComm( "r_ext_preferred_tc_method", "0", CVAR_ARCHIVE | CVAR_LATCH, "Preferred texture compression method" ); + + r_ext_draw_range_elements = ri_Cvar_Get_NoComm( "r_ext_draw_range_elements", "1", CVAR_ARCHIVE | CVAR_LATCH, "Unused" ); + r_ext_multi_draw_arrays = ri_Cvar_Get_NoComm( "r_ext_multi_draw_arrays", "1", CVAR_ARCHIVE | CVAR_LATCH, "Unused" ); + r_ext_texture_float = ri_Cvar_Get_NoComm( "r_ext_texture_float", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable floating-point textures" ); + r_arb_half_float_pixel = ri_Cvar_Get_NoComm( "r_arb_half_float_pixel", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable ARB_half_float GL extension" ); + r_ext_framebuffer_multisample = ri_Cvar_Get_NoComm( "r_ext_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable framebuffer MSAA" ); + r_arb_seamless_cube_map = ri_Cvar_Get_NoComm( "r_arb_seamless_cube_map", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable seamless cube map filtering GL extension" ); + r_arb_vertex_type_2_10_10_10_rev = ri_Cvar_Get_NoComm( "r_arb_vertex_type_2_10_10_10_rev", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable 1010102 UI data type" ); + r_arb_buffer_storage = ri_Cvar_Get_NoComm( "r_arb_buffer_storage", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable buffer storage GL extension" ); + r_ext_texture_filter_anisotropic = ri_Cvar_Get_NoComm( "r_ext_texture_filter_anisotropic", "16", CVAR_ARCHIVE, "Disable/enable anisotropic texture filtering" ); + + r_dynamicGlow = ri_Cvar_Get_NoComm( "r_dynamicGlow", "0", CVAR_ARCHIVE, "" ); + r_dynamicGlowPasses = ri_Cvar_Get_NoComm( "r_dynamicGlowPasses", "5", CVAR_ARCHIVE, "" ); + r_dynamicGlowDelta = ri_Cvar_Get_NoComm( "r_dynamicGlowDelta", "0.8f", CVAR_ARCHIVE, "" ); + r_dynamicGlowIntensity = ri_Cvar_Get_NoComm( "r_dynamicGlowIntensity", "1.13f", CVAR_ARCHIVE, "" ); + r_dynamicGlowSoft = ri_Cvar_Get_NoComm( "r_dynamicGlowSoft", "1", CVAR_ARCHIVE, "" ); + r_dynamicGlowWidth = ri_Cvar_Get_NoComm( "r_dynamicGlowWidth", "320", CVAR_ARCHIVE|CVAR_LATCH, "" ); + r_dynamicGlowHeight = ri_Cvar_Get_NoComm( "r_dynamicGlowHeight", "240", CVAR_ARCHIVE|CVAR_LATCH, "" ); + + r_debugContext = ri_Cvar_Get_NoComm( "r_debugContext", "0", CVAR_LATCH, "" ); + r_debugWeather = ri_Cvar_Get_NoComm( "r_debugWeather", "0", CVAR_ARCHIVE, "" ); + + r_picmip = ri_Cvar_Get_NoComm ("r_picmip", "0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + ri.Cvar_CheckRange( r_picmip, 0, 16, qtrue ); + r_roundImagesDown = ri_Cvar_Get_NoComm ("r_roundImagesDown", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_colorMipLevels = ri_Cvar_Get_NoComm ("r_colorMipLevels", "0", CVAR_LATCH, "" ); + r_detailTextures = ri_Cvar_Get_NoComm( "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_texturebits = ri_Cvar_Get_NoComm( "r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_overBrightBits = ri_Cvar_Get_NoComm ("r_overBrightBits", "0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_simpleMipMaps = ri_Cvar_Get_NoComm( "r_simpleMipMaps", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_vertexLight = ri_Cvar_Get_NoComm( "r_vertexLight", "0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_uiFullScreen = ri_Cvar_Get_NoComm( "r_uifullscreen", "0", 0, ""); + r_subdivisions = ri_Cvar_Get_NoComm ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH, ""); + ri.Cvar_CheckRange( r_subdivisions, 4, 80, qfalse ); + r_stereo = ri_Cvar_Get_NoComm( "r_stereo", "0", CVAR_ARCHIVE | CVAR_LATCH, ""); + r_greyscale = ri_Cvar_Get_NoComm("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH, ""); + ri.Cvar_CheckRange(r_greyscale, 0, 1, qfalse); + + r_externalGLSL = ri_Cvar_Get_NoComm( "r_externalGLSL", "0", CVAR_LATCH, "" ); + + r_hdr = ri_Cvar_Get_NoComm( "r_hdr", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable rendering in HDR" ); + r_floatLightmap = ri_Cvar_Get_NoComm( "r_floatLightmap", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable HDR lightmap support" ); + + r_toneMap = ri_Cvar_Get_NoComm( "r_toneMap", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable tonemapping" ); + r_forceToneMap = ri_Cvar_Get_NoComm( "r_forceToneMap", "0", CVAR_CHEAT, "" ); + r_forceToneMapMin = ri_Cvar_Get_NoComm( "r_forceToneMapMin", "-8.0", CVAR_CHEAT, "" ); + r_forceToneMapAvg = ri_Cvar_Get_NoComm( "r_forceToneMapAvg", "-2.0", CVAR_CHEAT, "" ); + r_forceToneMapMax = ri_Cvar_Get_NoComm( "r_forceToneMapMax", "0.0", CVAR_CHEAT, "" ); + + r_autoExposure = ri_Cvar_Get_NoComm( "r_autoExposure", "1", CVAR_ARCHIVE, "Disable/enable auto exposure" ); + r_forceAutoExposure = ri_Cvar_Get_NoComm( "r_forceAutoExposure", "0", CVAR_CHEAT, "" ); + r_forceAutoExposureMin = ri_Cvar_Get_NoComm( "r_forceAutoExposureMin", "-2.0", CVAR_CHEAT, "" ); + r_forceAutoExposureMax = ri_Cvar_Get_NoComm( "r_forceAutoExposureMax", "2.0", CVAR_CHEAT, "" ); + + r_cameraExposure = ri_Cvar_Get_NoComm( "r_cameraExposure", "0", CVAR_CHEAT, "" ); + + r_depthPrepass = ri_Cvar_Get_NoComm( "r_depthPrepass", "1", CVAR_ARCHIVE, "" ); + r_ssao = ri_Cvar_Get_NoComm( "r_ssao", "0", CVAR_LATCH | CVAR_ARCHIVE, "" ); + + r_normalMapping = ri_Cvar_Get_NoComm( "r_normalMapping", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable normal mapping" ); + r_specularMapping = ri_Cvar_Get_NoComm( "r_specularMapping", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable specular mapping" ); + r_deluxeMapping = ri_Cvar_Get_NoComm( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable reading deluxemaps when compiled with q3map2" ); + r_deluxeSpecular = ri_Cvar_Get_NoComm("r_deluxeSpecular", "1", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable/scale the specular response from deluxemaps"); + r_parallaxMapping = ri_Cvar_Get_NoComm( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable parallax mapping" ); + r_cubeMapping = ri_Cvar_Get_NoComm( "r_cubeMapping", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable cubemapping" ); + r_cubeMappingBounces = ri_Cvar_Get_NoComm("r_cubeMappingBounces", "0", CVAR_ARCHIVE | CVAR_LATCH, "Renders cubemaps multiple times to get reflections in reflections"); + ri.Cvar_CheckRange(r_cubeMappingBounces, 0, 2, qfalse); + r_baseNormalX = ri_Cvar_Get_NoComm( "r_baseNormalX", "1.0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_baseNormalY = ri_Cvar_Get_NoComm( "r_baseNormalY", "1.0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_baseParallax = ri_Cvar_Get_NoComm( "r_baseParallax", "0.05", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_baseSpecular = ri_Cvar_Get_NoComm( "r_baseSpecular", "0.04", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_dlightMode = ri_Cvar_Get_NoComm( "r_dlightMode", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_pshadowDist = ri_Cvar_Get_NoComm( "r_pshadowDist", "128", CVAR_ARCHIVE, "" ); + r_imageUpsample = ri_Cvar_Get_NoComm( "r_imageUpsample", "0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_imageUpsampleMaxSize = ri_Cvar_Get_NoComm( "r_imageUpsampleMaxSize", "1024", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_imageUpsampleType = ri_Cvar_Get_NoComm( "r_imageUpsampleType", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_genNormalMaps = ri_Cvar_Get_NoComm( "r_genNormalMaps", "0", CVAR_ARCHIVE | CVAR_LATCH, "Disable/enable generating normal maps from diffuse maps" ); + + r_forceSun = ri_Cvar_Get_NoComm( "r_forceSun", "0", CVAR_CHEAT, "" ); + r_forceSunMapLightScale = ri_Cvar_Get_NoComm( "r_forceSunMapLightScale", "1.0", CVAR_CHEAT, "" ); + r_forceSunLightScale = ri_Cvar_Get_NoComm( "r_forceSunLightScale", "1.0", CVAR_CHEAT, "" ); + r_forceSunAmbientScale = ri_Cvar_Get_NoComm( "r_forceSunAmbientScale", "0.5", CVAR_CHEAT, "" ); + r_drawSunRays = ri_Cvar_Get_NoComm( "r_drawSunRays", "0", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_sunlightMode = ri_Cvar_Get_NoComm( "r_sunlightMode", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + + r_sunShadows = ri_Cvar_Get_NoComm( "r_sunShadows", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_shadowFilter = ri_Cvar_Get_NoComm( "r_shadowFilter", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_shadowMapSize = ri_Cvar_Get_NoComm( "r_shadowMapSize", "1024", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_shadowCascadeZNear = ri_Cvar_Get_NoComm( "r_shadowCascadeZNear", "4", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_shadowCascadeZFar = ri_Cvar_Get_NoComm( "r_shadowCascadeZFar", "3072", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_shadowCascadeZBias = ri_Cvar_Get_NoComm( "r_shadowCascadeZBias", "-320", CVAR_ARCHIVE | CVAR_LATCH, "" ); + r_ignoreDstAlpha = ri_Cvar_Get_NoComm( "r_ignoreDstAlpha", "1", CVAR_ARCHIVE | CVAR_LATCH, "" ); + + // + // temporary latched variables that can only change over a restart + // + r_fullbright = ri_Cvar_Get_NoComm ("r_fullbright", "0", CVAR_LATCH|CVAR_CHEAT, "" ); + r_mapOverBrightBits = ri_Cvar_Get_NoComm ("r_mapOverBrightBits", "0", CVAR_LATCH, "" ); + r_intensity = ri_Cvar_Get_NoComm ("r_intensity", "1", CVAR_LATCH, "" ); + r_singleShader = ri_Cvar_Get_NoComm ("r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH, "" ); + + // + // archived variables that can change at any time + // + r_lodCurveError = ri_Cvar_Get_NoComm( "r_lodCurveError", "250", CVAR_ARCHIVE|CVAR_CHEAT, "" ); + r_lodbias = ri_Cvar_Get_NoComm( "r_lodbias", "0", CVAR_ARCHIVE, "" ); + r_flares = ri_Cvar_Get_NoComm ("r_flares", "0", CVAR_ARCHIVE, "" ); + r_znear = ri_Cvar_Get_NoComm( "r_znear", "4", CVAR_CHEAT, "" ); + ri.Cvar_CheckRange( r_znear, 0.001f, 200, qfalse ); + r_autolodscalevalue = ri_Cvar_Get_NoComm( "r_autolodscalevalue", "0", CVAR_ROM, "" ); + r_zproj = ri_Cvar_Get_NoComm( "r_zproj", "64", CVAR_ARCHIVE, "" ); + r_stereoSeparation = ri_Cvar_Get_NoComm( "r_stereoSeparation", "64", CVAR_ARCHIVE, "" ); + r_ignoreGLErrors = ri_Cvar_Get_NoComm( "r_ignoreGLErrors", "1", CVAR_ARCHIVE, "" ); + r_fastsky = ri_Cvar_Get_NoComm( "r_fastsky", "0", CVAR_ARCHIVE, "" ); + r_inGameVideo = ri_Cvar_Get_NoComm( "r_inGameVideo", "1", CVAR_ARCHIVE, "" ); + r_drawSun = ri_Cvar_Get_NoComm( "r_drawSun", "0", CVAR_ARCHIVE, "" ); + r_dynamiclight = ri_Cvar_Get_NoComm( "r_dynamiclight", "1", CVAR_ARCHIVE, "" ); + r_finish = ri_Cvar_Get_NoComm ("r_finish", "0", CVAR_ARCHIVE, ""); + r_textureMode = ri_Cvar_Get_NoComm( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE, "" ); + r_markcount = ri_Cvar_Get_NoComm( "r_markcount", "100", CVAR_ARCHIVE, "" ); + r_gamma = ri_Cvar_Get_NoComm( "r_gamma", "1", CVAR_ARCHIVE, "" ); + r_facePlaneCull = ri_Cvar_Get_NoComm ("r_facePlaneCull", "1", CVAR_ARCHIVE, "" ); + + r_ambientScale = ri_Cvar_Get_NoComm( "r_ambientScale", "0.6", CVAR_CHEAT, "" ); + r_directedScale = ri_Cvar_Get_NoComm( "r_directedScale", "1", CVAR_CHEAT, "" ); + + r_anaglyphMode = ri_Cvar_Get_NoComm("r_anaglyphMode", "0", CVAR_ARCHIVE, ""); + r_mergeMultidraws = ri_Cvar_Get_NoComm("r_mergeMultidraws", "1", CVAR_ARCHIVE, ""); + r_mergeLeafSurfaces = ri_Cvar_Get_NoComm("r_mergeLeafSurfaces", "1", CVAR_ARCHIVE, ""); + + // + // temporary variables that can change at any time + // + r_showImages = ri_Cvar_Get_NoComm( "r_showImages", "0", CVAR_TEMP, "" ); + + r_debugLight = ri_Cvar_Get_NoComm( "r_debuglight", "0", CVAR_TEMP, "" ); + r_debugSort = ri_Cvar_Get_NoComm( "r_debugSort", "0", CVAR_CHEAT, "" ); + r_printShaders = ri_Cvar_Get_NoComm( "r_printShaders", "0", 0, "" ); + r_saveFontData = ri_Cvar_Get_NoComm( "r_saveFontData", "0", 0, "" ); + + r_forceParallaxBias = ri_Cvar_Get_NoComm("r_forceParallaxBias", "0", CVAR_TEMP, ""); + ri.Cvar_CheckRange(r_forceParallaxBias, 0.0f, 1.0f, qfalse); + + r_nocurves = ri_Cvar_Get_NoComm ("r_nocurves", "0", CVAR_CHEAT, "" ); + r_drawworld = ri_Cvar_Get_NoComm ("r_drawworld", "1", CVAR_CHEAT, "" ); + r_drawfog = ri_Cvar_Get_NoComm("r_drawfog", "2", CVAR_CHEAT, ""); + r_lightmap = ri_Cvar_Get_NoComm ("r_lightmap", "0", 0, "" ); + r_portalOnly = ri_Cvar_Get_NoComm ("r_portalOnly", "0", CVAR_CHEAT, "" ); + + r_skipBackEnd = ri_Cvar_Get_NoComm ("r_skipBackEnd", "0", CVAR_CHEAT, ""); + + r_measureOverdraw = ri_Cvar_Get_NoComm( "r_measureOverdraw", "0", CVAR_CHEAT, "" ); + r_lodscale = ri_Cvar_Get_NoComm( "r_lodscale", "5", CVAR_CHEAT, "" ); + r_norefresh = ri_Cvar_Get_NoComm ("r_norefresh", "0", CVAR_CHEAT, ""); + r_drawentities = ri_Cvar_Get_NoComm ("r_drawentities", "1", CVAR_CHEAT, "" ); + r_ignore = ri_Cvar_Get_NoComm( "r_ignore", "1", CVAR_CHEAT, "" ); + r_nocull = ri_Cvar_Get_NoComm ("r_nocull", "0", CVAR_CHEAT, ""); + r_novis = ri_Cvar_Get_NoComm ("r_novis", "0", CVAR_CHEAT, ""); + r_showcluster = ri_Cvar_Get_NoComm ("r_showcluster", "0", CVAR_CHEAT, ""); + r_speeds = ri_Cvar_Get_NoComm ("r_speeds", "0", CVAR_CHEAT, ""); + r_verbose = ri_Cvar_Get_NoComm( "r_verbose", "0", CVAR_CHEAT, "" ); + r_logFile = ri_Cvar_Get_NoComm( "r_logFile", "0", CVAR_CHEAT, "" ); + r_debugSurface = ri_Cvar_Get_NoComm ("r_debugSurface", "0", CVAR_CHEAT, ""); + r_nobind = ri_Cvar_Get_NoComm ("r_nobind", "0", CVAR_CHEAT, ""); + r_showtris = ri_Cvar_Get_NoComm ("r_showtris", "0", CVAR_CHEAT, ""); + r_showsky = ri_Cvar_Get_NoComm ("r_showsky", "0", CVAR_CHEAT, ""); + r_shownormals = ri_Cvar_Get_NoComm ("r_shownormals", "0", CVAR_CHEAT, ""); + r_clear = ri_Cvar_Get_NoComm ("r_clear", "0", CVAR_CHEAT, ""); + r_offsetFactor = ri_Cvar_Get_NoComm( "r_offsetfactor", "-1", CVAR_CHEAT, "" ); + r_offsetUnits = ri_Cvar_Get_NoComm( "r_offsetunits", "-2", CVAR_CHEAT, "" ); + + r_shadowOffsetFactor = ri_Cvar_Get_NoComm("r_shadowOffsetFactor", "1.0", CVAR_CHEAT, ""); + r_shadowOffsetUnits = ri_Cvar_Get_NoComm("r_shadowOffsetUnits", "1.0", CVAR_CHEAT, ""); + + r_drawBuffer = ri_Cvar_Get_NoComm( "r_drawBuffer", "GL_BACK", CVAR_CHEAT, "" ); + r_lockpvs = ri_Cvar_Get_NoComm ("r_lockpvs", "0", CVAR_CHEAT, ""); + r_noportals = ri_Cvar_Get_NoComm ("r_noportals", "0", CVAR_CHEAT, ""); + r_shadows = ri_Cvar_Get_NoComm( "cg_shadows", "1", 0, "" ); + + r_marksOnTriangleMeshes = ri_Cvar_Get_NoComm("r_marksOnTriangleMeshes", "0", CVAR_ARCHIVE, ""); + + r_aviMotionJpegQuality = ri_Cvar_Get_NoComm("r_aviMotionJpegQuality", "90", CVAR_ARCHIVE, ""); + r_screenshotJpegQuality = ri_Cvar_Get_NoComm("r_screenshotJpegQuality", "90", CVAR_ARCHIVE, ""); + r_surfaceSprites = ri_Cvar_Get_NoComm("r_surfaceSprites", "1", CVAR_ARCHIVE, ""); + + r_aspectCorrectFonts = ri_Cvar_Get_NoComm( "r_aspectCorrectFonts", "0", CVAR_ARCHIVE, "" ); + r_maxpolys = ri_Cvar_Get_NoComm( "r_maxpolys", XSTRING( DEFAULT_MAX_POLYS ), 0, ""); + r_maxpolyverts = ri_Cvar_Get_NoComm( "r_maxpolyverts", XSTRING( DEFAULT_MAX_POLYVERTS ), 0, "" ); + + /* + Ghoul2 Insert Start + */ +#ifdef _DEBUG + r_noPrecacheGLA = ri_Cvar_Get_NoComm( "r_noPrecacheGLA", "0", CVAR_CHEAT, "" ); +#endif + r_noServerGhoul2 = ri_Cvar_Get_NoComm( "r_noghoul2", "0", CVAR_CHEAT, "" ); + r_Ghoul2AnimSmooth = ri_Cvar_Get_NoComm( "r_ghoul2animsmooth", "0.3", CVAR_TEMP, "" ); + r_Ghoul2UnSqashAfterSmooth = ri_Cvar_Get_NoComm( "r_ghoul2unsqashaftersmooth", "1", CVAR_TEMP, "" ); + broadsword = ri_Cvar_Get_NoComm( "broadsword", "0", CVAR_ARCHIVE, "" ); + broadsword_kickbones = ri_Cvar_Get_NoComm( "broadsword_kickbones", "1", CVAR_TEMP, "" ); + broadsword_kickorigin = ri_Cvar_Get_NoComm( "broadsword_kickorigin", "1", CVAR_TEMP, "" ); + broadsword_dontstopanim = ri_Cvar_Get_NoComm( "broadsword_dontstopanim", "0", CVAR_TEMP, "" ); + broadsword_waitforshot = ri_Cvar_Get_NoComm( "broadsword_waitforshot", "0", CVAR_TEMP, "" ); + broadsword_playflop = ri_Cvar_Get_NoComm( "broadsword_playflop", "1", CVAR_TEMP, "" ); + broadsword_smallbbox = ri_Cvar_Get_NoComm( "broadsword_smallbbox", "0", CVAR_TEMP, "" ); + broadsword_extra1 = ri_Cvar_Get_NoComm( "broadsword_extra1", "0", CVAR_TEMP, "" ); + broadsword_extra2 = ri_Cvar_Get_NoComm( "broadsword_extra2", "0", CVAR_TEMP, "" ); + broadsword_effcorr = ri_Cvar_Get_NoComm( "broadsword_effcorr", "1", CVAR_TEMP, "" ); + broadsword_ragtobase = ri_Cvar_Get_NoComm( "broadsword_ragtobase", "2", CVAR_TEMP, "" ); + broadsword_dircap = ri_Cvar_Get_NoComm( "broadsword_dircap", "64", CVAR_TEMP, "" ); + /* + Ghoul2 Insert End + */ + + se_language = ri_Cvar_Get_NoComm ( "se_language", "english", CVAR_ARCHIVE | CVAR_NORESTART, "" ); +#ifdef JK2_MODE + sp_language = ri.Cvar_Get("sp_language", va("%d", SP_LANGUAGE_ENGLISH), CVAR_ARCHIVE | CVAR_NORESTART); +#endif + // added for SP + // @TODO add all cvars from vanilla + com_buildScript = ri.Cvar_Get("com_buildScript", "0", 0); + sv_mapname = ri.Cvar_Get("mapname", "nomap", CVAR_SERVERINFO | CVAR_ROM); + sv_mapChecksum = ri.Cvar_Get("sv_mapChecksum", "", CVAR_ROM); + r_modelpoolmegs = ri.Cvar_Get("r_modelpoolmegs", "20", CVAR_ARCHIVE); + if (ri.LowPhysicalMemory()) + { + ri.Cvar_Set("r_modelpoolmegs", "0"); + } + r_environmentMapping = ri.Cvar_Get("r_environmentMapping", "1", CVAR_ARCHIVE_ND); + + for ( size_t i = 0; i < numCommands; i++ ) + ri.Cmd_AddCommand( commands[i].cmd, commands[i].func ); +} + +void R_InitQueries(void) +{ + if (r_drawSunRays->integer) + qglGenQueries(ARRAY_LEN(tr.sunFlareQuery), tr.sunFlareQuery); +} + +void R_ShutDownQueries(void) +{ + if (r_drawSunRays->integer) + qglDeleteQueries(ARRAY_LEN(tr.sunFlareQuery), tr.sunFlareQuery); +} + +void RE_SetLightStyle (int style, int color); + +static void R_InitBackEndFrameData() +{ + GLuint timerQueries[MAX_GPU_TIMERS*MAX_FRAMES]; + qglGenQueries(MAX_GPU_TIMERS*MAX_FRAMES, timerQueries); + + GLuint ubos[MAX_FRAMES]; + qglGenBuffers(MAX_FRAMES, ubos); + + for ( int i = 0; i < MAX_FRAMES; i++ ) + { + gpuFrame_t *frame = backEndData->frames + i; + const GLbitfield mapBits = GL_MAP_WRITE_BIT | GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT; + + frame->ubo = ubos[i]; + frame->uboWriteOffset = 0; + frame->uboSize = FRAME_UNIFORM_BUFFER_SIZE; + qglBindBuffer(GL_UNIFORM_BUFFER, frame->ubo); + glState.currentGlobalUBO = frame->ubo; + + // TODO: persistently mapped UBOs + qglBufferData(GL_UNIFORM_BUFFER, FRAME_UNIFORM_BUFFER_SIZE, + nullptr, GL_DYNAMIC_DRAW); + + frame->dynamicVbo = R_CreateVBO(nullptr, FRAME_VERTEX_BUFFER_SIZE, + VBO_USAGE_DYNAMIC); + frame->dynamicVboCommitOffset = 0; + frame->dynamicVboWriteOffset = 0; + + frame->dynamicIbo = R_CreateIBO(nullptr, FRAME_INDEX_BUFFER_SIZE, + VBO_USAGE_DYNAMIC); + frame->dynamicIboCommitOffset = 0; + frame->dynamicIboWriteOffset = 0; + + if ( glRefConfig.immutableBuffers ) + { + R_BindVBO(frame->dynamicVbo); + frame->dynamicVboMemory = qglMapBufferRange(GL_ARRAY_BUFFER, 0, + frame->dynamicVbo->vertexesSize, mapBits); + + R_BindIBO(frame->dynamicIbo); + frame->dynamicIboMemory = qglMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, + frame->dynamicIbo->indexesSize, mapBits); + } + else + { + frame->dynamicVboMemory = nullptr; + frame->dynamicIboMemory = nullptr; + } + + for ( int j = 0; j < MAX_GPU_TIMERS; j++ ) + { + gpuTimer_t *timer = frame->timers + j; + timer->queryName = timerQueries[i*MAX_GPU_TIMERS + j]; + } + } + + backEndData->currentFrame = backEndData->frames; +} + +#ifdef _G2_GORE +static void R_InitGoreVao() +{ + tr.goreVBO = R_CreateVBO( + nullptr, + sizeof(g2GoreVert_t) * MAX_LODS * MAX_GORE_RECORDS * MAX_GORE_VERTS * MAX_FRAMES, + VBO_USAGE_DYNAMIC); + tr.goreVBO->offsets[ATTR_INDEX_POSITION] = offsetof(g2GoreVert_t, position); + tr.goreVBO->offsets[ATTR_INDEX_NORMAL] = offsetof(g2GoreVert_t, normal); + tr.goreVBO->offsets[ATTR_INDEX_TEXCOORD0] = offsetof(g2GoreVert_t, texCoords); + tr.goreVBO->offsets[ATTR_INDEX_BONE_INDEXES] = offsetof(g2GoreVert_t, bonerefs); + tr.goreVBO->offsets[ATTR_INDEX_BONE_WEIGHTS] = offsetof(g2GoreVert_t, weights); + tr.goreVBO->offsets[ATTR_INDEX_TANGENT] = offsetof(g2GoreVert_t, tangents); + + tr.goreVBO->strides[ATTR_INDEX_POSITION] = sizeof(g2GoreVert_t); + tr.goreVBO->strides[ATTR_INDEX_NORMAL] = sizeof(g2GoreVert_t); + tr.goreVBO->strides[ATTR_INDEX_TEXCOORD0] = sizeof(g2GoreVert_t); + tr.goreVBO->strides[ATTR_INDEX_BONE_INDEXES] = sizeof(g2GoreVert_t); + tr.goreVBO->strides[ATTR_INDEX_BONE_WEIGHTS] = sizeof(g2GoreVert_t); + tr.goreVBO->strides[ATTR_INDEX_TANGENT] = sizeof(g2GoreVert_t); + + tr.goreVBO->sizes[ATTR_INDEX_POSITION] = sizeof(vec3_t); + tr.goreVBO->sizes[ATTR_INDEX_NORMAL] = sizeof(uint32_t); + tr.goreVBO->sizes[ATTR_INDEX_TEXCOORD0] = sizeof(vec2_t); + tr.goreVBO->sizes[ATTR_INDEX_BONE_WEIGHTS] = sizeof(byte); + tr.goreVBO->sizes[ATTR_INDEX_BONE_INDEXES] = sizeof(byte); + tr.goreVBO->sizes[ATTR_INDEX_TANGENT] = sizeof(uint32_t); + + tr.goreIBO = R_CreateIBO( + nullptr, + sizeof(glIndex_t) * MAX_LODS * MAX_GORE_RECORDS * MAX_GORE_INDECIES * MAX_FRAMES, + VBO_USAGE_DYNAMIC); + + tr.goreIBOCurrentIndex = 0; + tr.goreVBOCurrentIndex = 0; +} +#endif + +static void R_InitStaticConstants() +{ + const int alignment = glRefConfig.uniformBufferOffsetAlignment - 1; + size_t alignedBlockSize = 0; + + qglBindBuffer(GL_UNIFORM_BUFFER, tr.staticUbo); + qglBufferData( + GL_UNIFORM_BUFFER, + STATIC_UNIFORM_BUFFER_SIZE, + nullptr, + GL_STATIC_DRAW); + + // Setup static 2d camera data + EntityBlock entity2DBlock = {}; + entity2DBlock.fxVolumetricBase = -1.0f; + Matrix16Identity(entity2DBlock.modelMatrix); + tr.entity2DUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, 0, sizeof(entity2DBlock), &entity2DBlock); + alignedBlockSize += (sizeof(EntityBlock) + alignment) & ~alignment; + + // Setup static 2d camera data + CameraBlock a2DCameraBlock = {}; + Matrix16Ortho( + 0.0f, + 640.0f, + 480.0f, + 0.0f, + 0.0f, + 1.0f, + a2DCameraBlock.viewProjectionMatrix); + + tr.camera2DUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, tr.camera2DUboOffset, sizeof(a2DCameraBlock), &a2DCameraBlock); + alignedBlockSize += (sizeof(CameraBlock) + alignment) & ~alignment; + + // Setup static flare entity data + EntityBlock entityFlareBlock = {}; + entityFlareBlock.fxVolumetricBase = -1.0f; + Matrix16Identity(entityFlareBlock.modelMatrix); + + tr.entityFlareUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, tr.entityFlareUboOffset, sizeof(entityFlareBlock), &entityFlareBlock); + alignedBlockSize += (sizeof(EntityBlock) + alignment) & ~alignment; + + // Setup static flare camera data + CameraBlock flareCameraBlock = {}; + Matrix16Ortho( + 0.0f, + glConfig.vidWidth, + glConfig.vidHeight, + 0.0f, + -99999.0f, + 99999.0f, + flareCameraBlock.viewProjectionMatrix); + + tr.cameraFlareUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, tr.cameraFlareUboOffset, sizeof(flareCameraBlock), &flareCameraBlock); + alignedBlockSize += (sizeof(CameraBlock) + alignment) & ~alignment; + + // Setup default light block + LightsBlock lightsBlock = {}; + lightsBlock.numLights = 0; + + tr.defaultLightsUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, tr.defaultLightsUboOffset, sizeof(lightsBlock), &lightsBlock); + alignedBlockSize += (sizeof(LightsBlock) + alignment) & ~alignment; + + // Setup default scene block + SceneBlock sceneBlock = {}; + sceneBlock.globalFogIndex = -1; + sceneBlock.currentTime = 0.1f; + sceneBlock.frameTime = 0.1f; + + tr.defaultSceneUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, tr.defaultSceneUboOffset, sizeof(sceneBlock), &sceneBlock); + alignedBlockSize += (sizeof(SceneBlock) + alignment) & ~alignment; + + // Setup default fogs block + FogsBlock fogsBlock = {}; + fogsBlock.numFogs = 0; + tr.defaultFogsUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, tr.defaultFogsUboOffset, sizeof(fogsBlock), &fogsBlock); + alignedBlockSize += (sizeof(FogsBlock) + alignment) & ~alignment; + + // Setup default shader instance block + ShaderInstanceBlock shaderInstanceBlock = {}; + tr.defaultShaderInstanceUboOffset = alignedBlockSize; + qglBufferSubData( + GL_UNIFORM_BUFFER, tr.defaultShaderInstanceUboOffset, sizeof(shaderInstanceBlock), &shaderInstanceBlock); + alignedBlockSize += (sizeof(ShaderInstanceBlock) + alignment) & ~alignment; + + qglBindBuffer(GL_UNIFORM_BUFFER, NULL); + glState.currentGlobalUBO = -1; + + GL_CheckErrors(); +} + +static void R_ShutdownBackEndFrameData() +{ + if ( !backEndData ) + return; + + for ( int i = 0; i < MAX_FRAMES; i++ ) + { + gpuFrame_t *frame = backEndData->frames + i; + + if (frame->sync) + { + qglDeleteSync(frame->sync); + frame->sync = NULL; + } + + qglDeleteBuffers(1, &frame->ubo); + + if ( glRefConfig.immutableBuffers ) + { + R_BindVBO(frame->dynamicVbo); + R_BindIBO(frame->dynamicIbo); + qglUnmapBuffer(GL_ARRAY_BUFFER); + qglUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + } + + for ( int j = 0; j < MAX_GPU_TIMERS; j++ ) + { + gpuTimer_t *timer = frame->timers + j; + qglDeleteQueries(1, &timer->queryName); + } + } +} + +// need to do this hackery so ghoul2 doesn't crash the game because of ITS hackery... +// +void R_ClearStuffToStopGhoul2CrashingThings(void) +{ + memset(&tr, 0, sizeof(tr)); +} + +/* +=============== +R_Init +=============== +*/ +void R_Init( void ) { + byte *ptr; + int i; + + ri.Printf( PRINT_ALL, "----- R_Init -----\n" ); + + // clear all our internal state + Com_Memset( &tr, 0, sizeof( tr ) ); + Com_Memset( &backEnd, 0, sizeof( backEnd ) ); + Com_Memset( &tess, 0, sizeof( tess ) ); + + + // + // init function tables + // + for ( i = 0; i < FUNCTABLE_SIZE; i++ ) + { + tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ( ( float ) ( FUNCTABLE_SIZE - 1 ) ) ) ); + tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f; + tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; + tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i]; + + if ( i < FUNCTABLE_SIZE / 2 ) + { + if ( i < FUNCTABLE_SIZE / 4 ) + { + tr.triangleTable[i] = ( float ) i / ( FUNCTABLE_SIZE / 4 ); + } + else + { + tr.triangleTable[i] = 1.0f - tr.triangleTable[i-FUNCTABLE_SIZE / 4]; + } + } + else + { + tr.triangleTable[i] = -tr.triangleTable[i-FUNCTABLE_SIZE/2]; + } + } + + R_InitFogTable(); + + R_ImageLoader_Init(); + R_NoiseInit(); + R_Register(); + + max_polys = Q_min( r_maxpolys->integer, DEFAULT_MAX_POLYS ); + max_polyverts = Q_min( r_maxpolyverts->integer, DEFAULT_MAX_POLYVERTS ); + + ptr = (byte*)Hunk_Alloc( + sizeof( *backEndData ) + + sizeof(srfPoly_t) * max_polys + + sizeof(polyVert_t) * max_polyverts + + sizeof(Allocator) + + PER_FRAME_MEMORY_BYTES, + h_low); + + backEndData = (backEndData_t *)ptr; + ptr = (byte *)(backEndData + 1); + + backEndData->polys = (srfPoly_t *)ptr; + ptr += sizeof(*backEndData->polys) * max_polys; + + backEndData->polyVerts = (polyVert_t *)ptr; + ptr += sizeof(*backEndData->polyVerts) * max_polyverts; + + backEndData->perFrameMemory = new(ptr) Allocator(ptr + sizeof(*backEndData->perFrameMemory), PER_FRAME_MEMORY_BYTES); + + R_InitNextFrame(); + + for ( int i = 0; i < MAX_LIGHT_STYLES; i++ ) + { + RE_SetLightStyle (i, -1); + } + + R_InitImagesPool(); + + InitOpenGL(); + + R_InitGPUBuffers(); + + R_InitStaticConstants(); + R_InitBackEndFrameData(); + R_InitImages(); + +#ifdef _G2_GORE + R_InitGoreVao(); +#endif + + FBO_Init(); + + GLSL_LoadGPUShaders(); + + R_InitShaders(qfalse); + + R_InitSkins(); + + R_InitFonts(); + + R_ModelInit(); + + R_InitDecals(); + + R_InitQueries(); + + R_InitWeatherSystem(); + +#if defined(_DEBUG) + GLenum err = qglGetError(); + if ( err != GL_NO_ERROR ) + ri.Printf( PRINT_ALL, "glGetError() = 0x%x\n", err ); +#endif + + RestoreGhoul2InfoArray(); + + // print info + GfxInfo_f(); + ri.Printf( PRINT_ALL, "----- finished R_Init -----\n" ); +} + +/* +=============== +RE_Shutdown +=============== +*/ +void RE_Shutdown( qboolean destroyWindow, qboolean restarting ) { + + ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); + + for ( size_t i = 0; i < numCommands; i++ ) + ri.Cmd_RemoveCommand( commands[i].cmd ); + + // Flush here to make sure all the fences are processed + qglFlush(); + + R_IssuePendingRenderCommands(); + + R_ShutdownBackEndFrameData(); + + R_ShutdownWeatherSystem(); + + R_ShutdownFonts(); + if ( tr.registered ) { + R_ShutDownQueries(); + FBO_Shutdown(); + R_DeleteTextures(); + R_DestroyGPUBuffers(); + GLSL_ShutdownGPUShaders(); + + if ( destroyWindow && restarting ) + { + ri.Z_Free((void *)glConfig.extensions_string); + ri.Z_Free((void *)glConfigExt.originalExtensionString); + + qglDeleteVertexArrays(1, &tr.globalVao); + SaveGhoul2InfoArray(); + } + } + + // shut down platform specific OpenGL stuff + if ( destroyWindow ) { + ri.WIN_Shutdown(); + } + + tr.registered = qfalse; + backEndData = NULL; +} + +/* +============= +RE_EndRegistration + +Touch all images to make sure they are resident +============= +*/ +void RE_EndRegistration( void ) { + R_IssuePendingRenderCommands(); + /* + if (!ri.Sys_LowPhysicalMemory()) { + RB_ShowImages(); + } + */ +} + +// HACK +extern qboolean gG2_GBMNoReconstruct; +extern qboolean gG2_GBMUseSPMethod; +static void G2API_BoltMatrixReconstruction( qboolean reconstruct ) { gG2_GBMNoReconstruct = (qboolean)!reconstruct; } +static void G2API_BoltMatrixSPMethod( qboolean spMethod ) { gG2_GBMUseSPMethod = spMethod; } + +static float GetDistanceCull( void ) { return tr.distanceCull; } + +extern void R_SVModelInit( void ); //tr_model.cpp + +static void GetRealRes( int *w, int *h ) { + *w = glConfig.vidWidth; + *h = glConfig.vidHeight; +} + +// STUBS, REPLACEME +qboolean stub_InitializeWireframeAutomap() { return qtrue; } + +void RE_GetLightStyle(int style, color4ub_t color) +{ + if (style >= MAX_LIGHT_STYLES) + { + Com_Error( ERR_FATAL, "RE_GetLightStyle: %d is out of range", style ); + return; + } + + byteAlias_t *baDest = (byteAlias_t *)&color, *baSource = (byteAlias_t *)&styleColors[style]; + baDest->i = baSource->i; +} + +void RE_SetLightStyle(int style, int color) +{ + if (style >= MAX_LIGHT_STYLES) + { + Com_Error( ERR_FATAL, "RE_SetLightStyle: %d is out of range", style ); + return; + } + + byteAlias_t *ba = (byteAlias_t *)&styleColors[style]; + if ( ba->i != color) { + ba->i = color; + } +} + +void RE_GetBModelVerts(int bmodelIndex, vec3_t *verts, vec3_t normal); + + +void stub_RE_AddWeatherZone ( vec3_t mins, vec3_t maxs ) {} // Intentionally left blank. Rend2 reads the zones manually on bsp load +static void RE_SetRefractionProperties ( float distortionAlpha, float distortionStretch, qboolean distortionPrePost, qboolean distortionNegate ) { } + +void C_LevelLoadBegin(const char *psMapName, ForceReload_e eForceReload, qboolean bAllowScreenDissolve) +{ + static char sPrevMapName[MAX_QPATH]={0}; + bool bDeleteModels = eForceReload == eForceReload_MODELS || eForceReload == eForceReload_ALL; + + if( bDeleteModels ) + CModelCache->DeleteAll(); + else if( ri.Cvar_VariableIntegerValue( "sv_pure" ) ) + CModelCache->DumpNonPure(); + + tr.numBSPModels = 0; + + /* If we're switching to the same level, don't increment current level */ + if (Q_stricmp( psMapName,sPrevMapName )) + { + Q_strncpyz( sPrevMapName, psMapName, sizeof(sPrevMapName) ); + tr.currentLevel++; + } +} + +int C_GetLevel( void ) +{ + return tr.currentLevel; +} + +void C_LevelLoadEnd( void ) +{ +#ifndef REND2_SP + CModelCache->LevelLoadEnd(qfalse); + ri.SND_RegisterAudio_LevelLoadEnd(qfalse); + ri.S_RestartMusic(); +#else + CModelCache->LevelLoadEnd(qfalse); + + ri.SND_RegisterAudio_LevelLoadEnd(qfalse); + + if (gbAllowScreenDissolve) + { + RE_InitDissolve(qfalse); + } + + ri.S_RestartMusic(); + + *(ri.gbAlreadyDoingLoad()) = qfalse; +#endif // REND2_SP +} + +extern void RE_GetModelBounds(refEntity_t *refEnt, vec3_t bounds1, vec3_t bounds2); +extern void G2API_AnimateG2ModelsRag(CGhoul2Info_v &ghoul2, int AcurrentTime, CRagDollUpdateParams *params); +extern qboolean G2API_GetRagBonePos(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t pos, vec3_t entAngles, vec3_t entPos, vec3_t entScale); +extern qboolean G2API_RagEffectorKick(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t velocity); +extern qboolean G2API_RagForceSolve(CGhoul2Info_v &ghoul2, qboolean force); +extern qboolean G2API_SetBoneIKState(CGhoul2Info_v &ghoul2, int time, const char *boneName, int ikState, sharedSetBoneIKStateParams_t *params); +extern qboolean G2API_IKMove(CGhoul2Info_v &ghoul2, int time, sharedIKMoveParams_t *params); +extern qboolean G2API_RagEffectorGoal(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t pos); +extern qboolean G2API_RagPCJGradientSpeed(CGhoul2Info_v &ghoul2, const char *boneName, const float speed); +extern qboolean G2API_RagPCJConstraint(CGhoul2Info_v &ghoul2, const char *boneName, vec3_t min, vec3_t max); +extern void G2API_SetRagDoll(CGhoul2Info_v &ghoul2, CRagDollParams *parms); +#ifdef G2_PERFORMANCE_ANALYSIS +extern void G2Time_ResetTimers(void); +extern void G2Time_ReportTimers(void); +#endif + +#ifdef JK2_MODE +unsigned int AnyLanguage_ReadCharFromString_JK2(char **text, qboolean *pbIsTrailingPunctuation) { + return AnyLanguage_ReadCharFromString(text, pbIsTrailingPunctuation); +} +#endif + +/* +@@@@@@@@@@@@@@@@@@@@@ +GetRefAPI + +@@@@@@@@@@@@@@@@@@@@@ +*/ +extern "C" { +Q_EXPORT refexport_t* QDECL GetRefAPI ( int apiVersion, refimport_t *rimp ) { + static refexport_t re; + + assert( rimp ); + ri = *rimp; + + Com_Memset( &re, 0, sizeof( re ) ); + + if ( apiVersion != REF_API_VERSION ) { + ri.Printf(PRINT_ALL, "Mismatched REF_API_VERSION: expected %i, got %i\n", + REF_API_VERSION, apiVersion ); + return NULL; + } + + // the RE_ functions are Renderer Entry points + + re.Shutdown = RE_Shutdown; + + re.BeginRegistration = RE_BeginRegistration; + re.RegisterModel = RE_RegisterModel; + re.RegisterSkin = RE_RegisterSkin; + re.GetAnimationCFG = RE_GetAnimationCFG; + re.RegisterShader = RE_RegisterShader; + re.RegisterShaderNoMip = RE_RegisterShaderNoMip; + re.LoadWorld = RE_LoadWorldMap; + re.R_LoadImage = R_LoadImage; + + re.RegisterMedia_LevelLoadBegin = C_LevelLoadBegin; + re.RegisterMedia_LevelLoadEnd = C_LevelLoadEnd; + re.RegisterMedia_GetLevel = C_GetLevel; + re.RegisterImages_LevelLoadEnd = C_Images_LevelLoadEnd; + re.RegisterModels_LevelLoadEnd = C_Models_LevelLoadEnd; + + re.SetWorldVisData = RE_SetWorldVisData; + + re.EndRegistration = RE_EndRegistration; + + re.ClearScene = RE_ClearScene; + //re.ClearDecals = RE_ClearDecals; + re.AddRefEntityToScene = RE_AddRefEntityToScene; + //re.AddMiniRefEntityToScene = RE_AddMiniRefEntityToScene; + re.AddPolyToScene = RE_AddPolyToScene; + re.AddLightToScene = RE_AddLightToScene; + //re.AddDecalToScene = RE_AddDecalToScene; + //re.LightForPoint = R_LightForPoint; + re.RenderScene = RE_RenderScene; + //re.AddAdditiveLightToScene = RE_AddAdditiveLightToScene; + re.GetLighting = RE_GetLighting; + + re.SetColor = RE_SetColor; + re.DrawStretchPic = RE_StretchPic; + re.DrawRotatePic = RE_RotatePic; + re.DrawRotatePic2 = RE_RotatePic2; + re.LAGoggles = RE_LAGoggles; + re.Scissor = RE_Scissor; + + re.DrawStretchRaw = RE_StretchRaw; + re.UploadCinematic = RE_UploadCinematic; + + re.BeginFrame = RE_BeginFrame; + re.EndFrame = RE_EndFrame; + + re.ProcessDissolve = RE_ProcessDissolve; + re.InitDissolve = RE_InitDissolve; + re.GetScreenShot = RE_GetScreenShot; + +#ifdef JK2_MODE + re.SaveJPGToBuffer = RE_SaveJPGToBuffer; + re.LoadJPGFromBuffer = LoadJPGFromBuffer; +#endif + re.TempRawImage_ReadFromFile = RE_TempRawImage_ReadFromFile; + re.TempRawImage_CleanUp = RE_TempRawImage_CleanUp; + + re.MarkFragments = R_MarkFragments; + re.LerpTag = R_LerpTag; + re.ModelBounds = R_ModelBounds; + re.GetLightStyle = RE_GetLightStyle; + re.SetLightStyle = RE_SetLightStyle; + re.GetBModelVerts = RE_GetBModelVerts; + re.WorldEffectCommand = RE_WorldEffectCommand; + re.GetModelBounds = RE_GetModelBounds; + + re.SVModelInit = R_SVModelInit; + + re.RegisterFont = RE_RegisterFont; + re.Font_HeightPixels = RE_Font_HeightPixels; + re.Font_StrLenPixels = RE_Font_StrLenPixels; + re.Font_DrawString = RE_Font_DrawString; + re.Font_StrLenChars = RE_Font_StrLenChars; + re.Language_IsAsian = Language_IsAsian; + re.Language_UsesSpaces = Language_UsesSpaces; + re.AnyLanguage_ReadCharFromString = AnyLanguage_ReadCharFromString; +#ifdef JK2_MODE + re.AnyLanguage_ReadCharFromString2 = AnyLanguage_ReadCharFromString_JK2; +#endif + + re.R_InitWorldEffects = stub_R_InitWorldEffects; + re.R_ClearStuffToStopGhoul2CrashingThings = R_ClearStuffToStopGhoul2CrashingThings; + re.R_inPVS = R_inPVS; + + re.tr_distortionAlpha = stub_get_tr_distortionAlpha; + re.tr_distortionStretch = stub_get_tr_distortionStretch; + re.tr_distortionPrePost = stub_get_tr_distortionPrePost; + re.tr_distortionNegate = stub_get_tr_distortionNegate; + + //re.InitializeWireframeAutomap = stub_InitializeWireframeAutomap; // MP + re.GetWindVector = R_GetWindVector; + re.GetWindGusting = R_GetWindGusting; + re.IsOutside = R_IsOutside; + re.IsOutsideCausingPain = R_IsOutsideCausingPain; + re.GetChanceOfSaberFizz = R_GetChanceOfSaberFizz; + re.IsShaking = R_IsShaking; + re.AddWeatherZone = stub_RE_AddWeatherZone; + re.SetTempGlobalFogColor = stub_R_SetTempGlobalFogColor; + + re.SetRangedFog = RE_SetRangedFog; + //re.GetDistanceCull = GetDistanceCull; // MP + //re.GetRealRes = GetRealRes; // MP + + re.TheGhoul2InfoArray = TheGhoul2InfoArray; + //re.GetEntityToken = R_GetEntityToken; //MP only, but need this for cubemaps... + + re.G2API_AddBolt = G2API_AddBolt; + re.G2API_AddBoltSurfNum = G2API_AddBoltSurfNum; + re.G2API_AddSurface = G2API_AddSurface; + re.G2API_AnimateG2Models = G2API_AnimateG2ModelsRag; + re.G2API_AttachEnt = G2API_AttachEnt; + re.G2API_AttachG2Model = G2API_AttachG2Model; + re.G2API_CollisionDetect = G2API_CollisionDetect; + re.G2API_CleanGhoul2Models = G2API_CleanGhoul2Models; + re.G2API_CopyGhoul2Instance = G2API_CopyGhoul2Instance; + re.G2API_DetachEnt = G2API_DetachEnt; + re.G2API_DetachG2Model = G2API_DetachG2Model; + re.G2API_GetAnimFileName = G2API_GetAnimFileName; + re.G2API_GetAnimFileNameIndex = G2API_GetAnimFileNameIndex; + re.G2API_GetAnimFileInternalNameIndex = G2API_GetAnimFileInternalNameIndex; + re.G2API_GetAnimIndex = G2API_GetAnimIndex; + re.G2API_GetAnimRange = G2API_GetAnimRange; + re.G2API_GetAnimRangeIndex = G2API_GetAnimRangeIndex; + re.G2API_GetBoneAnim = G2API_GetBoneAnim; + re.G2API_GetBoneAnimIndex = G2API_GetBoneAnimIndex; + re.G2API_GetBoneIndex = G2API_GetBoneIndex; + re.G2API_GetBoltMatrix = G2API_GetBoltMatrix; + re.G2API_GetGhoul2ModelFlags = G2API_GetGhoul2ModelFlags; + re.G2API_GetGLAName = G2API_GetGLAName; + re.G2API_GetParentSurface = G2API_GetParentSurface; + re.G2API_GetRagBonePos = G2API_GetRagBonePos; + re.G2API_GetSurfaceIndex = G2API_GetSurfaceIndex; + re.G2API_GetSurfaceName = G2API_GetSurfaceName; + re.G2API_GetSurfaceRenderStatus = G2API_GetSurfaceRenderStatus; + re.G2API_GetTime = G2API_GetTime; + re.G2API_GiveMeVectorFromMatrix = G2API_GiveMeVectorFromMatrix; + re.G2API_HaveWeGhoul2Models = G2API_HaveWeGhoul2Models; + re.G2API_IKMove = G2API_IKMove; + re.G2API_InitGhoul2Model = G2API_InitGhoul2Model; + re.G2API_IsPaused = G2API_IsPaused; + re.G2API_ListBones = G2API_ListBones; + re.G2API_ListSurfaces = G2API_ListSurfaces; + re.G2API_LoadGhoul2Models = G2API_LoadGhoul2Models; + re.G2API_LoadSaveCodeDestructGhoul2Info = G2API_LoadSaveCodeDestructGhoul2Info; + re.G2API_PauseBoneAnim = G2API_PauseBoneAnim; + re.G2API_PauseBoneAnimIndex = G2API_PauseBoneAnimIndex; + re.G2API_PrecacheGhoul2Model = G2API_PrecacheGhoul2Model; + re.G2API_RagEffectorGoal = G2API_RagEffectorGoal; + re.G2API_RagEffectorKick = G2API_RagEffectorKick; + re.G2API_RagForceSolve = G2API_RagForceSolve; + re.G2API_RagPCJConstraint = G2API_RagPCJConstraint; + re.G2API_RagPCJGradientSpeed = G2API_RagPCJGradientSpeed; + re.G2API_RemoveBolt = G2API_RemoveBolt; + re.G2API_RemoveBone = G2API_RemoveBone; + re.G2API_RemoveGhoul2Model = G2API_RemoveGhoul2Model; + re.G2API_RemoveSurface = G2API_RemoveSurface; + re.G2API_SaveGhoul2Models = G2API_SaveGhoul2Models; + re.G2API_SetAnimIndex = G2API_SetAnimIndex; + re.G2API_SetBoneAnim = G2API_SetBoneAnim; + re.G2API_SetBoneAnimIndex = G2API_SetBoneAnimIndex; + re.G2API_SetBoneAngles = G2API_SetBoneAngles; + re.G2API_SetBoneAnglesIndex = G2API_SetBoneAnglesIndex; + re.G2API_SetBoneAnglesMatrix = G2API_SetBoneAnglesMatrix; + re.G2API_SetBoneAnglesMatrixIndex = G2API_SetBoneAnglesMatrixIndex; + re.G2API_SetBoneIKState = G2API_SetBoneIKState; + re.G2API_SetGhoul2ModelFlags = G2API_SetGhoul2ModelFlags; + re.G2API_SetGhoul2ModelIndexes = G2API_SetGhoul2ModelIndexes; + re.G2API_SetLodBias = G2API_SetLodBias; + //G2EX(SetModelIndexes); + re.G2API_SetNewOrigin = G2API_SetNewOrigin; + re.G2API_SetRagDoll = G2API_SetRagDoll; + re.G2API_SetRootSurface = G2API_SetRootSurface; + re.G2API_SetShader = G2API_SetShader; + re.G2API_SetSkin = G2API_SetSkin; + re.G2API_SetSurfaceOnOff = G2API_SetSurfaceOnOff; + re.G2API_SetTime = G2API_SetTime; + re.G2API_StopBoneAnim = G2API_StopBoneAnim; + re.G2API_StopBoneAnimIndex = G2API_StopBoneAnimIndex; + re.G2API_StopBoneAngles = G2API_StopBoneAngles; + re.G2API_StopBoneAnglesIndex = G2API_StopBoneAnglesIndex; +#ifdef _G2_GORE + re.G2API_AddSkinGore = G2API_AddSkinGore; + re.G2API_ClearSkinGore = G2API_ClearSkinGore; + //re.G2API_GetNumGoreMarks = G2API_GetNumGoreMarks; // MP +#endif + +#ifdef G2_PERFORMANCE_ANALYSIS + re.G2Time_ReportTimers = G2Time_ReportTimers; + re.G2Time_ResetTimers = G2Time_ResetTimers; +#endif + + //Swap_Init(); + + return &re; +} +} diff --git a/codemp/rd-common/tr_types.h b/codemp/rd-common/tr_types.h index 4ec970178c..ba0a35e805 100644 --- a/codemp/rd-common/tr_types.h +++ b/codemp/rd-common/tr_types.h @@ -283,20 +283,6 @@ Ghoul2 Insert Start #include "rd-common/mdx_format.h" #include "qcommon/qfiles.h" -// skins allow models to be retextured without modifying the model file -//this is a mock copy, renderers may have their own implementation. -// try not to break the ghoul2 code which is very implicit :/ -typedef struct _skinSurface_s { - char name[MAX_QPATH]; - void *shader; -} _skinSurface_t; - -typedef struct skin_s { - char name[MAX_QPATH]; // game path, including extension - int numSurfaces; - _skinSurface_t *surfaces[128]; -} skin_t; - /* Ghoul2 Insert End */ diff --git a/codemp/rd-dedicated/tr_local.h b/codemp/rd-dedicated/tr_local.h index aa558b35da..0768dc65cb 100644 --- a/codemp/rd-dedicated/tr_local.h +++ b/codemp/rd-dedicated/tr_local.h @@ -537,11 +537,18 @@ typedef struct trRefdef_s { //================================================================================= // skins allow models to be retextured without modifying the model file -typedef struct skinSurface_s { +typedef struct { char name[MAX_QPATH]; shader_t *shader; } skinSurface_t; +typedef struct skin_s { + char name[MAX_QPATH]; // game path, including extension + int numSurfaces; + skinSurface_t *surfaces[128]; +} skin_t; + + typedef struct fog_s { int originalBrushNumber; vec3_t bounds[2]; diff --git a/codemp/rd-dedicated/tr_skin.cpp b/codemp/rd-dedicated/tr_skin.cpp index ad179d0f35..a25589efed 100644 --- a/codemp/rd-dedicated/tr_skin.cpp +++ b/codemp/rd-dedicated/tr_skin.cpp @@ -146,7 +146,7 @@ qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin) break; } surf = (skinSurface_t *) Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[skin->numSurfaces] = (_skinSurface_t *)surf; + skin->surfaces[skin->numSurfaces] = (skinSurface_t *)surf; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); @@ -378,7 +378,7 @@ void R_InitSkins( void ) { skin = tr.skins[0] = (struct skin_s *)ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = (_skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0] = (skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); skin->surfaces[0]->shader = tr.defaultShader; } diff --git a/codemp/rd-rend2/CMakeLists.txt b/codemp/rd-rend2/CMakeLists.txt index 786cf254fc..073c92b5b4 100644 --- a/codemp/rd-rend2/CMakeLists.txt +++ b/codemp/rd-rend2/CMakeLists.txt @@ -5,78 +5,79 @@ endif(NOT InOpenJK) set(MPRend2IncludeDirectories "${SharedDir}" + "${SharedDir}/rd-rend2" "${MPDir}" "${MPDir}/rd-rend2" "${GSLIncludeDirectory}" ) set(MPRend2Files - "${MPDir}/rd-rend2/iqm.h" - "${MPDir}/rd-rend2/glext.h" - "${MPDir}/rd-rend2/qgl.h" - "${MPDir}/rd-rend2/stb_image.h" + "${SharedDir}/rd-rend2/iqm.h" + "${SharedDir}/rd-rend2/glext.h" + "${SharedDir}/rd-rend2/qgl.h" + "${SharedDir}/rd-rend2/stb_image.h" "${MPDir}/rd-rend2/G2_API.cpp" "${MPDir}/rd-rend2/G2_bolts.cpp" "${MPDir}/rd-rend2/G2_bones.cpp" - "${MPDir}/rd-rend2/G2_gore_r2.cpp" - "${MPDir}/rd-rend2/G2_gore_r2.h" + "${SharedDir}/rd-rend2/G2_gore_r2.cpp" + "${SharedDir}/rd-rend2/G2_gore_r2.h" "${MPDir}/rd-rend2/G2_misc.cpp" "${MPDir}/rd-rend2/G2_surfaces.cpp" - "${MPDir}/rd-rend2/tr_allocator.cpp" - "${MPDir}/rd-rend2/tr_allocator.h" - "${MPDir}/rd-rend2/tr_animation.cpp" - "${MPDir}/rd-rend2/tr_backend.cpp" - "${MPDir}/rd-rend2/tr_bsp.cpp" - "${MPDir}/rd-rend2/tr_cache.cpp" - "${MPDir}/rd-rend2/tr_cache.h" - "${MPDir}/rd-rend2/tr_cmds.cpp" - "${MPDir}/rd-rend2/tr_curve.cpp" - "${MPDir}/rd-rend2/tr_decals.cpp" - "${MPDir}/rd-rend2/tr_extensions.cpp" - "${MPDir}/rd-rend2/tr_extramath.cpp" - "${MPDir}/rd-rend2/tr_extramath.h" - "${MPDir}/rd-rend2/tr_extratypes.h" - "${MPDir}/rd-rend2/tr_fbo.cpp" - "${MPDir}/rd-rend2/tr_fbo.h" - "${MPDir}/rd-rend2/tr_flares.cpp" - "${MPDir}/rd-rend2/tr_ghoul2.cpp" - "${MPDir}/rd-rend2/tr_glsl.cpp" - "${MPDir}/rd-rend2/tr_glsl_parse.cpp" - "${MPDir}/rd-rend2/tr_image.cpp" - "${MPDir}/rd-rend2/tr_image_stb.cpp" + "${SharedDir}/rd-rend2/tr_allocator.cpp" + "${SharedDir}/rd-rend2/tr_allocator.h" + "${SharedDir}/rd-rend2/tr_animation.cpp" + "${SharedDir}/rd-rend2/tr_backend.cpp" + "${SharedDir}/rd-rend2/tr_bsp.cpp" + "${SharedDir}/rd-rend2/tr_cache.cpp" + "${SharedDir}/rd-rend2/tr_cache.h" + "${SharedDir}/rd-rend2/tr_cmds.cpp" + "${SharedDir}/rd-rend2/tr_curve.cpp" + "${SharedDir}/rd-rend2/tr_decals.cpp" + "${SharedDir}/rd-rend2/tr_extensions.cpp" + "${SharedDir}/rd-rend2/tr_extramath.cpp" + "${SharedDir}/rd-rend2/tr_extramath.h" + "${SharedDir}/rd-rend2/tr_extratypes.h" + "${SharedDir}/rd-rend2/tr_fbo.cpp" + "${SharedDir}/rd-rend2/tr_fbo.h" + "${SharedDir}/rd-rend2/tr_flares.cpp" + "${SharedDir}/rd-rend2/tr_ghoul2.cpp" + "${SharedDir}/rd-rend2/tr_glsl.cpp" + "${SharedDir}/rd-rend2/tr_glsl_parse.cpp" + "${SharedDir}/rd-rend2/tr_image.cpp" + "${SharedDir}/rd-rend2/tr_image_stb.cpp" "${MPDir}/rd-rend2/tr_init.cpp" - "${MPDir}/rd-rend2/tr_light.cpp" - "${MPDir}/rd-rend2/tr_local.h" - "${MPDir}/rd-rend2/tr_main.cpp" - "${MPDir}/rd-rend2/tr_marks.cpp" - "${MPDir}/rd-rend2/tr_mesh.cpp" - "${MPDir}/rd-rend2/tr_model.cpp" - "${MPDir}/rd-rend2/tr_model_iqm.cpp" - "${MPDir}/rd-rend2/tr_postprocess.cpp" - "${MPDir}/rd-rend2/tr_postprocess.h" - "${MPDir}/rd-rend2/tr_scene.cpp" - "${MPDir}/rd-rend2/tr_shade.cpp" - "${MPDir}/rd-rend2/tr_shade_calc.cpp" - "${MPDir}/rd-rend2/tr_shader.cpp" - "${MPDir}/rd-rend2/tr_shadows.cpp" - "${MPDir}/rd-rend2/tr_skin.cpp" - "${MPDir}/rd-rend2/tr_sky.cpp" - "${MPDir}/rd-rend2/tr_subs.cpp" - "${MPDir}/rd-rend2/tr_surface.cpp" - "${MPDir}/rd-rend2/tr_tangentspace.cpp" - "${MPDir}/rd-rend2/tr_vbo.cpp" - "${MPDir}/rd-rend2/tr_world.cpp" - "${MPDir}/rd-rend2/tr_weather.cpp" - "${MPDir}/rd-rend2/tr_weather.h") + "${SharedDir}/rd-rend2/tr_light.cpp" + "${SharedDir}/rd-rend2/tr_local.h" + "${SharedDir}/rd-rend2/tr_main.cpp" + "${SharedDir}/rd-rend2/tr_marks.cpp" + "${SharedDir}/rd-rend2/tr_mesh.cpp" + "${SharedDir}/rd-rend2/tr_model.cpp" + "${SharedDir}/rd-rend2/tr_model_iqm.cpp" + "${SharedDir}/rd-rend2/tr_postprocess.cpp" + "${SharedDir}/rd-rend2/tr_postprocess.h" + "${SharedDir}/rd-rend2/tr_scene.cpp" + "${SharedDir}/rd-rend2/tr_shade.cpp" + "${SharedDir}/rd-rend2/tr_shade_calc.cpp" + "${SharedDir}/rd-rend2/tr_shader.cpp" + "${SharedDir}/rd-rend2/tr_shadows.cpp" + "${SharedDir}/rd-rend2/tr_skin.cpp" + "${SharedDir}/rd-rend2/tr_sky.cpp" + "${SharedDir}/rd-rend2/tr_subs.cpp" + "${SharedDir}/rd-rend2/tr_surface.cpp" + "${SharedDir}/rd-rend2/tr_tangentspace.cpp" + "${SharedDir}/rd-rend2/tr_vbo.cpp" + "${SharedDir}/rd-rend2/tr_world.cpp" + "${SharedDir}/rd-rend2/tr_weather.cpp" + "${SharedDir}/rd-rend2/tr_weather.h") source_group("renderer" FILES ${MPRend2Files}) -file(GLOB MPRend2GLSLFiles "${MPDir}/rd-rend2/glsl/*.glsl") +file(GLOB MPRend2GLSLFiles "${SharedDir}/rd-rend2/glsl/*.glsl") source_group("renderer\\glsl" FILES ${MPRend2GLSLFiles}) set(MPRend2Files ${MPRend2Files} ${MPRend2GLSLFiles}) set(MPRend2MikktFiles - "${MPDir}/rd-rend2/MikkTSpace/mikktspace.h" - "${MPDir}/rd-rend2/MikkTSpace/mikktspace.c") + "${SharedDir}/rd-rend2/MikkTSpace/mikktspace.h" + "${SharedDir}/rd-rend2/MikkTSpace/mikktspace.c") source_group("MikktSpace" FILES ${MPRend2MikktFiles}) set(MPRend2Files ${MPRend2Files} ${MPRend2MikktFiles}) @@ -184,10 +185,10 @@ target_include_directories(${MPRend2} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # GLSL shader file generator add_executable(compact_glsl - ${MPDir}/rd-rend2/glsl/compact.cpp - ${MPDir}/rd-rend2/tr_allocator.cpp - ${MPDir}/rd-rend2/tr_allocator.h - ${MPDir}/rd-rend2/tr_glsl_parse.cpp) + ${SharedDir}/rd-rend2/glsl/compact.cpp + ${SharedDir}/rd-rend2/tr_allocator.cpp + ${SharedDir}/rd-rend2/tr_allocator.h + ${SharedDir}/rd-rend2/tr_glsl_parse.cpp) target_compile_definitions(compact_glsl PRIVATE "GLSL_BUILDTOOL" "NOMINMAX") if (NOT WIN32 AND NOT APPLE) target_compile_definitions(compact_glsl PRIVATE "ARCH_STRING=\"${Architecture}\"") diff --git a/codemp/rd-vanilla/tr_local.h b/codemp/rd-vanilla/tr_local.h index 67717946be..4e419f457f 100644 --- a/codemp/rd-vanilla/tr_local.h +++ b/codemp/rd-vanilla/tr_local.h @@ -570,11 +570,18 @@ typedef struct trRefdef_s { //================================================================================= // skins allow models to be retextured without modifying the model file -typedef struct skinSurface_s { +typedef struct { char name[MAX_QPATH]; shader_t *shader; } skinSurface_t; +typedef struct skin_s { + char name[MAX_QPATH]; // game path, including extension + int numSurfaces; + skinSurface_t *surfaces[128]; +} skin_t; + + typedef struct fog_s { int originalBrushNumber; vec3_t bounds[2]; diff --git a/codemp/rd-vanilla/tr_skin.cpp b/codemp/rd-vanilla/tr_skin.cpp index 57ff06618c..ce43a02d33 100644 --- a/codemp/rd-vanilla/tr_skin.cpp +++ b/codemp/rd-vanilla/tr_skin.cpp @@ -160,7 +160,7 @@ qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin) break; } surf = (skinSurface_t *) Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[skin->numSurfaces] = (_skinSurface_t *)surf; + skin->surfaces[skin->numSurfaces] = (skinSurface_t *)surf; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); @@ -393,7 +393,7 @@ void R_InitSkins( void ) { skin = tr.skins[0] = (struct skin_s *)ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = (_skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0] = (skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); skin->surfaces[0]->shader = tr.defaultShader; } diff --git a/codemp/rd-vulkan/tr_local.h b/codemp/rd-vulkan/tr_local.h index 92743ee6b7..00538e7cf4 100644 --- a/codemp/rd-vulkan/tr_local.h +++ b/codemp/rd-vulkan/tr_local.h @@ -701,11 +701,19 @@ typedef struct trRefdef_s { //================================================================================= // skins allow models to be retextured without modifying the model file -typedef struct skinSurface_s { +typedef struct { char name[MAX_QPATH]; shader_t *shader; } skinSurface_t; +typedef struct skin_s { + char name[MAX_QPATH]; // game path, including extension + int numSurfaces; + skinSurface_t *surfaces[128]; +} skin_t; + + + typedef struct fog_s { int originalBrushNumber; vec3_t bounds[2]; diff --git a/codemp/rd-vulkan/tr_skin.cpp b/codemp/rd-vulkan/tr_skin.cpp index 9971163831..bb10231955 100644 --- a/codemp/rd-vulkan/tr_skin.cpp +++ b/codemp/rd-vulkan/tr_skin.cpp @@ -162,7 +162,7 @@ qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin) break; } surf = (skinSurface_t *) Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[skin->numSurfaces] = (_skinSurface_t *)surf; + skin->surfaces[skin->numSurfaces] = (skinSurface_t *)surf; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); @@ -392,7 +392,7 @@ void R_InitSkins( void ) { skin = tr.skins[0] = (struct skin_s *)ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = (_skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0] = (skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); skin->surfaces[0]->shader = tr.defaultShader; } diff --git a/codemp/rd-rend2/G2_gore_r2.cpp b/shared/rd-rend2/G2_gore_r2.cpp similarity index 100% rename from codemp/rd-rend2/G2_gore_r2.cpp rename to shared/rd-rend2/G2_gore_r2.cpp diff --git a/codemp/rd-rend2/G2_gore_r2.h b/shared/rd-rend2/G2_gore_r2.h similarity index 100% rename from codemp/rd-rend2/G2_gore_r2.h rename to shared/rd-rend2/G2_gore_r2.h diff --git a/codemp/rd-rend2/MikkTSpace/README.md b/shared/rd-rend2/MikkTSpace/README.md similarity index 100% rename from codemp/rd-rend2/MikkTSpace/README.md rename to shared/rd-rend2/MikkTSpace/README.md diff --git a/codemp/rd-rend2/MikkTSpace/mikktspace.c b/shared/rd-rend2/MikkTSpace/mikktspace.c similarity index 100% rename from codemp/rd-rend2/MikkTSpace/mikktspace.c rename to shared/rd-rend2/MikkTSpace/mikktspace.c diff --git a/codemp/rd-rend2/MikkTSpace/mikktspace.h b/shared/rd-rend2/MikkTSpace/mikktspace.h similarity index 100% rename from codemp/rd-rend2/MikkTSpace/mikktspace.h rename to shared/rd-rend2/MikkTSpace/mikktspace.h diff --git a/codemp/rd-rend2/glext.h b/shared/rd-rend2/glext.h similarity index 100% rename from codemp/rd-rend2/glext.h rename to shared/rd-rend2/glext.h diff --git a/codemp/rd-rend2/glsl/bokeh.glsl b/shared/rd-rend2/glsl/bokeh.glsl similarity index 100% rename from codemp/rd-rend2/glsl/bokeh.glsl rename to shared/rd-rend2/glsl/bokeh.glsl diff --git a/codemp/rd-rend2/glsl/calclevels4x.glsl b/shared/rd-rend2/glsl/calclevels4x.glsl similarity index 100% rename from codemp/rd-rend2/glsl/calclevels4x.glsl rename to shared/rd-rend2/glsl/calclevels4x.glsl diff --git a/codemp/rd-rend2/glsl/compact.cpp b/shared/rd-rend2/glsl/compact.cpp similarity index 100% rename from codemp/rd-rend2/glsl/compact.cpp rename to shared/rd-rend2/glsl/compact.cpp diff --git a/codemp/rd-rend2/glsl/depthblur.glsl b/shared/rd-rend2/glsl/depthblur.glsl similarity index 100% rename from codemp/rd-rend2/glsl/depthblur.glsl rename to shared/rd-rend2/glsl/depthblur.glsl diff --git a/codemp/rd-rend2/glsl/dglow_downsample.glsl b/shared/rd-rend2/glsl/dglow_downsample.glsl similarity index 100% rename from codemp/rd-rend2/glsl/dglow_downsample.glsl rename to shared/rd-rend2/glsl/dglow_downsample.glsl diff --git a/codemp/rd-rend2/glsl/dglow_upsample.glsl b/shared/rd-rend2/glsl/dglow_upsample.glsl similarity index 100% rename from codemp/rd-rend2/glsl/dglow_upsample.glsl rename to shared/rd-rend2/glsl/dglow_upsample.glsl diff --git a/codemp/rd-rend2/glsl/down4x.glsl b/shared/rd-rend2/glsl/down4x.glsl similarity index 100% rename from codemp/rd-rend2/glsl/down4x.glsl rename to shared/rd-rend2/glsl/down4x.glsl diff --git a/codemp/rd-rend2/glsl/fogpass.glsl b/shared/rd-rend2/glsl/fogpass.glsl similarity index 99% rename from codemp/rd-rend2/glsl/fogpass.glsl rename to shared/rd-rend2/glsl/fogpass.glsl index 57dcf0ac87..633feaae54 100644 --- a/codemp/rd-rend2/glsl/fogpass.glsl +++ b/shared/rd-rend2/glsl/fogpass.glsl @@ -263,7 +263,7 @@ struct Fog layout(std140) uniform Fogs { int u_NumFogs; - Fog u_Fogs[16]; + Fog u_Fogs[MAX_GPU_FOGS]; }; layout(std140) uniform Camera diff --git a/codemp/rd-rend2/glsl/gaussian_blur.glsl b/shared/rd-rend2/glsl/gaussian_blur.glsl similarity index 100% rename from codemp/rd-rend2/glsl/gaussian_blur.glsl rename to shared/rd-rend2/glsl/gaussian_blur.glsl diff --git a/codemp/rd-rend2/glsl/generic.glsl b/shared/rd-rend2/glsl/generic.glsl similarity index 96% rename from codemp/rd-rend2/glsl/generic.glsl rename to shared/rd-rend2/glsl/generic.glsl index f249657e89..4958bc9ffc 100644 --- a/codemp/rd-rend2/glsl/generic.glsl +++ b/shared/rd-rend2/glsl/generic.glsl @@ -292,6 +292,23 @@ vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3 } break; + case TCGEN_ENVIRONMENT_MAPPED_SP: + { + vec3 viewer = normalize(u_LocalViewOrigin - position); + vec2 ref = reflect(viewer, normal).xy; + tex.s = ref.x * -0.5; + tex.t = ref.y * -0.5; + } + break; + + case TCGEN_ENVIRONMENT_MAPPED_SP_FP: + { + vec2 ref = reflect(u_ModelLightDir.xyz, normal).xy; + tex.s = ref.x * -0.5 + 0.5 * u_ModelLightDir.x; + tex.t = ref.y * -0.5 + 0.5 * u_ModelLightDir.y; + } + break; + case TCGEN_VECTOR: { tex = vec2(dot(position, TCGenVector0), dot(position, TCGenVector1)); @@ -472,7 +489,7 @@ struct Fog layout(std140) uniform Fogs { int u_NumFogs; - Fog u_Fogs[16]; + Fog u_Fogs[MAX_GPU_FOGS]; }; uniform vec4 u_FogColorMask; diff --git a/codemp/rd-rend2/glsl/lightall.glsl b/shared/rd-rend2/glsl/lightall.glsl similarity index 98% rename from codemp/rd-rend2/glsl/lightall.glsl rename to shared/rd-rend2/glsl/lightall.glsl index 759bbc8144..685c40ef39 100644 --- a/codemp/rd-rend2/glsl/lightall.glsl +++ b/shared/rd-rend2/glsl/lightall.glsl @@ -166,6 +166,23 @@ vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3 } break; + case TCGEN_ENVIRONMENT_MAPPED_SP: + { + vec3 viewer = normalize(u_LocalViewOrigin - position); + vec2 ref = reflect(viewer, normal).xy; + tex.s = ref.x * -0.5; + tex.t = ref.y * -0.5; + } + break; + + case TCGEN_ENVIRONMENT_MAPPED_SP_FP: + { + vec2 ref = reflect(u_ModelLightDir.xyz, normal).xy; + tex.s = ref.x * -0.5 + 0.5 * u_ModelLightDir.x; + tex.t = ref.y * -0.5 + 0.5 * u_ModelLightDir.y; + } + break; + case TCGEN_VECTOR: { tex = vec2(dot(position, TCGenVector0), dot(position, TCGenVector1)); diff --git a/codemp/rd-rend2/glsl/prefilterEnvMap.glsl b/shared/rd-rend2/glsl/prefilterEnvMap.glsl similarity index 100% rename from codemp/rd-rend2/glsl/prefilterEnvMap.glsl rename to shared/rd-rend2/glsl/prefilterEnvMap.glsl diff --git a/codemp/rd-rend2/glsl/pshadow.glsl b/shared/rd-rend2/glsl/pshadow.glsl similarity index 100% rename from codemp/rd-rend2/glsl/pshadow.glsl rename to shared/rd-rend2/glsl/pshadow.glsl diff --git a/codemp/rd-rend2/glsl/refraction.glsl b/shared/rd-rend2/glsl/refraction.glsl similarity index 96% rename from codemp/rd-rend2/glsl/refraction.glsl rename to shared/rd-rend2/glsl/refraction.glsl index dfee37f149..7c78cdf187 100644 --- a/codemp/rd-rend2/glsl/refraction.glsl +++ b/shared/rd-rend2/glsl/refraction.glsl @@ -236,6 +236,20 @@ vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3 tex.s = ref.x * -0.5 + 0.5; tex.t = ref.y * 0.5 + 0.5; } + else if (TCGen == TCGEN_ENVIRONMENT_MAPPED_SP) + { + vec3 viewer = normalize(u_LocalViewOrigin - position); + vec2 ref = reflect(viewer, normal).xy; + tex.s = ref.x * -0.5; + tex.t = ref.y * -0.5; + } + + else if (TCGen == TCGEN_ENVIRONMENT_MAPPED_SP_FP) + { + vec2 ref = reflect(u_ModelLightDir.xyz, normal).xy; + tex.s = ref.x * -0.5 + 0.5 * u_ModelLightDir.x; + tex.t = ref.y * -0.5 + 0.5 * u_ModelLightDir.y; + } else if (TCGen == TCGEN_VECTOR) { tex = vec2(dot(position, TCGenVector0), dot(position, TCGenVector1)); diff --git a/codemp/rd-rend2/glsl/shadowvolume.glsl b/shared/rd-rend2/glsl/shadowvolume.glsl similarity index 100% rename from codemp/rd-rend2/glsl/shadowvolume.glsl rename to shared/rd-rend2/glsl/shadowvolume.glsl diff --git a/codemp/rd-rend2/glsl/ssao.glsl b/shared/rd-rend2/glsl/ssao.glsl similarity index 100% rename from codemp/rd-rend2/glsl/ssao.glsl rename to shared/rd-rend2/glsl/ssao.glsl diff --git a/codemp/rd-rend2/glsl/surface_sprites.glsl b/shared/rd-rend2/glsl/surface_sprites.glsl similarity index 99% rename from codemp/rd-rend2/glsl/surface_sprites.glsl rename to shared/rd-rend2/glsl/surface_sprites.glsl index 338f7a421e..2b0ad2406f 100644 --- a/codemp/rd-rend2/glsl/surface_sprites.glsl +++ b/shared/rd-rend2/glsl/surface_sprites.glsl @@ -176,7 +176,7 @@ struct Fog layout(std140) uniform Fogs { int u_NumFogs; - Fog u_Fogs[16]; + Fog u_Fogs[MAX_GPU_FOGS]; }; uniform int u_FogIndex; diff --git a/codemp/rd-rend2/glsl/texturecolor.glsl b/shared/rd-rend2/glsl/texturecolor.glsl similarity index 100% rename from codemp/rd-rend2/glsl/texturecolor.glsl rename to shared/rd-rend2/glsl/texturecolor.glsl diff --git a/codemp/rd-rend2/glsl/tonemap.glsl b/shared/rd-rend2/glsl/tonemap.glsl similarity index 100% rename from codemp/rd-rend2/glsl/tonemap.glsl rename to shared/rd-rend2/glsl/tonemap.glsl diff --git a/codemp/rd-rend2/glsl/weather.glsl b/shared/rd-rend2/glsl/weather.glsl similarity index 97% rename from codemp/rd-rend2/glsl/weather.glsl rename to shared/rd-rend2/glsl/weather.glsl index 0c6eef6372..64280b15aa 100644 --- a/codemp/rd-rend2/glsl/weather.glsl +++ b/shared/rd-rend2/glsl/weather.glsl @@ -18,9 +18,10 @@ void main() attr_Position.z, 1.0); var_Velocity = attr_Color; + var_Velocity.z = min(-0.00001, var_Velocity.z); vec4 velocitiyOffset = u_ViewInfo.y * vec4(-var_Velocity.xy/var_Velocity.z, var_Velocity.z, 0.0); - velocitiyOffset.xyz = mix(vec3(0.0), velocitiyOffset.xyz, float(var_Velocity.z != 0.0)); + velocitiyOffset.xyz = mix(vec3(0.0), velocitiyOffset.xyz, float(attr_Color.z != 0.0)); var_Velocity.z *= u_ViewInfo.z; vec4 depthPosition = u_ShadowMvp * (gl_Position + velocitiyOffset); diff --git a/codemp/rd-rend2/glsl/weatherUpdate.glsl b/shared/rd-rend2/glsl/weatherUpdate.glsl similarity index 100% rename from codemp/rd-rend2/glsl/weatherUpdate.glsl rename to shared/rd-rend2/glsl/weatherUpdate.glsl diff --git a/codemp/rd-rend2/iqm.h b/shared/rd-rend2/iqm.h similarity index 100% rename from codemp/rd-rend2/iqm.h rename to shared/rd-rend2/iqm.h diff --git a/codemp/rd-rend2/json.h b/shared/rd-rend2/json.h similarity index 100% rename from codemp/rd-rend2/json.h rename to shared/rd-rend2/json.h diff --git a/codemp/rd-rend2/qgl.h b/shared/rd-rend2/qgl.h similarity index 100% rename from codemp/rd-rend2/qgl.h rename to shared/rd-rend2/qgl.h diff --git a/codemp/rd-rend2/stb_image.h b/shared/rd-rend2/stb_image.h similarity index 100% rename from codemp/rd-rend2/stb_image.h rename to shared/rd-rend2/stb_image.h diff --git a/codemp/rd-rend2/tr_allocator.cpp b/shared/rd-rend2/tr_allocator.cpp similarity index 96% rename from codemp/rd-rend2/tr_allocator.cpp rename to shared/rd-rend2/tr_allocator.cpp index 4da0228c8c..d1e555d596 100644 --- a/codemp/rd-rend2/tr_allocator.cpp +++ b/shared/rd-rend2/tr_allocator.cpp @@ -21,7 +21,7 @@ Allocator::Allocator( size_t memorySize, size_t alignment ) #if defined(GLSL_BUILDTOOL) , unalignedBase(malloc(memorySize)) #else - , unalignedBase(Z_Malloc(memorySize, TAG_SHADERTEXT)) + , unalignedBase(R_Malloc(memorySize, TAG_SHADERTEXT)) #endif , alignedBase(PADP(unalignedBase, alignment)) , mark(alignedBase) diff --git a/codemp/rd-rend2/tr_allocator.h b/shared/rd-rend2/tr_allocator.h similarity index 100% rename from codemp/rd-rend2/tr_allocator.h rename to shared/rd-rend2/tr_allocator.h diff --git a/codemp/rd-rend2/tr_animation.cpp b/shared/rd-rend2/tr_animation.cpp similarity index 86% rename from codemp/rd-rend2/tr_animation.cpp rename to shared/rd-rend2/tr_animation.cpp index b1ca5e95f8..63af468f08 100644 --- a/codemp/rd-rend2/tr_animation.cpp +++ b/shared/rd-rend2/tr_animation.cpp @@ -137,6 +137,7 @@ R_MDRComputeFogNum int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) { int i, j; + float frameRadius; fog_t *fog; mdrFrame_t *mdrFrame; vec3_t localOrigin; @@ -151,22 +152,56 @@ int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) { // FIXME: non-normalized axis issues mdrFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); VectorAdd( ent->e.origin, mdrFrame->localOrigin, localOrigin ); - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + frameRadius = mdrFrame->radius; +#ifndef REND2_SP + for (i = 1; i < tr.world->numfogs; i++) { fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - mdrFrame->radius >= fog->bounds[1][j] ) { + for (j = 0; j < 3; j++) { + if (localOrigin[j] - frameRadius >= fog->bounds[1][j]) { break; } - if ( localOrigin[j] + mdrFrame->radius <= fog->bounds[0][j] ) { + if (localOrigin[j] + frameRadius <= fog->bounds[0][j]) { break; } } - if ( j == 3 ) { + if (j == 3) { return i; } } - return 0; +#else + int partialFog = 0; + for (i = 1; i < tr.world->numfogs; i++) { + fog = &tr.world->fogs[i]; + if (localOrigin[0] - frameRadius >= fog->bounds[0][0] + && localOrigin[0] + frameRadius <= fog->bounds[1][0] + && localOrigin[1] - frameRadius >= fog->bounds[0][1] + && localOrigin[1] + frameRadius <= fog->bounds[1][1] + && localOrigin[2] - frameRadius >= fog->bounds[0][2] + && localOrigin[2] + frameRadius <= fog->bounds[1][2]) + {//totally inside it + return i; + break; + } + if ((localOrigin[0] - frameRadius >= fog->bounds[0][0] && localOrigin[1] - frameRadius >= fog->bounds[0][1] && localOrigin[2] - frameRadius >= fog->bounds[0][2] && + localOrigin[0] - frameRadius <= fog->bounds[1][0] && localOrigin[1] - frameRadius <= fog->bounds[1][1] && localOrigin[2] - frameRadius <= fog->bounds[1][2]) || + (localOrigin[0] + frameRadius >= fog->bounds[0][0] && localOrigin[1] + frameRadius >= fog->bounds[0][1] && localOrigin[2] + frameRadius >= fog->bounds[0][2] && + localOrigin[0] + frameRadius <= fog->bounds[1][0] && localOrigin[1] + frameRadius <= fog->bounds[1][1] && localOrigin[2] + frameRadius <= fog->bounds[1][2])) + {//partially inside it + //if (tr.refdef.fogIndex == i || R_FogParmsMatch(tr.refdef.fogIndex, i)) + //{//take new one only if it's the same one that the viewpoint is in + // return i; + // break; + //} + //else + if (!partialFog) + {//first partialFog + partialFog = i; + } + } + } + return partialFog; +#endif } diff --git a/codemp/rd-rend2/tr_backend.cpp b/shared/rd-rend2/tr_backend.cpp similarity index 97% rename from codemp/rd-rend2/tr_backend.cpp rename to shared/rd-rend2/tr_backend.cpp index 768c2c4204..f934cad2f2 100644 --- a/codemp/rd-rend2/tr_backend.cpp +++ b/shared/rd-rend2/tr_backend.cpp @@ -592,7 +592,11 @@ void RB_BeginDrawingView (void) { #endif } - if (tr.refdef.rdflags & RDF_AUTOMAP || (!(backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) + if ( +#ifndef REND2_SP + tr.refdef.rdflags & RDF_AUTOMAP || +#endif + (!(backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) { if (tr.world && tr.world->globalFog) { @@ -923,6 +927,16 @@ SamplerBindingsWriter& SamplerBindingsWriter::AddAnimatedImage( textureBundle_t { int index; +#ifdef REND2_SP + if ((r_fullbright->integer + || tr.refdef.doLAGoggles + || tr.refdef.doFullbright) + && bundle->isLightmap) + { + return AddStaticImage(tr.whiteImage, unit); + } +#endif + if ( bundle->isVideoMap ) { SamplerBinding *binding = &scratch[count]; @@ -1926,6 +1940,36 @@ static const void *RB_RotatePic2 ( const void *data ) return (const void *)(cmd + 1); } +#ifdef REND2_SP +/* +============= +RB_ScissorPic +============= +*/ +const void *RB_Scissor(const void *data) +{ + const scissorCommand_t *cmd; + + cmd = (const scissorCommand_t *)data; + + if (!backEnd.projection2D) + { + RB_SetGL2D(); + } + + if (cmd->x >= 0) + { + qglScissor(cmd->x, (glConfig.vidHeight - cmd->y - cmd->h), cmd->w, cmd->h); + } + else + { + qglScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight); + } + + return (const void *)(cmd + 1); +} +#endif + /* ============= RB_PrefilterEnvMap @@ -2263,6 +2307,12 @@ static void RB_UpdateFogsConstants(gpuFrame_t *frame) fogsBlock.numFogs = tr.world->numfogs - 1; // Don't reserve fog 0 as 'null' } + if (fogsBlock.numFogs > MAX_GPU_FOGS) + { + ri.Printf(PRINT_DEVELOPER, "Too many fogs in current map. Increase MAX_GPU_FOGS\n"); + fogsBlock.numFogs = MAX_GPU_FOGS; + } + for (int i = 0; i < fogsBlock.numFogs; ++i) { const fog_t *fog = tr.world->fogs + i + 1; @@ -2273,7 +2323,25 @@ static void RB_UpdateFogsConstants(gpuFrame_t *frame) fogData->depthToOpaque = sqrtf(-logf(1.0f / 255.0f)) / fog->parms.depthForOpaque; fogData->hasPlane = fog->hasSurface; } +#ifdef REND2_SP + if (tr.refdef.doLAGoggles && fogsBlock.numFogs+1 <= MAX_GPU_FOGS) + { + FogsBlock::Fog *fogData = fogsBlock.fogs + fogsBlock.numFogs; + + vec4_t color = { + 0.75f, + 0.42f + Q_flrand(0.0f, 1.0f) * 0.025f, + 0.07f, + 1.0f + }; + const float depthForOpaque = sqrtf(-logf(1.0f / 255.0f)) / 700.f; + VectorCopy4(color, fogData->color); + fogData->depthToOpaque = depthForOpaque; + fogData->hasPlane = 0; + fogsBlock.numFogs++; + } +#endif tr.fogsUboOffset = RB_AppendConstantsData( frame, &fogsBlock, sizeof(fogsBlock)); } @@ -2720,7 +2788,7 @@ static const void *RB_SwapBuffers( const void *data ) { long sum = 0; unsigned char *stencilReadback; - stencilReadback = (unsigned char *)ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight ); + stencilReadback = (unsigned char *)Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight ); qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) { @@ -2728,7 +2796,7 @@ static const void *RB_SwapBuffers( const void *data ) { } backEnd.pc.c_overDraw += sum; - ri.Hunk_FreeTempMemory( stencilReadback ); + Hunk_FreeTempMemory( stencilReadback ); } if (!backEnd.framePostProcessed) @@ -3063,6 +3131,11 @@ void RB_ExecuteRenderCommands( const void *data ) { case RC_ROTATE_PIC2: data = RB_RotatePic2( data ); break; +#ifdef REND2_SP + case RC_SCISSOR: + data = RB_Scissor(data); + break; +#endif case RC_DRAW_SURFS: data = RB_DrawSurfs( data ); break; diff --git a/codemp/rd-rend2/tr_bsp.cpp b/shared/rd-rend2/tr_bsp.cpp similarity index 96% rename from codemp/rd-rend2/tr_bsp.cpp rename to shared/rd-rend2/tr_bsp.cpp index 0df88ddf5c..13de1064ca 100644 --- a/codemp/rd-rend2/tr_bsp.cpp +++ b/shared/rd-rend2/tr_bsp.cpp @@ -236,7 +236,7 @@ static void R_LoadLightmaps( world_t *worldData, lump_t *l, lump_t *surfs ) { if (externalLightmap != NULL) { tr.worldInternalLightmapping = qfalse; - ri.Hunk_FreeTempMemory(externalLightmap); + Hunk_FreeTempMemory(externalLightmap); } } @@ -299,11 +299,11 @@ static void R_LoadLightmaps( world_t *worldData, lump_t *l, lump_t *surfs ) { tr.numLightmaps = numLightmaps; } - tr.lightmaps = (image_t **)ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); + tr.lightmaps = (image_t **)Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); if (tr.worldDeluxeMapping) { - tr.deluxemaps = (image_t **)ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); + tr.deluxemaps = (image_t **)Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); } if (hdr_capable) @@ -631,7 +631,7 @@ static void R_LoadLightmaps( world_t *worldData, lump_t *l, lump_t *surfs ) { if (!tr.deluxemaps) { - tr.deluxemaps = (image_t **)ri.Hunk_Alloc(tr.numLightmaps * sizeof(image_t *), h_low); + tr.deluxemaps = (image_t **)Hunk_Alloc(tr.numLightmaps * sizeof(image_t *), h_low); if (tr.worldInternalLightmapping) { tr.deluxemaps[lightmapnum] = R_CreateImage( @@ -761,7 +761,7 @@ static void R_LoadVisibility( world_t *worldData, lump_t *l ) { byte *buf; len = (worldData->numClusters + 63) & ~63; - worldData->novis = (byte *)ri.Hunk_Alloc(len, h_low); + worldData->novis = (byte *)Hunk_Alloc(len, h_low); Com_Memset(worldData->novis, 0xff, len); len = l->filelen; @@ -780,7 +780,7 @@ static void R_LoadVisibility( world_t *worldData, lump_t *l ) { } else { byte *dest; - dest = (byte *)ri.Hunk_Alloc( len - 8, h_low ); + dest = (byte *)Hunk_Alloc( len - 8, h_low ); Com_Memcpy( dest, buf + 8, len - 8 ); worldData->vis = dest; } @@ -864,15 +864,15 @@ static void ParseFace( const world_t *worldData, dsurface_t *ds, drawVert_t *ver numVerts = LittleLong(ds->numVerts); numIndexes = LittleLong(ds->numIndexes); - //cv = ri.Hunk_Alloc(sizeof(*cv), h_low); + //cv = Hunk_Alloc(sizeof(*cv), h_low); cv = (srfBspSurface_t *)surf->data; cv->surfaceType = SF_FACE; cv->numIndexes = numIndexes; - cv->indexes = (glIndex_t *)ri.Hunk_Alloc(numIndexes * sizeof(cv->indexes[0]), h_low); + cv->indexes = (glIndex_t *)Hunk_Alloc(numIndexes * sizeof(cv->indexes[0]), h_low); cv->numVerts = numVerts; - cv->verts = (srfVert_t *)ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); + cv->verts = (srfVert_t *)Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); // copy vertexes surf->cullinfo.type = CULLINFO_PLANE | CULLINFO_BOX; @@ -1141,15 +1141,15 @@ static void ParseTriSurf( const world_t *worldData, dsurface_t *ds, drawVert_t * numVerts = LittleLong(ds->numVerts); numIndexes = LittleLong(ds->numIndexes); - //cv = ri.Hunk_Alloc(sizeof(*cv), h_low); + //cv = Hunk_Alloc(sizeof(*cv), h_low); cv = (srfBspSurface_t *)surf->data; cv->surfaceType = SF_TRIANGLES; cv->numIndexes = numIndexes; - cv->indexes = (glIndex_t *)ri.Hunk_Alloc(numIndexes * sizeof(cv->indexes[0]), h_low); + cv->indexes = (glIndex_t *)Hunk_Alloc(numIndexes * sizeof(cv->indexes[0]), h_low); cv->numVerts = numVerts; - cv->verts = (srfVert_t *)ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); + cv->verts = (srfVert_t *)Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); surf->data = (surfaceType_t *) cv; @@ -1274,7 +1274,7 @@ static void ParseFlare( const world_t *worldData, dsurface_t *ds, drawVert_t *ve surf->shader = tr.defaultShader; } - //flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); + //flare = Hunk_Alloc( sizeof( *flare ), h_low ); flare = (srfFlare_t *)surf->data; flare->surfaceType = SF_FLARE; @@ -1973,21 +1973,21 @@ void R_MovePatchSurfacesToHunk( world_t *worldData ) { continue; // size = sizeof(*grid); - hunkgrid = (srfBspSurface_t *)ri.Hunk_Alloc(size, h_low); + hunkgrid = (srfBspSurface_t *)Hunk_Alloc(size, h_low); Com_Memcpy(hunkgrid, grid, size); - hunkgrid->widthLodError = (float *)ri.Hunk_Alloc( grid->width * 4, h_low ); + hunkgrid->widthLodError = (float *)Hunk_Alloc( grid->width * 4, h_low ); Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 ); - hunkgrid->heightLodError = (float *)ri.Hunk_Alloc( grid->height * 4, h_low ); + hunkgrid->heightLodError = (float *)Hunk_Alloc( grid->height * 4, h_low ); Com_Memcpy( hunkgrid->heightLodError, grid->heightLodError, grid->height * 4 ); hunkgrid->numIndexes = grid->numIndexes; - hunkgrid->indexes = (glIndex_t *)ri.Hunk_Alloc(grid->numIndexes * sizeof(glIndex_t), h_low); + hunkgrid->indexes = (glIndex_t *)Hunk_Alloc(grid->numIndexes * sizeof(glIndex_t), h_low); Com_Memcpy(hunkgrid->indexes, grid->indexes, grid->numIndexes * sizeof(glIndex_t)); hunkgrid->numVerts = grid->numVerts; - hunkgrid->verts = (srfVert_t *)ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); + hunkgrid->verts = (srfVert_t *)Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); Com_Memcpy(hunkgrid->verts, grid->verts, grid->numVerts * sizeof(srfVert_t)); R_FreeSurfaceGridMesh( grid ); @@ -2174,8 +2174,8 @@ static void R_CreateWorldVBOs( world_t *worldData ) ri.Printf(PRINT_ALL, "...calculating world VBO %d ( %i verts %i tris )\n", k, numVerts, numIndexes / 3); // create arrays - verts = (packedVertex_t *)ri.Hunk_AllocateTempMemory(numVerts * sizeof(packedVertex_t)); - indexes = (glIndex_t *)ri.Hunk_AllocateTempMemory(numIndexes * sizeof(glIndex_t)); + verts = (packedVertex_t *)Hunk_AllocateTempMemory(numVerts * sizeof(packedVertex_t)); + indexes = (glIndex_t *)Hunk_AllocateTempMemory(numIndexes * sizeof(glIndex_t)); // set up indices and copy vertices numVerts = 0; @@ -2275,8 +2275,8 @@ static void R_CreateWorldVBOs( world_t *worldData ) bspSurf->ibo = ibo; } - ri.Hunk_FreeTempMemory(indexes); - ri.Hunk_FreeTempMemory(verts); + Hunk_FreeTempMemory(indexes); + Hunk_FreeTempMemory(verts); k++; } @@ -2319,13 +2319,13 @@ static void R_LoadSurfaces( world_t *worldData, lump_t *surfs, lump_t *verts, lu if ( indexLump->filelen % sizeof(*indexes)) ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",worldData->name); - out = (msurface_t *)ri.Hunk_Alloc ( count * sizeof(*out), h_low ); + out = (msurface_t *)Hunk_Alloc ( count * sizeof(*out), h_low ); worldData->surfaces = out; worldData->numsurfaces = count; - worldData->surfacesViewCount = (int *)ri.Hunk_Alloc ( count * sizeof(*worldData->surfacesViewCount), h_low ); - worldData->surfacesDlightBits = (int *)ri.Hunk_Alloc ( count * sizeof(*worldData->surfacesDlightBits), h_low ); - worldData->surfacesPshadowBits = (int *)ri.Hunk_Alloc ( count * sizeof(*worldData->surfacesPshadowBits), h_low ); + worldData->surfacesViewCount = (int *)Hunk_Alloc ( count * sizeof(*worldData->surfacesViewCount), h_low ); + worldData->surfacesDlightBits = (int *)Hunk_Alloc ( count * sizeof(*worldData->surfacesDlightBits), h_low ); + worldData->surfacesPshadowBits = (int *)Hunk_Alloc ( count * sizeof(*worldData->surfacesPshadowBits), h_low ); // load hdr vertex colors if (r_hdr->integer) @@ -2372,13 +2372,13 @@ static void R_LoadSurfaces( world_t *worldData, lump_t *surfs, lump_t *verts, lu // FIXME: do this break; case MST_TRIANGLE_SOUP: - out->data = (surfaceType_t *)ri.Hunk_Alloc( sizeof(srfBspSurface_t), h_low); + out->data = (surfaceType_t *)Hunk_Alloc( sizeof(srfBspSurface_t), h_low); break; case MST_PLANAR: - out->data = (surfaceType_t *)ri.Hunk_Alloc( sizeof(srfBspSurface_t), h_low); + out->data = (surfaceType_t *)Hunk_Alloc( sizeof(srfBspSurface_t), h_low); break; case MST_FLARE: - out->data = (surfaceType_t *)ri.Hunk_Alloc( sizeof(srfFlare_t), h_low); + out->data = (surfaceType_t *)Hunk_Alloc( sizeof(srfFlare_t), h_low); break; default: break; @@ -2459,7 +2459,7 @@ static void R_LoadSubmodels( world_t *worldData, int worldIndex, lump_t *l ) { count = l->filelen / sizeof(*in); worldData->numBModels = count; - worldData->bmodels = out = (bmodel_t *)ri.Hunk_Alloc( count * sizeof(*out), h_low ); + worldData->bmodels = out = (bmodel_t *)Hunk_Alloc( count * sizeof(*out), h_low ); for ( i=0 ; ifilelen / sizeof(dnode_t); numLeafs = leafLump->filelen / sizeof(dleaf_t); - out = (mnode_t *)ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low); + out = (mnode_t *)Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low); worldData->nodes = out; worldData->numnodes = numNodes + numLeafs; @@ -2608,7 +2608,7 @@ static void R_LoadShaders( world_t *worldData, lump_t *l ) { if (l->filelen % sizeof(*in)) ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",worldData->name); count = l->filelen / sizeof(*in); - out = (dshader_t *)ri.Hunk_Alloc ( count*sizeof(*out), h_low ); + out = (dshader_t *)Hunk_Alloc ( count*sizeof(*out), h_low ); worldData->shaders = out; worldData->numShaders = count; @@ -2637,7 +2637,7 @@ static void R_LoadMarksurfaces (world_t *worldData, lump_t *l) if (l->filelen % sizeof(*in)) ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",worldData->name); count = l->filelen / sizeof(*in); - out = (int *)ri.Hunk_Alloc ( count*sizeof(*out), h_low); + out = (int *)Hunk_Alloc ( count*sizeof(*out), h_low); worldData->marksurfaces = out; worldData->nummarksurfaces = count; @@ -2666,7 +2666,7 @@ static void R_LoadPlanes( world_t *worldData, lump_t *l ) { if (l->filelen % sizeof(*in)) ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",worldData->name); count = l->filelen / sizeof(*in); - out = (cplane_t *)ri.Hunk_Alloc ( count*2*sizeof(*out), h_low); + out = (cplane_t *)Hunk_Alloc ( count*2*sizeof(*out), h_low); worldData->planes = out; worldData->numplanes = count; @@ -2713,7 +2713,7 @@ static void R_LoadFogs( world_t *worldData, lump_t *l, lump_t *brushesLump, lump // create fog strucutres for them worldData->numfogs = count + 1; - worldData->fogs = (fog_t *)ri.Hunk_Alloc ( worldData->numfogs*sizeof(*out), h_low); + worldData->fogs = (fog_t *)Hunk_Alloc ( worldData->numfogs*sizeof(*out), h_low); worldData->globalFog = nullptr; worldData->globalFogIndex = -1; out = worldData->fogs + 1; @@ -2847,7 +2847,7 @@ void R_LoadLightGrid( world_t *worldData, lump_t *l ) { int numGridDataElements = l->filelen / sizeof(*worldData->lightGridData); - worldData->lightGridData = (mgrid_t *)ri.Hunk_Alloc( l->filelen, h_low ); + worldData->lightGridData = (mgrid_t *)Hunk_Alloc( l->filelen, h_low ); Com_Memcpy( worldData->lightGridData, (void *)(fileBase + l->fileofs), l->filelen ); // deal with overbright bits @@ -2883,7 +2883,7 @@ void R_LoadLightGrid( world_t *worldData, lump_t *l ) { ri.Error(ERR_DROP, "Bad size for %s (%i, expected %i)!", filename, size, (int)(sizeof(float)) * 6 * worldData->lightGridBounds[0] * worldData->lightGridBounds[1] * worldData->lightGridBounds[2]); } - worldData->hdrLightGrid = (float *)ri.Hunk_Alloc(size, h_low); + worldData->hdrLightGrid = (float *)Hunk_Alloc(size, h_low); for (i = 0; i < worldData->lightGridBounds[0] * worldData->lightGridBounds[1] * worldData->lightGridBounds[2]; i++) { @@ -2944,10 +2944,13 @@ void R_LoadEntities( world_t *worldData, lump_t *l ) { p = (char *)(fileBase + l->fileofs); // store for reference by the cgame - w->entityString = (char *)ri.Hunk_Alloc( l->filelen + 1, h_low ); + w->entityString = (char *)Hunk_Alloc( l->filelen + 1, h_low ); strcpy( w->entityString, p ); w->entityParsePoint = w->entityString; +#ifdef REND2_SP + COM_BeginParseSession(); +#endif token = COM_ParseExt( &p, qtrue ); if (!*token || *token != '{') { return; @@ -3015,6 +3018,9 @@ void R_LoadEntities( world_t *worldData, lump_t *l ) { continue; } } +#ifdef REND2_SP + COM_EndParseSession(); +#endif } /* @@ -3163,7 +3169,7 @@ void R_LoadEnvironmentJson(const char *baseName) } tr.numCubemaps = JSON_ArrayGetIndex(environmentArrayJson, bufferEnd, NULL, 0); - tr.cubemaps = (cubemap_t *)ri.Hunk_Alloc(tr.numCubemaps * sizeof(*tr.cubemaps), h_low); + tr.cubemaps = (cubemap_t *)Hunk_Alloc(tr.numCubemaps * sizeof(*tr.cubemaps), h_low); for (i = 0; i < tr.numCubemaps; i++) { @@ -3215,7 +3221,7 @@ void R_LoadCubemapEntities(const char *cubemapEntityName) return; tr.numCubemaps = numCubemaps; - tr.cubemaps = (cubemap_t *)ri.Hunk_Alloc(tr.numCubemaps * sizeof(*tr.cubemaps), h_low); + tr.cubemaps = (cubemap_t *)Hunk_Alloc(tr.numCubemaps * sizeof(*tr.cubemaps), h_low); numCubemaps = 0; while(R_ParseSpawnVars(spawnVarChars, sizeof(spawnVarChars), &numSpawnVars, spawnVars)) @@ -3533,23 +3539,23 @@ static void R_MergeLeafSurfaces(world_t *worldData) // Allocate merged surfaces worldData->mergedSurfaces = - (msurface_t *)ri.Hunk_Alloc( + (msurface_t *)Hunk_Alloc( sizeof(*worldData->mergedSurfaces) * numMergedSurfaces, h_low); worldData->mergedSurfacesViewCount = - (int *)ri.Hunk_Alloc( + (int *)Hunk_Alloc( sizeof(*worldData->mergedSurfacesViewCount) * numMergedSurfaces, h_low); worldData->mergedSurfacesDlightBits = - (int *)ri.Hunk_Alloc( + (int *)Hunk_Alloc( sizeof(*worldData->mergedSurfacesDlightBits) * numMergedSurfaces, h_low); worldData->mergedSurfacesPshadowBits = - (int *)ri.Hunk_Alloc( + (int *)Hunk_Alloc( sizeof(*worldData->mergedSurfacesPshadowBits) * numMergedSurfaces, h_low); worldData->numMergedSurfaces = numMergedSurfaces; // view surfaces are like mark surfaces, except negative ones represent merged surfaces // -1 represents 0, -2 represents 1, and so on worldData->viewSurfaces = - (int *)ri.Hunk_Alloc( + (int *)Hunk_Alloc( sizeof(*worldData->viewSurfaces) * worldData->nummarksurfaces, h_low); // copy view surfaces into mark surfaces @@ -3612,7 +3618,7 @@ static void R_MergeLeafSurfaces(world_t *worldData) } // create ibo - ibo = tr.ibos[tr.numIBOs++] = (IBO_t*)ri.Hunk_Alloc(sizeof(*ibo), h_low); + ibo = tr.ibos[tr.numIBOs++] = (IBO_t*)Hunk_Alloc(sizeof(*ibo), h_low); memset(ibo, 0, sizeof(*ibo)); numIboIndexes = 0; @@ -3644,7 +3650,7 @@ static void R_MergeLeafSurfaces(world_t *worldData) break; } - vboSurf = (srfBspSurface_t *)ri.Hunk_Alloc(sizeof(*vboSurf), h_low); + vboSurf = (srfBspSurface_t *)Hunk_Alloc(sizeof(*vboSurf), h_low); memset(vboSurf, 0, sizeof(*vboSurf)); vboSurf->surfaceType = SF_VBO_MESH; @@ -3969,7 +3975,7 @@ static void R_GenerateSurfaceSprites( out->alphaTestType = stage->alphaTestType; out->numAttributes = 4; - out->attributes = (vertexAttribute_t *)ri.Hunk_Alloc( + out->attributes = (vertexAttribute_t *)Hunk_Alloc( sizeof(vertexAttribute_t) * out->numAttributes, h_low); out->attributes[0].vbo = out->vbo; @@ -4122,7 +4128,7 @@ static void R_GenerateSurfaceSprites( const world_t *world, int worldIndex ) continue; surf->numSurfaceSprites = shader->numSurfaceSpriteStages; - surf->surfaceSprites = (srfSprites_t *)ri.Hunk_Alloc( + surf->surfaceSprites = (srfSprites_t *)Hunk_Alloc( sizeof(srfSprites_t) * surf->numSurfaceSprites, h_low); int surfaceSpriteNum = 0; @@ -4217,7 +4223,7 @@ world_t *R_LoadBSP(const char *name, int *bspIndex) worldIndex = *bspIndex = tr.numBspModels; - worldData = (world_t *)ri.Hunk_Alloc(sizeof(*worldData), h_low); + worldData = (world_t *)Hunk_Alloc(sizeof(*worldData), h_low); tr.bspModels[tr.numBspModels] = worldData; ++tr.numBspModels; } @@ -4242,7 +4248,7 @@ world_t *R_LoadBSP(const char *name, int *bspIndex) Q_strncpyz(tr.worldName, worldData->name, sizeof(worldData->name)); COM_StripExtension(tr.worldName, tr.worldName, sizeof(tr.worldName)); - const byte *startMarker = (const byte *)ri.Hunk_Alloc(0, h_low); + const byte *startMarker = (const byte *)Hunk_Alloc(0, h_low); dheader_t *header = (dheader_t *)buffer.b; fileBase = (byte *)header; @@ -4297,6 +4303,7 @@ world_t *R_LoadBSP(const char *name, int *bspIndex) &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES]); + R_LoadWeatherImages(); R_GenerateSurfaceSprites(worldData, worldIndex + 1); // load cubemaps @@ -4319,7 +4326,13 @@ world_t *R_LoadBSP(const char *name, int *bspIndex) { for (int i = 0; i < numCubemapEntities; i++) { +#ifdef REND2_SP + COM_BeginParseSession(); +#endif R_LoadCubemapEntities(cubemapEntities[i]); +#ifdef REND2_SP + COM_EndParseSession(); +#endif if (tr.numCubemaps) break; } @@ -4339,7 +4352,7 @@ world_t *R_LoadBSP(const char *name, int *bspIndex) R_MergeLeafSurfaces(worldData); } - worldData->dataSize = (const byte *)ri.Hunk_Alloc(0, h_low) - startMarker; + worldData->dataSize = (const byte *)Hunk_Alloc(0, h_low) - startMarker; // make sure the VBO glState entries are safe R_BindNullVBO(); diff --git a/codemp/rd-rend2/tr_cache.cpp b/shared/rd-rend2/tr_cache.cpp similarity index 96% rename from codemp/rd-rend2/tr_cache.cpp rename to shared/rd-rend2/tr_cache.cpp index 4b546f90c9..df62c616e6 100644 --- a/codemp/rd-rend2/tr_cache.cpp +++ b/shared/rd-rend2/tr_cache.cpp @@ -81,7 +81,7 @@ qboolean CModelCacheManager::LoadFile( const char *pFileName, void **ppFileBuffe if (!strcmp (sDEFAULT_GLA_NAME ".gla", path)) { // return fake params as though it was found on disk... - void *pvFakeGLAFile = Z_Malloc(sizeof (FakeGLAFile), TAG_FILESYS, qfalse); + void *pvFakeGLAFile = R_Malloc(sizeof (FakeGLAFile), TAG_FILESYS, qfalse); memcpy(pvFakeGLAFile, &FakeGLAFile[0], sizeof (FakeGLAFile)); *ppFileBuffer = pvFakeGLAFile; @@ -103,7 +103,7 @@ qboolean CModelCacheManager::LoadFile( const char *pFileName, void **ppFileBuffe void* CModelCacheManager::Allocate( int iSize, void *pvDiskBuffer, const char *psModelFileName, qboolean *bAlreadyFound, memtag_t eTag ) { - int iChecksum; + int iChecksum = 1; char sModelName[MAX_QPATH]; /* Standard NULL checking. */ @@ -124,16 +124,20 @@ void* CModelCacheManager::Allocate( int iSize, void *pvDiskBuffer, const char *p if( pvDiskBuffer ) Z_MorphMallocTag( pvDiskBuffer, eTag ); else - pvDiskBuffer = Z_Malloc(iSize, eTag, qfalse); + pvDiskBuffer = R_Malloc(iSize, eTag, qfalse); files.emplace_back(); pFile = &files.back(); pFile->pDiskImage = pvDiskBuffer; pFile->iAllocSize = iSize; Q_strncpyz(pFile->path, sModelName, sizeof(pFile->path)); - +#ifndef REND2_SP if( ri.FS_FileIsInPAK( sModelName, &iChecksum ) ) pFile->iPAKChecksum = iChecksum; /* Otherwise, it will be -1. */ +#else + if (ri.FS_FileIsInPAK(sModelName)) + pFile->iPAKChecksum = iChecksum; /* Otherwise, it will be -1. */ +#endif *bAlreadyFound = qfalse; } @@ -177,7 +181,11 @@ void CModelCacheManager::DumpNonPure( void ) for ( auto it = files.begin(); it != files.end(); /* empty */ ) { int iChecksum; +#ifndef REND2_SP int iInPak = ri.FS_FileIsInPAK( it->path, &iChecksum ); +#else + int iInPak = ri.FS_FileIsInPAK(it->path); +#endif if( iInPak == -1 || iChecksum != it->iPAKChecksum ) { diff --git a/codemp/rd-rend2/tr_cache.h b/shared/rd-rend2/tr_cache.h similarity index 100% rename from codemp/rd-rend2/tr_cache.h rename to shared/rd-rend2/tr_cache.h diff --git a/codemp/rd-rend2/tr_cmds.cpp b/shared/rd-rend2/tr_cmds.cpp similarity index 98% rename from codemp/rd-rend2/tr_cmds.cpp rename to shared/rd-rend2/tr_cmds.cpp index 2a0fe463f7..d10e620691 100644 --- a/codemp/rd-rend2/tr_cmds.cpp +++ b/shared/rd-rend2/tr_cmds.cpp @@ -487,6 +487,33 @@ void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode) } } +#ifdef REND2_SP +void RE_LAGoggles(void) +{ + tr.refdef.doLAGoggles = true; + tr.refdef.doFullbright = true; +} + +/* +============= +RE_Scissor +============= +*/ +void RE_Scissor(float x, float y, float w, float h) +{ + scissorCommand_t* cmd; + + cmd = (scissorCommand_t*)R_GetCommandBuffer(sizeof(*cmd)); + if (!cmd) { + return; + } + cmd->commandId = RC_SCISSOR; + cmd->x = x; + cmd->y = y; + cmd->w = w; + cmd->h = h; +} +#endif /* ==================== diff --git a/codemp/rd-rend2/tr_curve.cpp b/shared/rd-rend2/tr_curve.cpp similarity index 98% rename from codemp/rd-rend2/tr_curve.cpp rename to shared/rd-rend2/tr_curve.cpp index bdb8494242..d65d5aecc6 100644 --- a/codemp/rd-rend2/tr_curve.cpp +++ b/shared/rd-rend2/tr_curve.cpp @@ -387,21 +387,21 @@ srfBspSurface_t *R_CreateSurfaceGridMesh(int width, int height, size = (width * height - 1) * sizeof( srfVert_t ) + sizeof( *grid ); #ifdef PATCH_STITCHING - grid = /*ri.Hunk_Alloc*/ (srfBspSurface_t *)Z_Malloc( size, TAG_GRIDMESH ); + grid = /*ri.Hunk_Alloc*/ (srfBspSurface_t *)R_Malloc( size, TAG_GRIDMESH ); Com_Memset(grid, 0, size); - grid->widthLodError = /*ri.Hunk_Alloc*/ (float *)Z_Malloc( width * 4, TAG_GRIDMESH ); + grid->widthLodError = /*ri.Hunk_Alloc*/ (float *)R_Malloc( width * 4, TAG_GRIDMESH ); Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); - grid->heightLodError = /*ri.Hunk_Alloc*/ (float *)Z_Malloc( height * 4, TAG_GRIDMESH ); + grid->heightLodError = /*ri.Hunk_Alloc*/ (float *)R_Malloc( height * 4, TAG_GRIDMESH ); Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); grid->numIndexes = numIndexes; - grid->indexes = (glIndex_t *)Z_Malloc(grid->numIndexes * sizeof(glIndex_t), TAG_GRIDMESH); + grid->indexes = (glIndex_t *)R_Malloc(grid->numIndexes * sizeof(glIndex_t), TAG_GRIDMESH); Com_Memcpy(grid->indexes, indexes, numIndexes * sizeof(glIndex_t)); grid->numVerts = (width * height); - grid->verts = (srfVert_t *)Z_Malloc(grid->numVerts * sizeof(srfVert_t), TAG_GRIDMESH); + grid->verts = (srfVert_t *)R_Malloc(grid->numVerts * sizeof(srfVert_t), TAG_GRIDMESH); #else grid = ri.Hunk_Alloc( size ); Com_Memset(grid, 0, size); diff --git a/codemp/rd-rend2/tr_decals.cpp b/shared/rd-rend2/tr_decals.cpp similarity index 97% rename from codemp/rd-rend2/tr_decals.cpp rename to shared/rd-rend2/tr_decals.cpp index 6bf4222da7..fe6a7d7f99 100644 --- a/codemp/rd-rend2/tr_decals.cpp +++ b/shared/rd-rend2/tr_decals.cpp @@ -209,7 +209,7 @@ void RE_AddDecalToScene( qhandle_t decalShader, const vec3_t origin, const vec3_ // if it is a temporary (shadow) mark, add it immediately and forget about it if ( temporary ) { - RE_AddPolyToScene( decalShader, mf->numPoints, verts, 1 ); + RE_AddPolyToScene( decalShader, mf->numPoints, verts ); continue; } @@ -275,7 +275,7 @@ void R_AddDecals( void ) p->verts[j].modulate[3] = fade; } - RE_AddPolyToScene( p->shader, p->poly.numVerts, p->verts, 1 ); + RE_AddPolyToScene( p->shader, p->poly.numVerts, p->verts ); } else { @@ -284,7 +284,7 @@ void R_AddDecals( void ) } else { - RE_AddPolyToScene( p->shader, p->poly.numVerts, p->verts, 1 ); + RE_AddPolyToScene( p->shader, p->poly.numVerts, p->verts ); } } diff --git a/codemp/rd-rend2/tr_extensions.cpp b/shared/rd-rend2/tr_extensions.cpp similarity index 100% rename from codemp/rd-rend2/tr_extensions.cpp rename to shared/rd-rend2/tr_extensions.cpp diff --git a/codemp/rd-rend2/tr_extramath.cpp b/shared/rd-rend2/tr_extramath.cpp similarity index 100% rename from codemp/rd-rend2/tr_extramath.cpp rename to shared/rd-rend2/tr_extramath.cpp diff --git a/codemp/rd-rend2/tr_extramath.h b/shared/rd-rend2/tr_extramath.h similarity index 100% rename from codemp/rd-rend2/tr_extramath.h rename to shared/rd-rend2/tr_extramath.h diff --git a/codemp/rd-rend2/tr_extratypes.h b/shared/rd-rend2/tr_extratypes.h similarity index 100% rename from codemp/rd-rend2/tr_extratypes.h rename to shared/rd-rend2/tr_extratypes.h diff --git a/codemp/rd-rend2/tr_fbo.cpp b/shared/rd-rend2/tr_fbo.cpp similarity index 99% rename from codemp/rd-rend2/tr_fbo.cpp rename to shared/rd-rend2/tr_fbo.cpp index 9a7e4f97a6..80a395ed49 100644 --- a/codemp/rd-rend2/tr_fbo.cpp +++ b/shared/rd-rend2/tr_fbo.cpp @@ -123,7 +123,7 @@ FBO_t *FBO_Create(const char *name, int width, int height) ri.Error(ERR_DROP, "FBO_Create: MAX_FBOS hit"); } - fbo = tr.fbos[tr.numFBOs] = (FBO_t *)ri.Hunk_Alloc(sizeof(*fbo), h_low); + fbo = tr.fbos[tr.numFBOs] = (FBO_t *)Hunk_Alloc(sizeof(*fbo), h_low); Q_strncpyz(fbo->name, name, sizeof(fbo->name)); fbo->index = tr.numFBOs++; fbo->width = width; diff --git a/codemp/rd-rend2/tr_fbo.h b/shared/rd-rend2/tr_fbo.h similarity index 100% rename from codemp/rd-rend2/tr_fbo.h rename to shared/rd-rend2/tr_fbo.h diff --git a/codemp/rd-rend2/tr_flares.cpp b/shared/rd-rend2/tr_flares.cpp similarity index 100% rename from codemp/rd-rend2/tr_flares.cpp rename to shared/rd-rend2/tr_flares.cpp diff --git a/codemp/rd-rend2/tr_ghoul2.cpp b/shared/rd-rend2/tr_ghoul2.cpp similarity index 96% rename from codemp/rd-rend2/tr_ghoul2.cpp rename to shared/rd-rend2/tr_ghoul2.cpp index 0705bee8d5..eb6438391e 100644 --- a/codemp/rd-rend2/tr_ghoul2.cpp +++ b/shared/rd-rend2/tr_ghoul2.cpp @@ -3,7 +3,9 @@ #include "qcommon/matcomp.h" #include "qcommon/qcommon.h" #include "ghoul2/G2.h" +#ifndef REND2_SP #include "ghoul2/g2_local.h" +#endif // !REND2_SP #ifdef _G2_GORE #include "G2_gore_r2.h" #endif @@ -11,7 +13,9 @@ #ifdef _MSC_VER #pragma warning (disable: 4512) //default assignment operator could not be gened #endif +#ifndef REND2_SP #include "qcommon/disablewarnings.h" +#endif // !REND2_SP #include "tr_cache.h" #define LL(x) x=LittleLong(x) @@ -81,6 +85,10 @@ void G2Time_ReportTimers(void) //rww - RAGDOLL_END +#ifdef REND2_SP +extern cvar_t* sv_mapname; +#endif + static const int MAX_RENDERABLE_SURFACES = 2048; static CRenderableSurface renderSurfHeap[MAX_RENDERABLE_SURFACES]; static int currentRenderSurfIndex = 0; @@ -918,19 +926,24 @@ R_AComputeFogNum static int R_GComputeFogNum( trRefEntity_t *ent ) { int i, j; + float frameRadius; fog_t *fog; + vec3_t localOrigin; if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { return 0; } + VectorCopy(ent->e.origin, localOrigin); + frameRadius = ent->e.radius; +#ifndef REND2_SP for ( i = 1 ; i < tr.world->numfogs ; i++ ) { fog = &tr.world->fogs[i]; for ( j = 0 ; j < 3 ; j++ ) { - if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) { + if ( localOrigin[j] - frameRadius >= fog->bounds[1][j] ) { break; } - if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) { + if ( localOrigin[j] + frameRadius <= fog->bounds[0][j] ) { break; } } @@ -938,8 +951,40 @@ static int R_GComputeFogNum( trRefEntity_t *ent ) { return i; } } - return 0; +#else + int partialFog = 0; + for (i = 1; i < tr.world->numfogs; i++) { + fog = &tr.world->fogs[i]; + if (localOrigin[0] - frameRadius >= fog->bounds[0][0] + && localOrigin[0] + frameRadius <= fog->bounds[1][0] + && localOrigin[1] - frameRadius >= fog->bounds[0][1] + && localOrigin[1] + frameRadius <= fog->bounds[1][1] + && localOrigin[2] - frameRadius >= fog->bounds[0][2] + && localOrigin[2] + frameRadius <= fog->bounds[1][2]) + {//totally inside it + return i; + break; + } + if ((localOrigin[0] - frameRadius >= fog->bounds[0][0] && localOrigin[1] - frameRadius >= fog->bounds[0][1] && localOrigin[2] - frameRadius >= fog->bounds[0][2] && + localOrigin[0] - frameRadius <= fog->bounds[1][0] && localOrigin[1] - frameRadius <= fog->bounds[1][1] && localOrigin[2] - frameRadius <= fog->bounds[1][2]) || + (localOrigin[0] + frameRadius >= fog->bounds[0][0] && localOrigin[1] + frameRadius >= fog->bounds[0][1] && localOrigin[2] + frameRadius >= fog->bounds[0][2] && + localOrigin[0] + frameRadius <= fog->bounds[1][0] && localOrigin[1] + frameRadius <= fog->bounds[1][1] && localOrigin[2] + frameRadius <= fog->bounds[1][2])) + {//partially inside it + //if (tr.refdef.fogIndex == i || R_FogParmsMatch(tr.refdef.fogIndex, i)) + //{//take new one only if it's the same one that the viewpoint is in + // return i; + // break; + //} + //else + if (!partialFog) + {//first partialFog + partialFog = i; + } + } + } + return partialFog; +#endif } // work out lod for this entity. @@ -1870,6 +1915,7 @@ static void G2_TransformBone( int child, CBoneCache& BC ) } } +/* void G2_SetUpBolts( mdxaHeader_t *header, CGhoul2Info &ghoul2, @@ -1892,6 +1938,7 @@ void G2_SetUpBolts( boltList[i].position = bonePtr[boltList[i].boneNumber].second * skel->BasePoseMat; } } +*/ #define GHOUL2_RAG_STARTED 0x0010 //rwwFIXMEFIXME: Move this into the stupid header or something. @@ -1943,11 +1990,14 @@ static void G2_TransformGhoulBones( float val = r_Ghoul2AnimSmooth->value; if (val > 0.0f && val < 1.0f) { +#ifndef REND2_SP if (ghoul2.mFlags & GHOUL2_CRAZY_SMOOTH) { val = 0.9f; } - else if (ghoul2.mFlags & GHOUL2_RAG_STARTED) + else +#endif + if (ghoul2.mFlags & GHOUL2_RAG_STARTED) { for (size_t k = 0; k < rootBoneList.size(); k++) { @@ -2026,7 +2076,7 @@ static void G2_TransformGhoulBones( // // Surface Manipulation code - +#ifndef REND2_SP // We've come across a surface that's designated as a bolt surface, process it and put it in the appropriate bolt place void G2_ProcessSurfaceBolt( mdxaBone_v &bonePtr, @@ -2319,6 +2369,7 @@ void G2_ProcessGeneratedSurfaceBolts(CGhoul2Info &ghoul2, mdxaBone_v &bonePtr, m G2PerformanceTimer_G2_ProcessGeneratedSurfaceBolts.End(); #endif } +#endif void RenderSurfaces( CRenderSurface &RS, const trRefEntity_t *ent, int entityNum ) { @@ -2367,7 +2418,7 @@ void RenderSurfaces( CRenderSurface &RS, const trRefEntity_t *ent, int entityNum int j; // match the surface name to something in the skin file - shader = tr.defaultShader; + shader = R_GetShaderByHandle(surfInfo->shaderIndex); for ( j = 0 ; j < RS.skin->numSurfaces ; j++ ) { // the names have both been lowercased @@ -2454,7 +2505,7 @@ void RenderSurfaces( CRenderSurface &RS, const trRefEntity_t *ent, int entityNum float(curTime - kcur->second.mGoreGrowStartTime) / float(magicFactor42); // linear } -#ifdef REND2_SP_MAYBE +#ifdef REND2_SP_GORE if (curTime < kcur->second.mGoreGrowEndTime) { newSurf2->scale = Q_max( @@ -2474,7 +2525,7 @@ void RenderSurfaces( CRenderSurface &RS, const trRefEntity_t *ent, int entityNum { gshader = R_GetShaderByHandle(goreShader); } -#ifdef REND2_SP_MAYBE +#ifdef REND2_SP_GORE // Set fade on surf. // Only if we have a fade time set, and let us fade on // rgb if we want -rww @@ -2543,6 +2594,7 @@ void RenderSurfaces( CRenderSurface &RS, const trRefEntity_t *ent, int entityNum #endif } +/* // Go through the model and deal with just the surfaces that are tagged as bolt // on points - this is for the server side skeleton construction void ProcessModelBoltSurfaces( @@ -2608,7 +2660,7 @@ void ProcessModelBoltSurfaces( G2Time_ProcessModelBoltSurfaces += G2PerformanceTimer_ProcessModelBoltSurfaces.End(); #endif } - +*/ // build the used bone list so when doing bone transforms we can determine if we need to do it or not void G2_ConstructUsedBoneList(CConstructBoneList &CBL) @@ -2618,7 +2670,11 @@ void G2_ConstructUsedBoneList(CConstructBoneList &CBL) mdxmHeader_t *mdxm = CBL.currentModel->data.glm->header; // back track and get the surfinfo struct for this surface +#ifndef REND2_SP const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface((void *)CBL.currentModel, CBL.surfaceNum, 0); +#else + const mdxmSurface_t *surface = (mdxmSurface_t *)G2_FindSurface(CBL.currentModel, CBL.surfaceNum, 0); +#endif const mdxmHierarchyOffsets_t *surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)mdxm + sizeof(mdxmHeader_t)); const mdxmSurfHierarchy_t *surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]); const model_t *mod_a = R_GetModelByHandle(mdxm->animIndex); @@ -3232,19 +3288,22 @@ void R_AddGhoulSurfaces( trRefEntity_t *ent, int entityNum ) else { cust_shader = nullptr; +#ifndef REND2_SP // figure out the custom skin thing if (g2Info.mCustomSkin) { - skin = R_GetSkinByHandle(g2Info.mCustomSkin ); - } - else if (ent->e.customSkin) - { - skin = R_GetSkinByHandle(ent->e.customSkin ); - } - else if ( g2Info.mSkin > 0 && g2Info.mSkin < tr.numSkins ) - { - skin = R_GetSkinByHandle( g2Info.mSkin ); + skin = R_GetSkinByHandle(g2Info.mCustomSkin); } + else +#endif + if (ent->e.customSkin) + { + skin = R_GetSkinByHandle(ent->e.customSkin); + } + else if (g2Info.mSkin > 0 && g2Info.mSkin < tr.numSkins) + { + skin = R_GetSkinByHandle(g2Info.mSkin); + } } int whichLod = G2_ComputeLOD( ent, g2Info.currentModel, g2Info.mLodBias ); @@ -3494,7 +3553,11 @@ void RB_TransformBones(const trRefEntity_t *ent, const trRefdef_t *refdef, int c for (int bone = 0; bone < (int)bc->mBones.size(); bone++) { +#ifdef JK2_MODE + const mdxaBone_t& b = bc->Eval(bone); +#else const mdxaBone_t& b = bc->EvalRender(bone); +#endif // JK2_MODE Com_Memcpy( bc->boneMatrices + bone, &b.matrix[0][0], @@ -3566,7 +3629,7 @@ void RB_SurfaceGhoul( CRenderableSurface *surf ) maxIndex = surf->alternateTex->firstVert + surf->alternateTex->numVerts; indexOffset = surf->alternateTex->firstIndex; -#ifdef REND2_SP_MAYBE +#ifdef REND2_SP_GORE // UNTESTED CODE if (surf->scale > 1.0f) { @@ -3600,8 +3663,6 @@ void RB_SurfaceGhoul( CRenderableSurface *surf ) tess.svars.colors[tess.firstIndex][3] = lFade; } } - tess.scale = false; - tess.fade = false; #endif } else { #endif @@ -4097,7 +4158,7 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & qboolean bAlreadyFound = qfalse; mdxm = (mdxmHeader_t*)CModelCache->Allocate(size, buffer, mod_name, &bAlreadyFound, TAG_MODEL_GLM); - mod->data.glm = (mdxmData_t *)ri.Hunk_Alloc (sizeof (mdxmData_t), h_low); + mod->data.glm = (mdxmData_t *)Hunk_Alloc(sizeof (mdxmData_t), h_low); mod->data.glm->header = mdxm; //RE_RegisterModels_Malloc(size, buffer, mod_name, &bAlreadyFound, TAG_MODEL_GLM); @@ -4127,7 +4188,34 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & // first up, go load in the animation file we need that has the skeletal // animation info for this model - mdxm->animIndex = RE_RegisterModel(va ("%s.gla",mdxm->animName)); + mdxm->animIndex = RE_RegisterModel(va("%s.gla", mdxm->animName)); +#ifdef REND2_SP + char animGLAName[MAX_QPATH]; + char* strippedName; + char* slash = NULL; + const char* mapname = sv_mapname->string; + + if (strcmp(mapname, "nomap")) + { + if (strrchr(mapname, '/')) //maps in subfolders use the root name, ( presuming only one level deep!) + { + mapname = strrchr(mapname, '/') + 1; + } + //stripped name of GLA for this model + Q_strncpyz(animGLAName, mdxm->animName, sizeof(animGLAName)); + slash = strrchr(animGLAName, '/'); + if (slash) + { + *slash = 0; + } + strippedName = COM_SkipPath(animGLAName); + if (VALIDSTRING(strippedName)) + { + RE_RegisterModel(va("models/players/%s_%s/%s_%s.gla", strippedName, mapname, strippedName, mapname)); + } + } + +#endif if (!mdxm->animIndex) { @@ -4137,28 +4225,30 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & mod->numLods = mdxm->numLODs -1 ; //copy this up to the model for ease of use - it wil get inced after this. - if (bAlreadyFound) - { - return qtrue; // All done. Stop, go no further, do not LittleLong(), do not pass Go... - } - +#ifndef JK2_MODE bool isAnOldModelFile = false; if (mdxm->numBones == 72 && strstr(mdxm->animName,"_humanoid") ) { isAnOldModelFile = true; } - +#endif surfInfo = (mdxmSurfHierarchy_t *)( (byte *)mdxm + mdxm->ofsSurfHierarchy); for ( i = 0 ; i < mdxm->numSurfaces ; i++) { LL(surfInfo->numChildren); LL(surfInfo->parentIndex); - +#ifndef JK2_MODE Q_strlwr(surfInfo->name); //just in case if ( !strcmp( &surfInfo->name[strlen(surfInfo->name)-4],"_off") ) { surfInfo->name[strlen(surfInfo->name)-4]=0; //remove "_off" from name } +#endif + + if (surfInfo->shader[0] == '[') + { + surfInfo->shader[0] = 0; //kill the stupid [nomaterial] since carcass doesn't + } // do all the children indexs for (j=0; jnumChildren; j++) @@ -4228,7 +4318,7 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & // change to surface identifier surf->ident = SF_MDX; // register the shaders - +#ifndef JK2_MODE if (isAnOldModelFile) { int *boneRef = (int *) ( (byte *)surf + surf->ofsBoneReferences ); @@ -4244,6 +4334,7 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & } } } +#endif // find the next surface surf = (mdxmSurface_t *)( (byte *)surf + surf->ofsEnd ); } @@ -4254,7 +4345,7 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & // Make a copy on the GPU lod = (mdxmLOD_t *)((byte *)mdxm + mdxm->ofsLODs); - mod->data.glm->vboModels = (mdxmVBOModel_t *)ri.Hunk_Alloc (sizeof (mdxmVBOModel_t) * mdxm->numLODs, h_low); + mod->data.glm->vboModels = (mdxmVBOModel_t *)Hunk_Alloc (sizeof (mdxmVBOModel_t) * mdxm->numLODs, h_low); for ( l = 0; l < mdxm->numLODs; l++ ) { mdxmVBOModel_t *vboModel = &mod->data.glm->vboModels[l]; @@ -4275,11 +4366,11 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & int numTriangles = 0; // +1 to add total vertex count - int *baseVertexes = (int *)ri.Hunk_AllocateTempMemory (sizeof (int) * (mdxm->numSurfaces + 1)); - int *indexOffsets = (int *)ri.Hunk_AllocateTempMemory (sizeof (int) * mdxm->numSurfaces); + int *baseVertexes = (int *)Hunk_AllocateTempMemory (sizeof (int) * (mdxm->numSurfaces + 1)); + int *indexOffsets = (int *)Hunk_AllocateTempMemory (sizeof (int) * mdxm->numSurfaces); vboModel->numVBOMeshes = mdxm->numSurfaces; - vboModel->vboMeshes = (mdxmVBOMesh_t *)ri.Hunk_Alloc (sizeof (mdxmVBOMesh_t) * mdxm->numSurfaces, h_low); + vboModel->vboMeshes = (mdxmVBOMesh_t *)Hunk_Alloc (sizeof (mdxmVBOMesh_t) * mdxm->numSurfaces, h_low); vboMeshes = vboModel->vboMeshes; surf = (mdxmSurface_t *)((byte *)lod + sizeof (mdxmLOD_t) + (mdxm->numSurfaces * sizeof (mdxmLODSurfOffset_t))); @@ -4306,7 +4397,7 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & dataSize += numVerts * sizeof (*tangents); // Allocate and write to memory - data = (byte *)ri.Hunk_AllocateTempMemory (dataSize); + data = (byte *)Hunk_AllocateTempMemory (dataSize); ofsPosition = stride; verts = (vec3_t *)(data + ofsPosition); @@ -4333,16 +4424,16 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & stride += sizeof (*tangents); // Fill in the index buffer and compute tangents - glIndex_t *indices = (glIndex_t *)ri.Hunk_AllocateTempMemory(sizeof(glIndex_t) * numTriangles * 3); + glIndex_t *indices = (glIndex_t *)Hunk_AllocateTempMemory(sizeof(glIndex_t) * numTriangles * 3); glIndex_t *index = indices; - uint32_t *tangentsf = (uint32_t *)ri.Hunk_AllocateTempMemory(sizeof(uint32_t) * numVerts); + uint32_t *tangentsf = (uint32_t *)Hunk_AllocateTempMemory(sizeof(uint32_t) * numVerts); surf = (mdxmSurface_t *)((byte *)lod + sizeof(mdxmLOD_t) + (mdxm->numSurfaces * sizeof(mdxmLODSurfOffset_t))); for (int n = 0; n < mdxm->numSurfaces; n++) { mdxmTriangle_t *t = (mdxmTriangle_t *)((byte *)surf + surf->ofsTriangles); - glIndex_t *surf_indices = (glIndex_t *)ri.Hunk_AllocateTempMemory(sizeof(glIndex_t) * surf->numTriangles * 3); + glIndex_t *surf_indices = (glIndex_t *)Hunk_AllocateTempMemory(sizeof(glIndex_t) * surf->numTriangles * 3); glIndex_t *surf_index = surf_indices; for (int k = 0; k < surf->numTriangles; k++, index += 3, surf_index += 3) @@ -4458,9 +4549,9 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & VBO_t *vbo = R_CreateVBO (data, dataSize, VBO_USAGE_STATIC); IBO_t *ibo = R_CreateIBO((byte *)indices, sizeof(glIndex_t) * numTriangles * 3, VBO_USAGE_STATIC); - ri.Hunk_FreeTempMemory (data); - ri.Hunk_FreeTempMemory (tangentsf); - ri.Hunk_FreeTempMemory (indices); + Hunk_FreeTempMemory (data); + Hunk_FreeTempMemory (tangentsf); + Hunk_FreeTempMemory (indices); vbo->offsets[ATTR_INDEX_POSITION] = ofsPosition; vbo->offsets[ATTR_INDEX_NORMAL] = ofsNormals; @@ -4502,8 +4593,8 @@ qboolean R_LoadMDXM(model_t *mod, void *buffer, const char *mod_name, qboolean & vboModel->vbo = vbo; vboModel->ibo = ibo; - ri.Hunk_FreeTempMemory (indexOffsets); - ri.Hunk_FreeTempMemory (baseVertexes); + Hunk_FreeTempMemory (indexOffsets); + Hunk_FreeTempMemory (baseVertexes); lod = (mdxmLOD_t *)((byte *)lod + lod->ofsEnd); } diff --git a/codemp/rd-rend2/tr_glsl.cpp b/shared/rd-rend2/tr_glsl.cpp similarity index 98% rename from codemp/rd-rend2/tr_glsl.cpp rename to shared/rd-rend2/tr_glsl.cpp index 5a3201fa52..c6d7fb1c13 100644 --- a/codemp/rd-rend2/tr_glsl.cpp +++ b/shared/rd-rend2/tr_glsl.cpp @@ -159,7 +159,7 @@ static void GLSL_PrintProgramInfoLog(GLuint object, qboolean developerOnly) } else { - char *msg = (char *)Z_Malloc(maxLength, TAG_SHADERTEXT); + char *msg = (char *)R_Malloc(maxLength, TAG_SHADERTEXT); qglGetProgramInfoLog(object, maxLength, &maxLength, msg); @@ -202,7 +202,7 @@ static void GLSL_PrintShaderInfoLog(GLuint object, qboolean developerOnly) } else { - msg = (char *)Z_Malloc(maxLength, TAG_SHADERTEXT); + msg = (char *)R_Malloc(maxLength, TAG_SHADERTEXT); qglGetShaderInfoLog(object, maxLength, &maxLength, msg); @@ -228,7 +228,7 @@ static void GLSL_PrintShaderSource(GLuint shader) return; } - char *msg = (char *)Z_Malloc(maxLength, TAG_SHADERTEXT); + char *msg = (char *)R_Malloc(maxLength, TAG_SHADERTEXT); qglGetShaderSource(shader, maxLength, nullptr, msg); for (int i = 0; i < maxLength; i += 1023) @@ -301,6 +301,8 @@ static size_t GLSL_GetShaderHeader( "#define TCGEN_LIGHTMAP3 %i\n" "#define TCGEN_TEXTURE %i\n" "#define TCGEN_ENVIRONMENT_MAPPED %i\n" + "#define TCGEN_ENVIRONMENT_MAPPED_SP %i\n" + "#define TCGEN_ENVIRONMENT_MAPPED_SP_FP %i\n" "#define TCGEN_FOG %i\n" "#define TCGEN_VECTOR %i\n" "#endif\n", @@ -310,6 +312,8 @@ static size_t GLSL_GetShaderHeader( TCGEN_LIGHTMAP3, TCGEN_TEXTURE, TCGEN_ENVIRONMENT_MAPPED, + TCGEN_ENVIRONMENT_MAPPED_SP, + TCGEN_ENVIRONMENT_MAPPED_SP_FP, TCGEN_FOG, TCGEN_VECTOR)); @@ -347,6 +351,10 @@ static size_t GLSL_GetShaderHeader( va("#define MAX_G2_BONES %i\n", MAX_G2_BONES)); + Q_strcat(dest, size, + va("#define MAX_GPU_FOGS %i\n", + MAX_GPU_FOGS)); + fbufWidthScale = (float)glConfig.vidWidth; fbufHeightScale = (float)glConfig.vidHeight; Q_strcat(dest, size, @@ -756,7 +764,7 @@ bool ShaderProgramBuilder::AddShader( const GPUShaderDesc& shaderDesc, const cha bool ShaderProgramBuilder::Build( shaderProgram_t *shaderProgram ) { const size_t nameBufferSize = strlen(name) + 1; - shaderProgram->name = (char *)Z_Malloc(nameBufferSize, TAG_GENERAL); + shaderProgram->name = (char *)R_Malloc(nameBufferSize, TAG_GENERAL); Q_strncpyz(shaderProgram->name, name, nameBufferSize); shaderProgram->program = program; @@ -806,9 +814,9 @@ static bool GLSL_LoadGPUShader( void GLSL_InitUniforms(shaderProgram_t *program) { - program->uniforms = (GLint *)Z_Malloc( + program->uniforms = (GLint *)R_Malloc( UNIFORM_COUNT * sizeof(*program->uniforms), TAG_GENERAL); - program->uniformBufferOffsets = (short *)Z_Malloc( + program->uniformBufferOffsets = (short *)R_Malloc( UNIFORM_COUNT * sizeof(*program->uniformBufferOffsets), TAG_GENERAL); GLint *uniforms = program->uniforms; @@ -848,7 +856,7 @@ void GLSL_InitUniforms(shaderProgram_t *program) } } - program->uniformBuffer = (char *)Z_Malloc(size, TAG_SHADERTEXT, qtrue); + program->uniformBuffer = (char *)R_Malloc(size, TAG_SHADERTEXT, qtrue); program->uniformBlocks = 0; for ( int i = 0; i < UNIFORM_BLOCK_COUNT; ++i ) @@ -1292,10 +1300,10 @@ void GLSL_DeleteGPUShader(shaderProgram_t *program) { qglDeleteProgram(program->program); - Z_Free (program->name); - Z_Free (program->uniformBuffer); - Z_Free (program->uniformBufferOffsets); - Z_Free (program->uniforms); + Z_Free(program->name); + Z_Free(program->uniformBuffer); + Z_Free(program->uniformBufferOffsets); + Z_Free(program->uniforms); Com_Memset(program, 0, sizeof(*program)); } @@ -1303,7 +1311,7 @@ void GLSL_DeleteGPUShader(shaderProgram_t *program) static bool GLSL_IsValidPermutationForGeneric (int shaderCaps) { -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if ( (shaderCaps & GENERICDEF_USE_VERTEX_ANIMATION) && (shaderCaps & GENERICDEF_USE_SKELETAL_ANIMATION) ) return false; @@ -1313,7 +1321,7 @@ static bool GLSL_IsValidPermutationForGeneric (int shaderCaps) static bool GLSL_IsValidPermutationForFog (int shaderCaps) { -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if ( (shaderCaps & FOGDEF_USE_VERTEX_ANIMATION) && (shaderCaps & FOGDEF_USE_SKELETAL_ANIMATION) ) return false; @@ -1329,7 +1337,7 @@ static bool GLSL_IsValidPermutationForLight (int lightType, int shaderCaps) if (!lightType && (shaderCaps & LIGHTDEF_USE_PARALLAXMAP)) return false; -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if ( (shaderCaps & LIGHTDEF_USE_SKELETAL_ANIMATION) && (shaderCaps & LIGHTDEF_USE_VERTEX_ANIMATION) ) return false; @@ -1386,7 +1394,8 @@ void GLSL_InitSplashScreenShader() size_t splashLen = strlen("splash"); tr.splashScreenShader.program = program; - tr.splashScreenShader.name = (char *)Z_Malloc(splashLen + 1, TAG_GENERAL); + tr.splashScreenShader.name = (char *)R_Malloc(splashLen + 1, TAG_GENERAL); + GLSL_InitUniforms(&tr.splashScreenShader); Q_strncpyz(tr.splashScreenShader.name, "splash", splashLen + 1); } @@ -1442,7 +1451,7 @@ static int GLSL_LoadGPUProgramGeneric( Q_strcat(extradefines, sizeof(extradefines), "#define USE_TCGEN\n"); Q_strcat(extradefines, sizeof(extradefines), "#define USE_TCMOD\n"); } -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (i & GENERICDEF_USE_VERTEX_ANIMATION) { Q_strcat(extradefines, sizeof(extradefines), "#define USE_VERTEX_ANIMATION\n"); @@ -1511,11 +1520,11 @@ static int GLSL_LoadGPUProgramFogPass( if (i & FOGDEF_USE_DEFORM_VERTEXES) Q_strcat(extradefines, sizeof(extradefines), "#define USE_DEFORM_VERTEXES\n"); -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (i & FOGDEF_USE_VERTEX_ANIMATION) { Q_strcat(extradefines, sizeof(extradefines), "#define USE_VERTEX_ANIMATION\n"); - attribs |= ATTR_POSITION2 | ATTR_NORMAL2 + attribs |= ATTR_POSITION2 | ATTR_NORMAL2; } #endif // REND2_SP if (i & FOGDEF_USE_SKELETAL_ANIMATION) @@ -1574,7 +1583,7 @@ static int GLSL_LoadGPUProgramRefraction( Q_strcat(extradefines, sizeof(extradefines), "#define USE_TCGEN\n"); Q_strcat(extradefines, sizeof(extradefines), "#define USE_TCMOD\n"); } -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (i & REFRACTIONDEF_USE_VERTEX_ANIMATION) { Q_strcat(extradefines, sizeof(extradefines), "#define USE_VERTEX_ANIMATION\n"); @@ -1739,7 +1748,7 @@ static int GLSL_LoadGPUProgramLightAll( { Q_strcat(extradefines, sizeof(extradefines), "#define USE_CLOTH_BRDF\n"); } -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (i & LIGHTDEF_USE_VERTEX_ANIMATION) { Q_strcat(extradefines, sizeof(extradefines), "#define USE_VERTEX_ANIMATION\n"); @@ -2528,7 +2537,7 @@ void GL_VertexArraysToAttribs( { 4, GL_FALSE, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE }, // light direction { 4, GL_TRUE, GL_UNSIGNED_BYTE, GL_FALSE }, // bone indices { 4, GL_FALSE, GL_UNSIGNED_BYTE, GL_TRUE }, // bone weights -#ifdef REND2_SP +#ifdef REND2_SP_MD3 { 3, GL_FALSE, GL_FLOAT, GL_FALSE }, // pos2 { 4, GL_FALSE, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE }, // tangent2 { 4, GL_FALSE, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE }, // normal2 @@ -2613,7 +2622,7 @@ shaderProgram_t *GLSL_GetGenericShaderProgram(int stage) { shaderAttribs |= GENERICDEF_USE_DEFORM_VERTEXES; } -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (glState.vertexAnimation) { shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; diff --git a/codemp/rd-rend2/tr_glsl_parse.cpp b/shared/rd-rend2/tr_glsl_parse.cpp similarity index 100% rename from codemp/rd-rend2/tr_glsl_parse.cpp rename to shared/rd-rend2/tr_glsl_parse.cpp diff --git a/codemp/rd-rend2/tr_image.cpp b/shared/rd-rend2/tr_image.cpp similarity index 98% rename from codemp/rd-rend2/tr_image.cpp rename to shared/rd-rend2/tr_image.cpp index 03e0f03292..8953d63451 100644 --- a/codemp/rd-rend2/tr_image.cpp +++ b/shared/rd-rend2/tr_image.cpp @@ -45,8 +45,8 @@ Extends the size of the images pool allocator */ static void R_ExtendImagesPool() { - ImagesPool *pool = (ImagesPool *)Z_Malloc(sizeof(*pool), TAG_GENERAL); - image_t *freeImages = (image_t *)Z_Malloc(sizeof(*freeImages) * NUM_IMAGES_PER_POOL_ALLOC, TAG_IMAGE_T); + ImagesPool *pool = (ImagesPool *)R_Malloc(sizeof(*pool), TAG_GENERAL); + image_t *freeImages = (image_t *)R_Malloc(sizeof(*freeImages) * NUM_IMAGES_PER_POOL_ALLOC, TAG_IMAGE_T); for ( int i = 0; i < (NUM_IMAGES_PER_POOL_ALLOC - 1); i++ ) { @@ -1319,7 +1319,7 @@ static void R_MipMap2( byte *in, int inWidth, int inHeight ) { outWidth = inWidth >> 1; outHeight = inHeight >> 1; - temp = (unsigned int *)ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); + temp = (unsigned int *)Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); inWidthMask = inWidth - 1; inHeightMask = inHeight - 1; @@ -1354,7 +1354,7 @@ static void R_MipMap2( byte *in, int inWidth, int inHeight ) { } Com_Memcpy( in, temp, outWidth * outHeight * 4 ); - ri.Hunk_FreeTempMemory( temp ); + Hunk_FreeTempMemory( temp ); } @@ -1369,7 +1369,7 @@ static void R_MipMapsRGB( byte *in, int inWidth, int inHeight) outWidth = inWidth >> 1; outHeight = inHeight >> 1; - temp = (byte *)ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); + temp = (byte *)Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); for ( i = 0 ; i < outHeight ; i++ ) { byte *outbyte = temp + ( i * outWidth ) * 4; @@ -1399,7 +1399,7 @@ static void R_MipMapsRGB( byte *in, int inWidth, int inHeight) } Com_Memcpy( in, temp, outWidth * outHeight * 4 ); - ri.Hunk_FreeTempMemory( temp ); + Hunk_FreeTempMemory( temp ); } /* @@ -1625,7 +1625,7 @@ static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_he finalheight >>= 1; } - *resampledBuffer = (byte *)ri.Hunk_AllocateTempMemory( finalwidth * finalheight * 4 ); + *resampledBuffer = (byte *)Hunk_AllocateTempMemory( finalwidth * finalheight * 4 ); if (scaled_width != width || scaled_height != height) { @@ -1677,7 +1677,7 @@ static void RawImage_ScaleToPower2( byte **data, int *inout_width, int *inout_he else if ( scaled_width != width || scaled_height != height ) { if (data && resampledBuffer) { - *resampledBuffer = (byte *)ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); + *resampledBuffer = (byte *)Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); ResampleTexture (*data, width, height, *resampledBuffer, scaled_width, scaled_height); *data = *resampledBuffer; } @@ -2102,7 +2102,7 @@ static void Upload32( byte *data, int width, int height, imgType_t type, int fla RawImage_ScaleToPower2(&data, &width, &height, &scaled_width, &scaled_height, type, flags, &resampledBuffer); } - scaledBuffer = (byte *)ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + scaledBuffer = (byte *)Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); // // scan the texture for each channel's max values @@ -2221,9 +2221,9 @@ static void Upload32( byte *data, int width, int height, imgType_t type, int fla GL_CheckErrors(); if ( scaledBuffer != 0 ) - ri.Hunk_FreeTempMemory( scaledBuffer ); + Hunk_FreeTempMemory( scaledBuffer ); if ( resampledBuffer != 0 ) - ri.Hunk_FreeTempMemory( resampledBuffer ); + Hunk_FreeTempMemory( resampledBuffer ); } @@ -2610,7 +2610,7 @@ void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int h RawImage_ScaleToPower2(&pic, &width, &height, &scaled_width, &scaled_height, image->type, image->flags, &resampledBuffer); - scaledBuffer = (byte *)ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + scaledBuffer = (byte *)Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); GL_SelectTexture( image->TMU ); GL_Bind(image); @@ -2676,9 +2676,9 @@ void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int h GL_CheckErrors(); if ( scaledBuffer != 0 ) - ri.Hunk_FreeTempMemory( scaledBuffer ); + Hunk_FreeTempMemory( scaledBuffer ); if ( resampledBuffer != 0 ) - ri.Hunk_FreeTempMemory( resampledBuffer ); + Hunk_FreeTempMemory( resampledBuffer ); } image_t* R_GetLoadedImage(const char *name, int flags) { @@ -2818,7 +2818,7 @@ image_t *R_BuildSDRSpecGlossImage(shaderStage_t *stage, const char *specImageNam if (specPic == NULL) return NULL; - byte *sdrSpecPic = (byte *)ri.Hunk_AllocateTempMemory(sizeof(unsigned) * specWidth * specHeight); + byte *sdrSpecPic = (byte *)Hunk_AllocateTempMemory(sizeof(unsigned) * specWidth * specHeight); vec3_t currentColor; for (int i = 0; i < specWidth * specHeight * 4; i += 4) { @@ -2835,7 +2835,7 @@ image_t *R_BuildSDRSpecGlossImage(shaderStage_t *stage, const char *specImageNam sdrSpecPic[i + 2] = FloatToByte(currentColor[2] * ratio); sdrSpecPic[i + 3] = specPic[i + 3]; } - ri.Hunk_FreeTempMemory(specPic); + Hunk_FreeTempMemory(specPic); return R_CreateImage(sdrName, sdrSpecPic, specWidth, specHeight, IMGTYPE_COLORALPHA, flags & ~IMGFLAG_SRGB, 0); } @@ -2863,7 +2863,7 @@ static void R_CreateNormalMap ( const char *name, byte *pic, int width, int heig normalWidth = width; normalHeight = height; - normalPic = (byte *)Z_Malloc(width * height * 4, TAG_GENERAL); + normalPic = (byte *)R_Malloc(width * height * 4, TAG_GENERAL); RGBAtoNormal(pic, normalPic, width, height, (qboolean)(flags & IMGFLAG_CLAMPTOEDGE)); #if 1 @@ -3148,7 +3148,7 @@ static void R_CreateFogImage( void ) { float d; float borderColor[4]; - data = (byte *)ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); + data = (byte *)Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); // S is distance, T is depth for (x=0 ; x. static void* R_LocalMalloc(size_t size) { - return ri.Hunk_AllocateTempMemory(size); + return Hunk_AllocateTempMemory(size); } static void* R_LocalReallocSized(void *ptr, size_t old_size, size_t new_size) { - void *mem = ri.Hunk_AllocateTempMemory(new_size); + void *mem = Hunk_AllocateTempMemory(new_size); if (ptr) { memcpy(mem, ptr, old_size); - ri.Hunk_FreeTempMemory(ptr); + Hunk_FreeTempMemory(ptr); } return mem; } static void R_LocalFree(void *ptr) { if (ptr) - ri.Hunk_FreeTempMemory(ptr); + Hunk_FreeTempMemory(ptr); } #define STBI_MALLOC R_LocalMalloc diff --git a/codemp/rd-rend2/tr_light.cpp b/shared/rd-rend2/tr_light.cpp similarity index 99% rename from codemp/rd-rend2/tr_light.cpp rename to shared/rd-rend2/tr_light.cpp index abd63d36bf..b162e75595 100644 --- a/codemp/rd-rend2/tr_light.cpp +++ b/shared/rd-rend2/tr_light.cpp @@ -455,7 +455,10 @@ int R_LightDirForPoint( vec3_t point, vec3_t lightDir, vec3_t normal, world_t *w trRefEntity_t ent; if ( world->lightGridData == NULL ) - return qfalse; + { + VectorCopy(normal, lightDir); + return qfalse; + } Com_Memset(&ent, 0, sizeof(ent)); VectorCopy( point, ent.e.origin ); diff --git a/codemp/rd-rend2/tr_local.h b/shared/rd-rend2/tr_local.h similarity index 98% rename from codemp/rd-rend2/tr_local.h rename to shared/rd-rend2/tr_local.h index 78f602709b..c28f54d13a 100644 --- a/codemp/rd-rend2/tr_local.h +++ b/shared/rd-rend2/tr_local.h @@ -41,6 +41,20 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include +#ifdef REND2_SP +typedef enum { + h_high, + h_low, + h_dontcare +} ha_pref; +void* Hunk_Alloc(int size, ha_pref preference); +void* Hunk_AllocateTempMemory(int size); +void Hunk_FreeTempMemory(void* buf); +#else +void* R_Malloc(int iSize, memtag_t eTag); +void* R_Malloc(int iSize, memtag_t eTag, qboolean bZeroit); +#endif + #define GL_INDEX_TYPE GL_UNSIGNED_INT typedef unsigned int glIndex_t; @@ -57,6 +71,7 @@ typedef unsigned int glIndex_t; #define MAX_VBOS 4096 #define MAX_IBOS 4096 #define MAX_G2_BONES 72 +#define MAX_GPU_FOGS 24 #define MAX_CALC_PSHADOWS 64 #define MAX_DRAWN_PSHADOWS 32 // do not increase past 32, because bit flags are used on surfaces @@ -342,7 +357,7 @@ enum // GPU vertex animations and some extra sprite info ATTR_INDEX_POSITION2, -#ifdef REND2_SP +#ifdef REND2_SP_MD3 ATTR_INDEX_TANGENT2, ATTR_INDEX_NORMAL2, #endif // REND2_SP @@ -591,6 +606,8 @@ typedef enum { TCGEN_LIGHTMAP3, TCGEN_TEXTURE, TCGEN_ENVIRONMENT_MAPPED, + TCGEN_ENVIRONMENT_MAPPED_SP, + TCGEN_ENVIRONMENT_MAPPED_SP_FP, TCGEN_FOG, TCGEN_VECTOR // S and T from world coordinates } texCoordGen_t; @@ -752,7 +769,7 @@ struct FogsBlock int numFogs; float pad0[3]; - Fog fogs[16]; + Fog fogs[MAX_GPU_FOGS]; }; struct EntityBlock @@ -1140,7 +1157,7 @@ enum // for .md3 interpolation and some sprite data ATTR_POSITION2 = 0x1000, -#ifdef REND2_SP +#ifdef REND2_SP_MD3 ATTR_TANGENT2 = 0x2000, ATTR_NORMAL2 = 0x4000, #endif // REND2_SP @@ -1159,7 +1176,7 @@ enum ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS | ATTR_POSITION2 -#ifdef REND2_SP +#ifdef REND2_SP_MD3 | ATTR_TANGENT2 | ATTR_NORMAL2 @@ -1175,7 +1192,7 @@ enum GENERICDEF_USE_SKELETAL_ANIMATION = 0x0010, GENERICDEF_USE_GLOW_BUFFER = 0x0020, GENERICDEF_USE_ALPHA_TEST = 0x0040, -#ifdef REND2_SP +#ifdef REND2_SP_MD3 GENERICDEF_USE_VERTEX_ANIMATION = 0x0080, GENERICDEF_ALL = 0x00FF, #else @@ -1191,7 +1208,7 @@ enum FOGDEF_USE_SKELETAL_ANIMATION = 0x0002, FOGDEF_USE_ALPHA_TEST = 0x0004, FOGDEF_USE_FALLBACK_GLOBAL_FOG = 0x0008, -#ifdef REND2_SP +#ifdef REND2_SP_MD3 FOGDEF_USE_VERTEX_ANIMATION = 0x0010, FOGDEF_ALL = 0x001F, #else @@ -1209,7 +1226,7 @@ enum REFRACTIONDEF_USE_SKELETAL_ANIMATION = 0x0008, REFRACTIONDEF_USE_ALPHA_TEST = 0x0010, REFRACTIONDEF_USE_SRGB_TRANSFORM = 0x0020, -#ifdef REND2_SP +#ifdef REND2_SP_MD3 REFRACTIONDEF_USE_VERTEX_ANIMATION = 0x0040, REFRACTIONDEF_ALL = 0x007F, #else @@ -1237,7 +1254,7 @@ enum LIGHTDEF_USE_LIGHT_VECTOR | LIGHTDEF_USE_LIGHT_VERTEX, -#ifdef REND2_SP +#ifdef REND2_SP_MD3 LIGHTDEF_USE_VERTEX_ANIMATION = 0x0200, LIGHTDEF_ALL = 0x03FF, #else @@ -1497,11 +1514,27 @@ typedef struct { float autoExposureMinMax[2]; float toneMinAvgMaxLinear[3]; +#ifdef REND2_SP + bool doLAGoggles; + bool doFullbright; +#endif } trRefdef_t; //================================================================================= +// skins allow models to be retextured without modifying the model file +typedef struct { + char name[MAX_QPATH]; + shader_t *shader; +} skinSurface_t; + +typedef struct skin_s { + char name[MAX_QPATH]; // game path, including extension + int numSurfaces; + skinSurface_t *surfaces[128]; +} skin_t; + typedef struct { int originalBrushNumber; @@ -2909,12 +2942,15 @@ void GL_Draw( GLenum primitiveType, int firstVertex, int numVertices, int numIns extern glconfig_t glConfig; extern glconfigExt_t glConfigExt; -typedef _skinSurface_t skinSurface_t; - void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); void RE_UploadCinematic (int cols, int rows, const byte *data, int client, qboolean dirty); void RE_SetRangedFog ( float range ); - +#ifdef REND2_SP +byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen); +void RE_GetScreenShot(byte *data, int w, int h); +byte* RE_TempRawImage_ReadFromFile(const char *psLocalFilename, int *piWidth, int *piHeight, byte *pbReSampleBuffer, qboolean qbVertFlip); +void RE_TempRawImage_CleanUp(); +#endif void RE_BeginRegistration( glconfig_t *glconfig ); void RE_LoadWorldMap( const char *mapname ); @@ -2926,6 +2962,10 @@ qhandle_t RE_RegisterSkin( const char *name ); void RE_Shutdown(qboolean destroyWindow, qboolean restarting); world_t *R_LoadBSP(const char *name, int *bspIndex = nullptr); +#ifdef REND2_SP +int RE_GetAnimationCFG(const char* psCFGFilename, char* psDest, int iDestSize); +#endif + qboolean R_GetEntityToken( char *buffer, int size ); model_t *R_AllocModel( void ); @@ -3035,7 +3075,7 @@ struct shaderCommands_s float shaderTime; int fogNum; int cubemapIndex; -#ifdef REND2_SP_MAYBE +#ifdef REND2_SP_GORE bool scale; // uses texCoords[input->firstIndex] for storage bool fade; // uses svars.colors[input->firstIndex] for storage #endif @@ -3104,8 +3144,11 @@ void R_AddBrushModelSurfaces( trRefEntity_t *e, int entityNum ); void R_AddWorldSurfaces( viewParms_t *viewParms, trRefdef_t *refdef ); void R_MarkLeaves(void); void R_RecursiveWorldNode(mnode_t *node, int planeBits, int dlightBits, int pshadowBits); +#ifndef REND2_SP qboolean R_inPVS( const vec3_t p1, const vec3_t p2, byte *mask ); - +#else +qboolean R_inPVS(vec3_t p1, vec3_t p2); +#endif /* ============================================================ @@ -3136,6 +3179,9 @@ int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, ve int R_LightDirForPoint( vec3_t point, vec3_t lightDir, vec3_t normal, world_t *world ); int R_DLightsForPoint(const vec3_t point, const float radius); int R_CubemapForPoint( const vec3_t point ); +#ifdef REND2_SP +qboolean RE_GetLighting(const vec3_t origin, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir); +#endif /* ============================================================ @@ -3289,8 +3335,10 @@ void R_InitNextFrame( void ); void RE_ClearScene( void ); void RE_AddRefEntityToScene( const refEntity_t *ent ); +#ifndef REND2_SP void RE_AddMiniRefEntityToScene( const miniRefEntity_t *miniRefEnt ); -void RE_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ); +#endif +void RE_AddPolyToScene(qhandle_t hShader, int numVerts, const polyVert_t *verts, int num = 1); void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ); void RE_BeginScene( const refdef_t *fd ); @@ -3314,8 +3362,6 @@ UNCOMPRESSING BONES #define MC_SCALE_Y (1.0f/64) #define MC_SCALE_Z (1.0f/64) -void MC_UnCompress(float mat[3][4],const unsigned char * comp); - /* ============================================================= @@ -3519,6 +3565,14 @@ typedef struct rotatePicCommand_s { float a; } rotatePicCommand_t; +#ifdef REND2_SP +typedef struct scissorCommand_s { + int commandId; + float x, y; + float w, h; +} scissorCommand_t; +#endif + typedef struct drawSurfsCommand_s { int commandId; trRefdef_t refdef; @@ -3596,6 +3650,7 @@ typedef enum { RC_SWAP_BUFFERS, RC_SCREENSHOT, RC_VIDEOFRAME, + RC_SCISSOR, RC_COLORMASK, RC_CLEARDEPTH, RC_CONVOLVECUBEMAP, @@ -3701,6 +3756,10 @@ void RE_SetColor( const float *rgba ); void RE_StretchPic ( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ); void RE_RotatePic ( float x, float y, float w, float h, float s1, float t1, float s2, float t2, float a, qhandle_t hShader ); void RE_RotatePic2 ( float x, float y, float w, float h, float s1, float t1, float s2, float t2,float a, qhandle_t hShader ); +#ifdef REND2_SP +void RE_LAGoggles(void); +void RE_Scissor(float x, float y, float w, float h); +#endif void RE_BeginFrame( stereoFrame_t stereoFrame ); void R_NewFrameSync(); void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); @@ -3708,6 +3767,10 @@ void RE_TakeVideoFrame( int width, int height, byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); // tr_ghoul2.cpp +#ifdef REND2_SP +void Create_Matrix(const float* angle, mdxaBone_t* matrix); +void G2_SetSurfaceOnOffFromSkin(CGhoul2Info *ghlInfo, qhandle_t renderSkin); //tr_ghoul2.cpp +#endif void Mat3x4_Multiply(mdxaBone_t *out, const mdxaBone_t *in2, const mdxaBone_t *in); void Mat3x4_Scale( mdxaBone_t *result, const mdxaBone_t *lhs, const float scale ); void Mat3x4_Lerp( diff --git a/codemp/rd-rend2/tr_main.cpp b/shared/rd-rend2/tr_main.cpp similarity index 97% rename from codemp/rd-rend2/tr_main.cpp rename to shared/rd-rend2/tr_main.cpp index 8245479073..5fcf322daf 100644 --- a/codemp/rd-rend2/tr_main.cpp +++ b/shared/rd-rend2/tr_main.cpp @@ -25,9 +25,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "tr_weather.h" #include // memcpy - +#ifndef REND2_SP #include "ghoul2/g2_local.h" - +#endif trGlobals_t tr; static float s_flipMatrix[16] = { @@ -820,10 +820,12 @@ static void R_SetFarClip( viewParms_t *viewParms, const trRefdef_t *refdef ) if (refdef != NULL) { if (refdef->rdflags & RDF_NOWORLDMODEL) { +#ifndef REND2_SP // override the zfar then if (refdef->rdflags & RDF_AUTOMAP) viewParms->zFar = 32768.0f; else +#endif viewParms->zFar = 2048.0f; return; } @@ -1653,28 +1655,65 @@ See if a sprite is inside a fog volume */ int R_SpriteFogNum( trRefEntity_t *ent ) { int i, j; + float frameRadius; fog_t *fog; + vec3_t localOrigin; if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { return 0; } - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + VectorCopy(ent->e.origin, localOrigin); + frameRadius = ent->e.radius; +#ifndef REND2_SP + for (i = 1; i < tr.world->numfogs; i++) { fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) { + for (j = 0; j < 3; j++) { + if (localOrigin[j] - frameRadius >= fog->bounds[1][j]) { break; } - if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) { + if (localOrigin[j] + frameRadius <= fog->bounds[0][j]) { break; } } - if ( j == 3 ) { + if (j == 3) { return i; } } - return 0; +#else + int partialFog = 0; + for (i = 1; i < tr.world->numfogs; i++) { + fog = &tr.world->fogs[i]; + if (localOrigin[0] - frameRadius >= fog->bounds[0][0] + && localOrigin[0] + frameRadius <= fog->bounds[1][0] + && localOrigin[1] - frameRadius >= fog->bounds[0][1] + && localOrigin[1] + frameRadius <= fog->bounds[1][1] + && localOrigin[2] - frameRadius >= fog->bounds[0][2] + && localOrigin[2] + frameRadius <= fog->bounds[1][2]) + {//totally inside it + return i; + break; + } + if ((localOrigin[0] - frameRadius >= fog->bounds[0][0] && localOrigin[1] - frameRadius >= fog->bounds[0][1] && localOrigin[2] - frameRadius >= fog->bounds[0][2] && + localOrigin[0] - frameRadius <= fog->bounds[1][0] && localOrigin[1] - frameRadius <= fog->bounds[1][1] && localOrigin[2] - frameRadius <= fog->bounds[1][2]) || + (localOrigin[0] + frameRadius >= fog->bounds[0][0] && localOrigin[1] + frameRadius >= fog->bounds[0][1] && localOrigin[2] + frameRadius >= fog->bounds[0][2] && + localOrigin[0] + frameRadius <= fog->bounds[1][0] && localOrigin[1] + frameRadius <= fog->bounds[1][1] && localOrigin[2] + frameRadius <= fog->bounds[1][2])) + {//partially inside it + //if (tr.refdef.fogIndex == i || R_FogParmsMatch(tr.refdef.fogIndex, i)) + //{//take new one only if it's the same one that the viewpoint is in + // return i; + // break; + //} + //else + if (!partialFog) + {//first partialFog + partialFog = i; + } + } + } + return partialFog; +#endif } /* @@ -1741,7 +1780,9 @@ static void R_RadixSort( drawSurf_t *source, int size ) bool R_IsPostRenderEntity ( const trRefEntity_t *refEntity ) { return (refEntity->e.renderfx & RF_DISTORTION) || +#ifndef REND2_SP (refEntity->e.renderfx & RF_FORCEPOST) || +#endif (refEntity->e.renderfx & RF_FORCE_ENT_ALPHA); } @@ -1786,12 +1827,17 @@ void R_AddDrawSurf( { int index; drawSurf_t *surf; - +#ifndef REND2_SP if (tr.refdef.rdflags & RDF_NOFOG) { fogIndex = 0; } - +#else + if (tr.refdef.doLAGoggles) + { + fogIndex = tr.world->numfogs; + } +#endif if ( (shader->surfaceFlags & SURF_FORCESIGHT) && !(tr.refdef.rdflags & RDF_ForceSightOn) ) { //if shader is only seen with ForceSight and we don't have ForceSight on, then don't draw return; @@ -1871,7 +1917,12 @@ static void R_AddEntitySurface(const trRefdef_t *refdef, trRefEntity_t *ent, int case RT_ORIENTED_QUAD: case RT_ELECTRICITY: case RT_LINE: +#ifndef REND2_SP case RT_ORIENTEDLINE: +#else + case RT_LATHE: + case RT_CLOUDS: +#endif case RT_CYLINDER: case RT_SABER_GLOW: // self blood sprites, talk balloons, etc should not be drawn in the primary @@ -1952,6 +2003,7 @@ static void R_AddEntitySurface(const trRefdef_t *refdef, trRefEntity_t *ent, int } } break; +#ifndef REND2_SP case RT_ENT_CHAIN: shader = R_GetShaderByHandle(ent->e.customShader); R_AddDrawSurf( @@ -1963,6 +2015,7 @@ static void R_AddEntitySurface(const trRefdef_t *refdef, trRefEntity_t *ent, int R_IsPostRenderEntity(ent), 0 /* cubeMap */ ); break; +#endif default: ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" ); } @@ -2260,7 +2313,7 @@ void R_SetupPshadowMaps(trRefdef_t *refdef) { trRefEntity_t *ent = &refdef->entities[i]; - if((ent->e.renderfx & (RF_FIRST_PERSON | RF_NOSHADOW))) + if((ent->e.renderfx & (RF_FIRST_PERSON | RF_NOSHADOW | RF_DEPTHHACK))) continue; //if((ent->e.renderfx & RF_THIRD_PERSON)) @@ -2290,6 +2343,13 @@ void R_SetupPshadowMaps(trRefdef_t *refdef) { if (ent->e.ghoul2 && G2API_HaveWeGhoul2Models(*((CGhoul2Info_v *)ent->e.ghoul2))) { + shader_t *cust_shader = nullptr; + if (ent->e.customShader) + { + cust_shader = R_GetShaderByHandle(ent->e.customShader); + if (cust_shader->sort != SS_OPAQUE) + continue; + } // scale the radius if needed float largestScale = ent->e.modelScale[0]; if (ent->e.modelScale[1] > largestScale) @@ -2488,7 +2548,7 @@ void R_SetupViewParms(const trRefdef_t *refdef) tr.viewParms.viewportX = refdef->x; // Shoud be just refef->y but this flips the menu orientation for models, so its actually needed like this - if (!tr.world) + if (!tr.world || refdef->rdflags & RDF_NOWORLDMODEL) tr.viewParms.viewportY = glConfig.vidHeight - (refdef->y + refdef->height); else tr.viewParms.viewportY = refdef->y; @@ -2573,7 +2633,9 @@ qboolean R_AddPortalView(const trRefdef_t *refdef) case RT_ORIENTED_QUAD: case RT_ELECTRICITY: case RT_LINE: +#ifndef REND2_SP case RT_ORIENTEDLINE: +#endif case RT_CYLINDER: case RT_SABER_GLOW: break; @@ -2620,8 +2682,10 @@ qboolean R_AddPortalView(const trRefdef_t *refdef) } } break; +#ifndef REND2_SP case RT_ENT_CHAIN: break; +#endif default: break; } diff --git a/codemp/rd-rend2/tr_marks.cpp b/shared/rd-rend2/tr_marks.cpp similarity index 100% rename from codemp/rd-rend2/tr_marks.cpp rename to shared/rd-rend2/tr_marks.cpp diff --git a/codemp/rd-rend2/tr_mesh.cpp b/shared/rd-rend2/tr_mesh.cpp similarity index 82% rename from codemp/rd-rend2/tr_mesh.cpp rename to shared/rd-rend2/tr_mesh.cpp index e242d79ee0..0344f6318e 100644 --- a/codemp/rd-rend2/tr_mesh.cpp +++ b/shared/rd-rend2/tr_mesh.cpp @@ -156,6 +156,31 @@ static int R_CullModel( mdvModel_t *model, trRefEntity_t *ent ) { } } +/* +================= +RE_GetModelBounds + + Returns the bounds of the current model + (qhandle_t)hModel and (int)frame need to be set +================= +*/ + +void RE_GetModelBounds(refEntity_t *refEnt, vec3_t bounds1, vec3_t bounds2) +{ + mdvFrame_t *frame; + md3Header_t *header; + model_t *model; + + assert(refEnt); + + model = R_GetModelByHandle(refEnt->hModel); + assert(model); + frame = &model->data.mdv[0]->frames[refEnt->frame]; + assert(frame); + + VectorCopy(frame->bounds[0], bounds1); + VectorCopy(frame->bounds[1], bounds2); +} /* ================= @@ -245,6 +270,7 @@ R_ComputeFogNum */ int R_ComputeFogNum( mdvModel_t *model, trRefEntity_t *ent ) { int i, j; + float frameRadius; fog_t *fog; mdvFrame_t *mdvFrame; vec3_t localOrigin; @@ -256,13 +282,15 @@ int R_ComputeFogNum( mdvModel_t *model, trRefEntity_t *ent ) { // FIXME: non-normalized axis issues mdvFrame = model->frames + ent->e.frame; VectorAdd( ent->e.origin, mdvFrame->localOrigin, localOrigin ); + frameRadius = mdvFrame->radius; +#ifndef REND2_SP for ( i = 1 ; i < tr.world->numfogs ; i++ ) { fog = &tr.world->fogs[i]; for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - mdvFrame->radius >= fog->bounds[1][j] ) { + if ( localOrigin[j] - frameRadius >= fog->bounds[1][j] ) { break; } - if ( localOrigin[j] + mdvFrame->radius <= fog->bounds[0][j] ) { + if ( localOrigin[j] + frameRadius <= fog->bounds[0][j] ) { break; } } @@ -270,8 +298,40 @@ int R_ComputeFogNum( mdvModel_t *model, trRefEntity_t *ent ) { return i; } } - return 0; +#else + int partialFog = 0; + for (i = 1; i < tr.world->numfogs; i++) { + fog = &tr.world->fogs[i]; + if (localOrigin[0] - frameRadius >= fog->bounds[0][0] + && localOrigin[0] + frameRadius <= fog->bounds[1][0] + && localOrigin[1] - frameRadius >= fog->bounds[0][1] + && localOrigin[1] + frameRadius <= fog->bounds[1][1] + && localOrigin[2] - frameRadius >= fog->bounds[0][2] + && localOrigin[2] + frameRadius <= fog->bounds[1][2]) + {//totally inside it + return i; + break; + } + if ((localOrigin[0] - frameRadius >= fog->bounds[0][0] && localOrigin[1] - frameRadius >= fog->bounds[0][1] && localOrigin[2] - frameRadius >= fog->bounds[0][2] && + localOrigin[0] - frameRadius <= fog->bounds[1][0] && localOrigin[1] - frameRadius <= fog->bounds[1][1] && localOrigin[2] - frameRadius <= fog->bounds[1][2]) || + (localOrigin[0] + frameRadius >= fog->bounds[0][0] && localOrigin[1] + frameRadius >= fog->bounds[0][1] && localOrigin[2] + frameRadius >= fog->bounds[0][2] && + localOrigin[0] + frameRadius <= fog->bounds[1][0] && localOrigin[1] + frameRadius <= fog->bounds[1][1] && localOrigin[2] + frameRadius <= fog->bounds[1][2])) + {//partially inside it + //if (tr.refdef.fogIndex == i || R_FogParmsMatch(tr.refdef.fogIndex, i)) + //{//take new one only if it's the same one that the viewpoint is in + // return i; + // break; + //} + //else + if (!partialFog) + {//first partialFog + partialFog = i; + } + } + } + return partialFog; +#endif } /* diff --git a/codemp/rd-rend2/tr_model.cpp b/shared/rd-rend2/tr_model.cpp similarity index 95% rename from codemp/rd-rend2/tr_model.cpp rename to shared/rd-rend2/tr_model.cpp index 0f9d746110..f56719864e 100644 --- a/codemp/rd-rend2/tr_model.cpp +++ b/shared/rd-rend2/tr_model.cpp @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "tr_local.h" #include "tr_cache.h" #include +#include #define LL(x) x=LittleLong(x) @@ -247,7 +248,7 @@ model_t *R_AllocModel( void ) { return NULL; } - mod = (model_t *)ri.Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), h_low ); + mod = (model_t *)Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), h_low ); mod->index = tr.numModels; tr.models[tr.numModels] = mod; tr.numModels++; @@ -315,6 +316,7 @@ qhandle_t RE_RegisterModel( const char *name ) { if( ( hModel = CModelCache->GetModelHandle( name ) ) != -1 ) return hModel; +#ifndef REND2_SP if ( name[0] == '*' ) { if ( strcmp (name, "*default.gla") != 0 ) @@ -322,6 +324,7 @@ qhandle_t RE_RegisterModel( const char *name ) { return 0; } } +#endif if( name[0] == '#' ) { @@ -522,7 +525,7 @@ qboolean R_LoadMDXM_Server( model_t *mod, void *buffer, const char *mod_name, qb qboolean bAlreadyFound = qfalse; mdxm = (mdxmHeader_t*)CModelCache->Allocate( size, buffer, mod_name, &bAlreadyFound, TAG_MODEL_GLM ); - mod->data.glm = (mdxmData_t *)ri.Hunk_Alloc (sizeof (mdxmData_t), h_low); + mod->data.glm = (mdxmData_t *)Hunk_Alloc (sizeof (mdxmData_t), h_low); mod->data.glm->header = mdxm; assert(bAlreadyCached == bAlreadyFound); // I should probably eliminate 'bAlreadyFound', but wtf? @@ -743,7 +746,11 @@ qhandle_t RE_RegisterServerModel( const char *name ) { if (!r_noServerGhoul2) { //keep it from choking when it gets to these checks in the g2 code. Registering all r_ cvars for the server would be a Bad Thing though. +#ifndef REND2_SP r_noServerGhoul2 = ri.Cvar_Get( "r_noserverghoul2", "0", 0, ""); +#else + r_noServerGhoul2 = ri.Cvar_Get("r_noghoul2", "0", 0); +#endif } if ( !name || !name[0] ) { @@ -859,10 +866,10 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN mod->type = MOD_MESH; size = LittleLong(md3Model->ofsEnd); mod->dataSize += size; - //mdvModel = mod->mdv[lod] = (mdvModel_t *)ri.Hunk_Alloc(sizeof(mdvModel_t), h_low); + //mdvModel = mod->mdv[lod] = (mdvModel_t *)Hunk_Alloc(sizeof(mdvModel_t), h_low); qboolean bAlreadyFound = qfalse; md3Model = (md3Header_t *)CModelCache->Allocate(size, buffer, modName, &bAlreadyFound, TAG_MODEL_MD3); - mdvModel = mod->data.mdv[lod] = (mdvModel_t *)ri.Hunk_Alloc(sizeof(*mdvModel), h_low); + mdvModel = mod->data.mdv[lod] = (mdvModel_t *)Hunk_Alloc(sizeof(*mdvModel), h_low); // Com_Memcpy(mod->md3[lod], buffer, LittleLong(md3Model->ofsEnd)); if( !bAlreadyFound ) @@ -890,7 +897,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN // swap all the frames mdvModel->numFrames = md3Model->numFrames; - mdvModel->frames = frame = (mdvFrame_t *)ri.Hunk_Alloc(sizeof(*frame) * md3Model->numFrames, h_low); + mdvModel->frames = frame = (mdvFrame_t *)Hunk_Alloc(sizeof(*frame) * md3Model->numFrames, h_low); md3Frame = (md3Frame_t *) ((byte *) md3Model + md3Model->ofsFrames); for(i = 0; i < md3Model->numFrames; i++, frame++, md3Frame++) @@ -906,7 +913,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN // swap all the tags mdvModel->numTags = md3Model->numTags; - mdvModel->tags = tag = (mdvTag_t *)ri.Hunk_Alloc(sizeof(*tag) * (md3Model->numTags * md3Model->numFrames), h_low); + mdvModel->tags = tag = (mdvTag_t *)Hunk_Alloc(sizeof(*tag) * (md3Model->numTags * md3Model->numFrames), h_low); md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); for(i = 0; i < md3Model->numTags * md3Model->numFrames; i++, tag++, md3Tag++) @@ -921,7 +928,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN } - mdvModel->tagNames = tagName = (mdvTagName_t *)ri.Hunk_Alloc(sizeof(*tagName) * (md3Model->numTags), h_low); + mdvModel->tagNames = tagName = (mdvTagName_t *)Hunk_Alloc(sizeof(*tagName) * (md3Model->numTags), h_low); md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); for(i = 0; i < md3Model->numTags; i++, tagName++, md3Tag++) @@ -931,7 +938,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN // swap all the surfaces mdvModel->numSurfaces = md3Model->numSurfaces; - mdvModel->surfaces = surf = (mdvSurface_t *)ri.Hunk_Alloc(sizeof(*surf) * md3Model->numSurfaces, h_low); + mdvModel->surfaces = surf = (mdvSurface_t *)Hunk_Alloc(sizeof(*surf) * md3Model->numSurfaces, h_low); md3Surf = (md3Surface_t *) ((byte *) md3Model + md3Model->ofsSurfaces); for(i = 0; i < md3Model->numSurfaces; i++) @@ -985,7 +992,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN // register the shaders surf->numShaderIndexes = md3Surf->numShaders; - surf->shaderIndexes = shaderIndex = (int *)ri.Hunk_Alloc(sizeof(*shaderIndex) * md3Surf->numShaders, h_low); + surf->shaderIndexes = shaderIndex = (int *)Hunk_Alloc(sizeof(*shaderIndex) * md3Surf->numShaders, h_low); md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); for(j = 0; j < md3Surf->numShaders; j++, shaderIndex++, md3Shader++) @@ -1005,7 +1012,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN // swap all the triangles surf->numIndexes = md3Surf->numTriangles * 3; - surf->indexes = tri = (glIndex_t *)ri.Hunk_Alloc(sizeof(*tri) * 3 * md3Surf->numTriangles, h_low); + surf->indexes = tri = (glIndex_t *)Hunk_Alloc(sizeof(*tri) * 3 * md3Surf->numTriangles, h_low); md3Tri = (md3Triangle_t *) ((byte *) md3Surf + md3Surf->ofsTriangles); for(j = 0; j < md3Surf->numTriangles; j++, tri += 3, md3Tri++) @@ -1017,7 +1024,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN // swap all the XyzNormals surf->numVerts = md3Surf->numVerts; - surf->verts = v = (mdvVertex_t *)ri.Hunk_Alloc(sizeof(*v) * (md3Surf->numVerts * md3Surf->numFrames), h_low); + surf->verts = v = (mdvVertex_t *)Hunk_Alloc(sizeof(*v) * (md3Surf->numVerts * md3Surf->numFrames), h_low); md3xyz = (md3XyzNormal_t *) ((byte *) md3Surf + md3Surf->ofsXyzNormals); for(j = 0; j < md3Surf->numVerts * md3Surf->numFrames; j++, md3xyz++, v++) @@ -1046,7 +1053,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN } // swap all the ST - surf->st = st = (mdvSt_t *)ri.Hunk_Alloc(sizeof(*st) * md3Surf->numVerts, h_low); + surf->st = st = (mdvSt_t *)Hunk_Alloc(sizeof(*st) * md3Surf->numVerts, h_low); md3st = (md3St_t *) ((byte *) md3Surf + md3Surf->ofsSt); for(j = 0; j < md3Surf->numVerts; j++, md3st++, st++) @@ -1064,7 +1071,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN srfVBOMDVMesh_t *vboSurf; mdvModel->numVBOSurfaces = mdvModel->numSurfaces; - mdvModel->vboSurfaces = (srfVBOMDVMesh_t *)ri.Hunk_Alloc(sizeof(*mdvModel->vboSurfaces) * mdvModel->numSurfaces, h_low); + mdvModel->vboSurfaces = (srfVBOMDVMesh_t *)Hunk_Alloc(sizeof(*mdvModel->vboSurfaces) * mdvModel->numSurfaces, h_low); vboSurf = mdvModel->vboSurfaces; surf = mdvModel->surfaces; @@ -1082,8 +1089,8 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN int numIndexes = 0; // +1 to add total vertex count - int *baseVertexes = (int *)ri.Hunk_AllocateTempMemory(sizeof(int) * (mdvModel->numSurfaces + 1)); - int *indexOffsets = (int *)ri.Hunk_AllocateTempMemory(sizeof(int) * mdvModel->numSurfaces); + int *baseVertexes = (int *)Hunk_AllocateTempMemory(sizeof(int) * (mdvModel->numSurfaces + 1)); + int *indexOffsets = (int *)Hunk_AllocateTempMemory(sizeof(int) * mdvModel->numSurfaces); // Calculate the required size of the vertex buffer. for (int n = 0; n < mdvModel->numSurfaces; n++, surf++) @@ -1102,7 +1109,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN dataSize += numVerts * sizeof(*tangents); // Allocate and write to memory - data = (byte *)ri.Hunk_AllocateTempMemory(dataSize); + data = (byte *)Hunk_AllocateTempMemory(dataSize); ofsPosition = stride; verts = (vec3_t *)(data + ofsPosition); @@ -1121,13 +1128,13 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN stride += sizeof(*tangents); // Fill in the index buffer and compute tangents - glIndex_t *indices = (glIndex_t *)ri.Hunk_AllocateTempMemory(sizeof(glIndex_t) * numIndexes); + glIndex_t *indices = (glIndex_t *)Hunk_AllocateTempMemory(sizeof(glIndex_t) * numIndexes); glIndex_t *index = indices; surf = mdvModel->surfaces; for (i = 0; i < mdvModel->numSurfaces; i++, surf++) { - uint32_t *tangentsf = (uint32_t *)ri.Hunk_AllocateTempMemory(sizeof(uint32_t) * surf->numVerts); + uint32_t *tangentsf = (uint32_t *)Hunk_AllocateTempMemory(sizeof(uint32_t) * surf->numVerts); R_CalcMikkTSpaceMD3Surface( surf->numIndexes / 3, surf->verts, @@ -1153,7 +1160,7 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN normals = (uint32_t *)((byte *)normals + stride); tangents = (uint32_t *)((byte *)tangents + stride); } - ri.Hunk_FreeTempMemory(tangentsf); + Hunk_FreeTempMemory(tangentsf); st = surf->st; for (j = 0; j < surf->numVerts; j++, st++) { @@ -1169,8 +1176,8 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN VBO_t *vbo = R_CreateVBO(data, dataSize, VBO_USAGE_STATIC); IBO_t *ibo = R_CreateIBO((byte *)indices, sizeof(glIndex_t) * numIndexes, VBO_USAGE_STATIC); - ri.Hunk_FreeTempMemory(data); - ri.Hunk_FreeTempMemory(indices); + Hunk_FreeTempMemory(data); + Hunk_FreeTempMemory(indices); vbo->offsets[ATTR_INDEX_POSITION] = ofsPosition; vbo->offsets[ATTR_INDEX_NORMAL] = ofsNormals; @@ -1203,8 +1210,8 @@ static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, const char *modN vboSurf->numIndexes = surf->numIndexes; } - ri.Hunk_FreeTempMemory(indexOffsets); - ri.Hunk_FreeTempMemory(baseVertexes); + Hunk_FreeTempMemory(indexOffsets); + Hunk_FreeTempMemory(baseVertexes); } return qtrue; @@ -1273,7 +1280,7 @@ static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char } mod->dataSize += size; - mod->data.mdr = mdr = (mdrHeader_t*)ri.Hunk_Alloc( size, h_low ); + mod->data.mdr = mdr = (mdrHeader_t*)Hunk_Alloc( size, h_low ); // Copy all the values over from the file and fix endian issues in the process, if necessary. @@ -1585,6 +1592,18 @@ void RE_BeginRegistration( glconfig_t *glconfigOut ) { void R_SVModelInit() { +#ifdef REND2_SP + tr.numShaders = 0; + tr.numImages = 0; + + R_InitImagesPool(); + R_InitImages(); + R_InitGPUBuffers(); + FBO_Init(); + GLSL_LoadGPUShaders(); + R_InitShaders(qfalse); +#endif + R_ModelInit(); } diff --git a/codemp/rd-rend2/tr_model_iqm.cpp b/shared/rd-rend2/tr_model_iqm.cpp similarity index 99% rename from codemp/rd-rend2/tr_model_iqm.cpp rename to shared/rd-rend2/tr_model_iqm.cpp index 7639ac1b2d..eff2e47e35 100644 --- a/codemp/rd-rend2/tr_model_iqm.cpp +++ b/shared/rd-rend2/tr_model_iqm.cpp @@ -483,7 +483,7 @@ qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_na } mod->type = MOD_IQM; - iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low ); + iqmData = (iqmData_t *)Hunk_Alloc( size, h_low ); mod->data.iqm = iqmData; // fill header diff --git a/codemp/rd-rend2/tr_postprocess.cpp b/shared/rd-rend2/tr_postprocess.cpp similarity index 100% rename from codemp/rd-rend2/tr_postprocess.cpp rename to shared/rd-rend2/tr_postprocess.cpp diff --git a/codemp/rd-rend2/tr_postprocess.h b/shared/rd-rend2/tr_postprocess.h similarity index 100% rename from codemp/rd-rend2/tr_postprocess.h rename to shared/rd-rend2/tr_postprocess.h diff --git a/codemp/rd-rend2/tr_scene.cpp b/shared/rd-rend2/tr_scene.cpp similarity index 98% rename from codemp/rd-rend2/tr_scene.cpp rename to shared/rd-rend2/tr_scene.cpp index b62dca2004..3107d594b3 100644 --- a/codemp/rd-rend2/tr_scene.cpp +++ b/shared/rd-rend2/tr_scene.cpp @@ -91,8 +91,9 @@ Adds all the scene's polys into this view's drawsurf list */ void R_AddPolygonSurfaces( const trRefdef_t *refdef ) { srfPoly_t *poly; +#ifndef REND2_SP int fogMask = -((refdef->rdflags & RDF_NOFOG) == 0); - +#endif int i; for ( i = 0, poly = refdef->polys; i < refdef->numPolys ; i++, poly++ ) { shader_t *sh = R_GetShaderByHandle( poly->hShader ); @@ -100,7 +101,11 @@ void R_AddPolygonSurfaces( const trRefdef_t *refdef ) { (surfaceType_t *)poly, REFENTITYNUM_WORLD, sh, +#ifndef REND2_SP poly->fogIndex & fogMask, +#else + poly->fogIndex, +#endif qfalse, qfalse, 0 /* cubemapIndex */ ); @@ -113,6 +118,7 @@ RE_AddPolyToScene ===================== */ + void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { srfPoly_t *poly; int i, j; @@ -218,6 +224,7 @@ void RE_AddRefEntityToScene( const refEntity_t *ent ) { r_numentities++; } +#ifndef REND2_SP /* ===================== RE_AddMiniRefEntityToScene @@ -235,7 +242,7 @@ void RE_AddMiniRefEntityToScene( const miniRefEntity_t *miniRefEnt ) { memcpy(&entity, miniRefEnt, sizeof(*miniRefEnt)); RE_AddRefEntityToScene(&entity); } - +#endif /* ===================== @@ -298,8 +305,6 @@ void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, flo void RE_BeginScene(const refdef_t *fd) { - Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) ); - tr.refdef.x = fd->x; tr.refdef.y = fd->y; tr.refdef.width = fd->width; @@ -505,6 +510,10 @@ void RE_EndScene() r_firstScenePoly = r_numpolys; tr.skyPortalEntities = 0; tr.numCachedViewParms = 0; +#ifdef REND2_SP + tr.refdef.doLAGoggles = false; + tr.refdef.doFullbright = false; +#endif } /* diff --git a/codemp/rd-rend2/tr_shade.cpp b/shared/rd-rend2/tr_shade.cpp similarity index 97% rename from codemp/rd-rend2/tr_shade.cpp rename to shared/rd-rend2/tr_shade.cpp index bc65a71cb7..f5805113a4 100644 --- a/codemp/rd-rend2/tr_shade.cpp +++ b/shared/rd-rend2/tr_shade.cpp @@ -864,13 +864,14 @@ static void DrawTris(shaderCommands_t *input, const VertexArraysProperties *vert { index |= GENERICDEF_USE_DEFORM_VERTEXES; } -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (glState.vertexAnimation) { index |= GENERICDEF_USE_VERTEX_ANIMATION; } + else #endif // REND2_SP - else if (glState.skeletalAnimation) + if (glState.skeletalAnimation) { index |= GENERICDEF_USE_SKELETAL_ANIMATION; } @@ -897,7 +898,7 @@ static void DrawTris(shaderCommands_t *input, const VertexArraysProperties *vert uniformDataWriter.SetUniformVec4(UNIFORM_VERTCOLOR, vertColor); DrawItem item = {}; - item.renderState.stateBits = GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_POLYGON_OFFSET_FILL; + item.renderState.stateBits = GLS_POLYMODE_LINE | GLS_DEPTHTEST_DISABLE; item.renderState.cullType = RB_GetCullType(&backEnd.viewParms, backEnd.currentEntity, input->shader->cullType); item.renderState.depthRange = RB_GetDepthRange(backEnd.currentEntity, input->shader); item.program = sp; @@ -1037,11 +1038,12 @@ static void RB_FogPass( shaderCommands_t *input, const VertexArraysProperties *v if (input->shader->numDeforms && !ShaderRequiresCPUDeforms(input->shader)) shaderBits |= FOGDEF_USE_DEFORM_VERTEXES; -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (glState.vertexAnimation) shaderBits |= FOGDEF_USE_VERTEX_ANIMATION; + else #endif // REND2_SP - else if (glState.skeletalAnimation) + if (glState.skeletalAnimation) shaderBits |= FOGDEF_USE_SKELETAL_ANIMATION; if (tr.world && tr.world->globalFog && @@ -1153,7 +1155,7 @@ static void RB_FogPass( shaderCommands_t *input, const VertexArraysProperties *v static unsigned int RB_CalcShaderVertexAttribs( const shader_t *shader ) { unsigned int vertexAttribs = shader->vertexAttribs; -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if(glState.vertexAnimation) { //vertexAttribs &= ~ATTR_COLOR; @@ -1186,13 +1188,14 @@ static shaderProgram_t *SelectShaderProgram( int stageIndex, shaderStage_t *stag { index |= REFRACTIONDEF_USE_DEFORM_VERTEXES; } -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (glState.vertexAnimation) { index |= REFRACTIONDEF_USE_VERTEX_ANIMATION; } + else #endif // REND2_SP - else if (glState.skeletalAnimation) + if (glState.skeletalAnimation) { index |= REFRACTIONDEF_USE_SKELETAL_ANIMATION; } @@ -1224,7 +1227,7 @@ static shaderProgram_t *SelectShaderProgram( int stageIndex, shaderStage_t *stag if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) { -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (glState.vertexAnimation) { index |= LIGHTDEF_USE_VERTEX_ANIMATION; @@ -1258,13 +1261,14 @@ static shaderProgram_t *SelectShaderProgram( int stageIndex, shaderStage_t *stag { index |= GENERICDEF_USE_DEFORM_VERTEXES; } -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (glState.vertexAnimation) { index |= GENERICDEF_USE_VERTEX_ANIMATION; } + else #endif // REND2_SP - else if (glState.skeletalAnimation) + if (glState.skeletalAnimation) { index |= GENERICDEF_USE_SKELETAL_ANIMATION; } @@ -1301,7 +1305,7 @@ static shaderProgram_t *SelectShaderProgram( int stageIndex, shaderStage_t *stag { if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) { -#ifdef REND2_SP +#ifdef REND2_SP_MD3 if (glState.vertexAnimation) { index |= LIGHTDEF_USE_VERTEX_ANIMATION; @@ -1464,6 +1468,7 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input, const VertexArrays if ( backEnd.currentEntity->e.renderfx & RF_FORCE_ENT_ALPHA ) { stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; +#ifndef REND2_SP if ( backEnd.currentEntity->e.renderfx & RF_ALPHA_DEPTH ) { // depth write, so faces through the model will be stomped @@ -1472,7 +1477,18 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input, const VertexArrays // standard alpha surfs. stateBits |= GLS_DEPTHMASK_TRUE; } +#endif + } +#ifdef REND2_SP + if (backEnd.currentEntity->e.renderfx & RF_ALPHA_FADE) + { + if (backEnd.currentEntity->e.shaderRGBA[3] < 255) + { + stateBits = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + forceAlphaGen = AGEN_ENTITY; + } } +#endif } if (backEnd.viewParms.flags & VPF_POINTSHADOW) @@ -1500,7 +1516,7 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input, const VertexArrays vec4_t baseColor; vec4_t vertColor; -#ifdef REND2_SP_MAYBE +#ifdef REND2_SP_GORE // Fade will be set true when rendering some gore surfaces if (input->fade) { @@ -1580,9 +1596,9 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input, const VertexArrays uniformDataWriter.SetUniformVec3(UNIFORM_TONEMINAVGMAXLINEAR, tr.refdef.toneMinAvgMaxLinear); } -#ifdef REND2_SP_MAYBE +#ifdef REND2_SP_GORE // tess scale will be set true only when theres scaled gore - if (!input->scale) + if (input->scale) texMatrix[0] = texMatrix[3] = input->texCoords[input->firstIndex][0][0]; else #endif @@ -1591,8 +1607,19 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input, const VertexArrays uniformDataWriter.SetUniformVec4(UNIFORM_DIFFUSETEXMATRIX, texMatrix); uniformDataWriter.SetUniformVec4(UNIFORM_DIFFUSETEXOFFTURB, texOffTurb); - uniformDataWriter.SetUniformInt(UNIFORM_TCGEN0, pStage->bundle[0].tcGen); - uniformDataWriter.SetUniformInt(UNIFORM_TCGEN1, pStage->bundle[1].tcGen); + texCoordGen_t bundle0TcGen = pStage->bundle[0].tcGen; + texCoordGen_t bundle1TcGen = pStage->bundle[1].tcGen; +#ifdef REND2_SP + if (backEnd.currentEntity && backEnd.currentEntity->e.renderfx&RF_FIRST_PERSON) + { + if (bundle0TcGen == TCGEN_ENVIRONMENT_MAPPED_SP) + bundle0TcGen = TCGEN_ENVIRONMENT_MAPPED_SP_FP; + if (bundle1TcGen == TCGEN_ENVIRONMENT_MAPPED_SP) + bundle1TcGen = TCGEN_ENVIRONMENT_MAPPED_SP_FP; + } +#endif + uniformDataWriter.SetUniformInt(UNIFORM_TCGEN0, bundle0TcGen); + uniformDataWriter.SetUniformInt(UNIFORM_TCGEN1, bundle1TcGen); if (pStage->bundle[0].tcGen == TCGEN_VECTOR) { uniformDataWriter.SetUniformVec3(UNIFORM_TCGEN0VECTOR0, pStage->bundle[0].tcGenVectors[0]); @@ -1992,7 +2019,7 @@ void RB_EndSurface( void ) { return; } - if (tr.world && !backEnd.framePostProcessed) { + if (tr.world && !backEnd.framePostProcessed && !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL)) { if (tr.world->skyboxportal) { // world @@ -2029,6 +2056,10 @@ void RB_EndSurface( void ) { tess.firstIndex = 0; tess.multiDrawPrimitives = 0; tess.externalIBO = nullptr; +#ifdef REND2_SP_GORE + tess.fade = false; + tess.scale = false; +#endif glState.vertexAnimation = qfalse; glState.skeletalAnimation = qfalse; glState.genShadows = qfalse; diff --git a/codemp/rd-rend2/tr_shade_calc.cpp b/shared/rd-rend2/tr_shade_calc.cpp similarity index 100% rename from codemp/rd-rend2/tr_shade_calc.cpp rename to shared/rd-rend2/tr_shade_calc.cpp diff --git a/codemp/rd-rend2/tr_shader.cpp b/shared/rd-rend2/tr_shader.cpp similarity index 98% rename from codemp/rd-rend2/tr_shader.cpp rename to shared/rd-rend2/tr_shader.cpp index 92933dd7f9..d419d71328 100644 --- a/codemp/rd-rend2/tr_shader.cpp +++ b/shared/rd-rend2/tr_shader.cpp @@ -34,6 +34,9 @@ static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS]; // Hash value (generated using the generateHashValueForText function) for the original // retail JKA shader for gfx/2d/wedge. #define RETAIL_ROCKET_WEDGE_SHADER_HASH (1217042) +// Hash value (generated using the generateHashValueForText function) for the original +// retail JKA shader for gfx/menus/radar/arrow_w. +#define RETAIL_ARROW_W_SHADER_HASH (1650186) #define FILE_HASH_SIZE 1024 static shader_t* hashTable[FILE_HASH_SIZE]; @@ -2770,6 +2773,13 @@ static qboolean ParseShader( const char **text ) return qfalse; } + if (tr.hdrLighting) + { + shader.fogParms.color[0] = sRGBtoRGB(shader.fogParms.color[0]); + shader.fogParms.color[1] = sRGBtoRGB(shader.fogParms.color[1]); + shader.fogParms.color[2] = sRGBtoRGB(shader.fogParms.color[2]); + } + token = COM_ParseExt( text, qfalse ); if ( !token[0] ) { @@ -2863,6 +2873,21 @@ static qboolean ParseShader( const char **text ) stages[0].stateBits |= GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; } + // The basejka radar arrow contains an incorrect rgbGen of identity + // It only worked because the original code didn't check shaders at all, + // thus setcolor worked fine but with fixing RB_RotatePic it no longer + // functioned because rgbGen identity doesn't work with setcolor. + // + // We match against retail version of gfx/menus/radar/arrow_w by calculating + // the hash value of the shader text, and comparing it against a + // precalculated value. + if (shaderHash == RETAIL_ARROW_W_SHADER_HASH && + Q_stricmp(shader.name, "gfx/menus/radar/arrow_w") == 0) + { + stages[0].rgbGen = CGEN_VERTEX; + stages[0].alphaGen = AGEN_VERTEX; + } + return qtrue; } @@ -3689,7 +3714,7 @@ static shader_t *GeneratePermanentShader( void ) { return tr.defaultShader; } - newShader = (shader_t *)ri.Hunk_Alloc( sizeof( shader_t ), h_low ); + newShader = (shader_t *)Hunk_Alloc( sizeof( shader_t ), h_low ); *newShader = shader; @@ -3741,12 +3766,12 @@ static shader_t *GeneratePermanentShader( void ) { if ( !stages[i].active ) { break; } - newShader->stages[i] = (shaderStage_t *)ri.Hunk_Alloc( sizeof( stages[i] ), h_low ); + newShader->stages[i] = (shaderStage_t *)Hunk_Alloc( sizeof( stages[i] ), h_low ); *newShader->stages[i] = stages[i]; for ( b = 0 ; b < NUM_TEXTURE_BUNDLES ; b++ ) { size = newShader->stages[i]->bundle[b].numTexMods * sizeof( texModInfo_t ); - newShader->stages[i]->bundle[b].texMods = (texModInfo_t *)ri.Hunk_Alloc( size, h_low ); + newShader->stages[i]->bundle[b].texMods = (texModInfo_t *)Hunk_Alloc( size, h_low ); Com_Memcpy( newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size ); } } @@ -4179,6 +4204,9 @@ static shader_t *FinishShader( void ) { if (!pStage->active) continue; + if (pStage->stateBits & (GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS)) + break; + if (pStage->alphaTestType == ALPHA_TEST_NONE) shader.useSimpleDepthShader = qtrue; break; @@ -4928,7 +4956,7 @@ static void ScanAndLoadShaderFiles( void ) size += MAX_SHADERTEXT_HASH; - hashMem = (char *)ri.Hunk_Alloc( size * sizeof(char *), h_low ); + hashMem = (char *)Hunk_Alloc( size * sizeof(char *), h_low ); for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { shaderTextHashTable[i] = (const char **) hashMem; diff --git a/codemp/rd-rend2/tr_shadows.cpp b/shared/rd-rend2/tr_shadows.cpp similarity index 100% rename from codemp/rd-rend2/tr_shadows.cpp rename to shared/rd-rend2/tr_shadows.cpp diff --git a/codemp/rd-rend2/tr_skin.cpp b/shared/rd-rend2/tr_skin.cpp similarity index 82% rename from codemp/rd-rend2/tr_skin.cpp rename to shared/rd-rend2/tr_skin.cpp index d14f211b6a..6924083004 100644 --- a/codemp/rd-rend2/tr_skin.cpp +++ b/shared/rd-rend2/tr_skin.cpp @@ -1,5 +1,8 @@ #include "tr_local.h" +#ifdef REND2_SP +#include "../qcommon/sstring.h" +#endif /* ============================================================================ @@ -125,6 +128,7 @@ qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin) // parse the shader name token = CommaParse( &text_p ); +#ifndef JK2_MODE if ( !strcmp( &surfName[strlen(surfName)-4], "_off") ) { if ( !strcmp( token ,"*off" ) ) @@ -133,6 +137,7 @@ qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin) } surfName[strlen(surfName)-4] = 0; //remove the "_off" } +#endif if ( (unsigned)skin->numSurfaces >= ARRAY_LEN( skin->surfaces ) ) { assert( ARRAY_LEN( skin->surfaces ) > (unsigned)skin->numSurfaces ); @@ -140,7 +145,7 @@ qhandle_t RE_RegisterIndividualSkin( const char *name , qhandle_t hSkin) break; } surf = (skinSurface_t *) Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[skin->numSurfaces] = (_skinSurface_t *)surf; + skin->surfaces[skin->numSurfaces] = (skinSurface_t *)surf; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); @@ -191,23 +196,23 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; } tr.numSkins++; - skin = (struct skin_s *)Hunk_Alloc( sizeof( skin_t ), h_low ); + skin = (skin_s *)Hunk_Alloc( sizeof( skin_t ), h_low ); tr.skins[hSkin] = skin; Q_strncpyz( skin->name, name, sizeof( skin->name ) ); skin->numSurfaces = 0; // make sure the render thread is stopped R_IssuePendingRenderCommands(); - +#ifdef JK2_MODE // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { -/* skin->numSurfaces = 1; + skin->numSurfaces = 1; skin->surfaces[0] = (skinSurface_t *)Hunk_Alloc( sizeof(skin->surfaces[0]), h_low ); skin->surfaces[0]->shader = R_FindShader( name, lightmapsNone, stylesDefault, qtrue ); return hSkin; -*/ - } + } +#endif char skinhead[MAX_QPATH]={0}; char skintorso[MAX_QPATH]={0}; char skinlower[MAX_QPATH]={0}; @@ -339,6 +344,7 @@ static char *CommaParse( char **data_p ) { return com_token; } +#ifndef REND2_SP /* =============== RE_RegisterServerSkin @@ -362,6 +368,66 @@ qhandle_t RE_RegisterServerSkin( const char *name ) { return r; } +#else +/* +class CStringComparator +{ +public: + bool operator()(const char *s1, const char *s2) const { return(stricmp(s1, s2) < 0); } +}; +*/ +typedef std::map AnimationCFGs_t; +AnimationCFGs_t AnimationCFGs; + +// I added this function for development purposes (and it's VM-safe) so we don't have problems +// with our use of cached models but uncached animation.cfg files (so frame sequences are out of sync +// if someone rebuild the model while you're ingame and you change levels)... +// +// Usage: call with psDest == NULL for a size enquire (for malloc), +// then with NZ ptr for it to copy to your supplied buffer... +// +int RE_GetAnimationCFG(const char *psCFGFilename, char *psDest, int iDestSize) +{ + char *psText = NULL; + + AnimationCFGs_t::iterator it = AnimationCFGs.find(psCFGFilename); + if (it != AnimationCFGs.end()) + { + psText = (*it).second; + } + else + { + // not found, so load it... + // + fileHandle_t f; + int iLen = ri.FS_FOpenFileRead(psCFGFilename, &f, qfalse); + if (iLen <= 0) + { + return 0; + } + + psText = (char *)R_Malloc(iLen + 1, TAG_ANIMATION_CFG, qfalse); + + ri.FS_Read(psText, iLen, f); + psText[iLen] = '\0'; + ri.FS_FCloseFile(f); + + AnimationCFGs[psCFGFilename] = psText; + } + + if (psText) // sanity, but should always be NZ + { + if (psDest) + { + Q_strncpyz(psDest, psText, iDestSize); + } + + return strlen(psText); + } + + return 0; +} +#endif /* =============== @@ -374,10 +440,10 @@ void R_InitSkins( void ) { tr.numSkins = 1; // make the default skin have all default shaders - skin = tr.skins[0] = (struct skin_s *)ri.Hunk_Alloc( sizeof( skin_t ), h_low ); + skin = tr.skins[0] = (struct skin_s *)Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = (_skinSurface_t *)ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0] = (skinSurface_t *)Hunk_Alloc( sizeof( skinSurface_t ), h_low ); skin->surfaces[0]->shader = tr.defaultShader; } diff --git a/codemp/rd-rend2/tr_sky.cpp b/shared/rd-rend2/tr_sky.cpp similarity index 100% rename from codemp/rd-rend2/tr_sky.cpp rename to shared/rd-rend2/tr_sky.cpp diff --git a/codemp/rd-rend2/tr_subs.cpp b/shared/rd-rend2/tr_subs.cpp similarity index 62% rename from codemp/rd-rend2/tr_subs.cpp rename to shared/rd-rend2/tr_subs.cpp index 0228319861..8e01c88f24 100644 --- a/codemp/rd-rend2/tr_subs.cpp +++ b/shared/rd-rend2/tr_subs.cpp @@ -44,8 +44,11 @@ void QDECL Com_OPrintf( const char *msg, ... ) va_start(argptr, msg); Q_vsnprintf(text, sizeof(text), msg, argptr); va_end(argptr); - +#ifndef REND2_SP ri.OPrintf("%s", text); +#else + ri.Printf(PRINT_ALL, "%s", text); +#endif } void QDECL Com_Error( int level, const char *error, ... ) @@ -60,31 +63,42 @@ void QDECL Com_Error( int level, const char *error, ... ) ri.Error(level, "%s", text); } -// HUNK -void *Hunk_AllocateTempMemory( int size ) { - return ri.Hunk_AllocateTempMemory( size ); + + +// ZONE +void *Z_Malloc( int iSize, memtag_t eTag, qboolean bZeroit, int iAlign ) { + return ri.Z_Malloc( iSize, eTag, bZeroit, iAlign ); } -void Hunk_FreeTempMemory( void *buf ) { - ri.Hunk_FreeTempMemory( buf ); +void* R_Malloc(int iSize, memtag_t eTag) +{ + return ri.Z_Malloc(iSize, eTag, qtrue, 4); } -void *Hunk_Alloc( int size, ha_pref preference ) { - return ri.Hunk_Alloc( size, preference ); +void* R_Malloc(int iSize, memtag_t eTag, qboolean bZeroit) +{ + return ri.Z_Malloc(iSize, eTag, bZeroit, 4); } -int Hunk_MemoryRemaining( void ) { - return ri.Hunk_MemoryRemaining(); +#ifdef REND2_SP +void* R_Malloc(int iSize, memtag_t eTag, qboolean bZeroit, int iAlign) +{ + return ri.Z_Malloc(iSize, eTag, bZeroit, iAlign); } -// ZONE -void *Z_Malloc( int iSize, memtag_t eTag, qboolean bZeroit, int iAlign ) { - return ri.Z_Malloc( iSize, eTag, bZeroit, iAlign ); +int Z_Free( void *ptr ) { + return ri.Z_Free( ptr ); } -void Z_Free( void *ptr ) { - ri.Z_Free( ptr ); +void R_Free(void *ptr) +{ + ri.Z_Free(ptr); +} +#else +void Z_Free(void *ptr) { + ri.Z_Free(ptr); } +#endif int Z_MemSize( memtag_t eTag ) { return ri.Z_MemSize( eTag ); @@ -93,3 +107,42 @@ int Z_MemSize( memtag_t eTag ) { void Z_MorphMallocTag( void *pvBuffer, memtag_t eDesiredTag ) { ri.Z_MorphMallocTag( pvBuffer, eDesiredTag ); } + +// HUNK +#ifdef REND2_SP +//void* Hunk_Alloc(int iSize, ha_pref preferences) +//{ +// return Hunk_Alloc(iSize, qtrue); +//} + +void* Hunk_Alloc(int size, ha_pref preference) { + return R_Malloc(size, TAG_HUNKALLOC, qtrue); +} + +void* Hunk_AllocateTempMemory(int size) { + // don't bother clearing, because we are going to load a file over it + return R_Malloc(size, TAG_TEMP_HUNKALLOC, qfalse); +} + +void Hunk_FreeTempMemory(void* buf) +{ + ri.Z_Free(buf); +} +#else + +void *Hunk_AllocateTempMemory(int size) { + return ri.Hunk_AllocateTempMemory(size); +} + +void Hunk_FreeTempMemory(void *buf) { + ri.Hunk_FreeTempMemory(buf); +} + +void *Hunk_Alloc(int size, ha_pref preference) { + return ri.Hunk_Alloc(size, preference); +} + +int Hunk_MemoryRemaining(void) { + return ri.Hunk_MemoryRemaining(); +} +#endif \ No newline at end of file diff --git a/codemp/rd-rend2/tr_surface.cpp b/shared/rd-rend2/tr_surface.cpp similarity index 83% rename from codemp/rd-rend2/tr_surface.cpp rename to shared/rd-rend2/tr_surface.cpp index fc3255a5ed..63443be5e7 100644 --- a/codemp/rd-rend2/tr_surface.cpp +++ b/shared/rd-rend2/tr_surface.cpp @@ -818,7 +818,7 @@ static void DoLine( const vec3_t start, const vec3_t end, const vec3_t up, float tess.indexes[tess.numIndexes++] = vbase + 3; } -static void DoLine2( const vec3_t start, const vec3_t end, const vec3_t up, float spanWidth, float spanWidth2 ) +static void DoLine2(const vec3_t start, const vec3_t end, const vec3_t up, float spanWidth, float spanWidth2, const float tcStart = 0.0f, const float tcEnd = 1.0f) { int vbase; @@ -828,26 +828,26 @@ static void DoLine2( const vec3_t start, const vec3_t end, const vec3_t up, floa VectorMA( start, spanWidth, up, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; + tess.texCoords[tess.numVertexes][0][1] = tcStart; VectorScale4 (backEnd.currentEntity->e.shaderRGBA, 1.0f / 255.0f, tess.vertexColors[tess.numVertexes]); tess.numVertexes++; VectorMA( start, -spanWidth, up, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 1;//backEnd.currentEntity->e.shaderTexCoord[0]; - tess.texCoords[tess.numVertexes][0][1] = 0; + tess.texCoords[tess.numVertexes][0][1] = tcStart; VectorScale4 (backEnd.currentEntity->e.shaderRGBA, 1.0f / 255.0f, tess.vertexColors[tess.numVertexes]); tess.numVertexes++; VectorMA( end, spanWidth2, up, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1;//backEnd.currentEntity->e.shaderTexCoord[1]; + tess.texCoords[tess.numVertexes][0][1] = tcEnd;//backEnd.currentEntity->e.shaderTexCoord[1]; VectorScale4 (backEnd.currentEntity->e.shaderRGBA, 1.0f / 255.0f, tess.vertexColors[tess.numVertexes]); tess.numVertexes++; VectorMA( end, -spanWidth2, up, tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0][0] = 1;//backEnd.currentEntity->e.shaderTexCoord[0]; - tess.texCoords[tess.numVertexes][0][1] = 1;//backEnd.currentEntity->e.shaderTexCoord[1]; + tess.texCoords[tess.numVertexes][0][1] = tcEnd;//backEnd.currentEntity->e.shaderTexCoord[1]; VectorScale4 (backEnd.currentEntity->e.shaderRGBA, 1.0f / 255.0f, tess.vertexColors[tess.numVertexes]); tess.numVertexes++; @@ -860,6 +860,7 @@ static void DoLine2( const vec3_t start, const vec3_t end, const vec3_t up, floa tess.indexes[tess.numIndexes++] = vbase + 3; } +#ifndef REND2_SP static void DoLine_Oriented( const vec3_t start, const vec3_t end, const vec3_t up, float spanWidth ) { float spanWidth2; @@ -904,35 +905,33 @@ static void DoLine_Oriented( const vec3_t start, const vec3_t end, const vec3_t tess.indexes[tess.numIndexes++] = vbase + 3; } -//----------------- -// RB_SurfaceLine -//----------------- -static void RB_SurfaceLine( void ) +static void RB_SurfaceOrientedLine(void) { refEntity_t *e; vec3_t right; vec3_t start, end; - vec3_t v1, v2; e = &backEnd.currentEntity->e; - VectorCopy( e->oldorigin, end ); - VectorCopy( e->origin, start ); + VectorCopy(e->oldorigin, end); + VectorCopy(e->origin, start); // compute side vector - VectorSubtract( start, backEnd.viewParms.ori.origin, v1 ); - VectorSubtract( end, backEnd.viewParms.ori.origin, v2 ); - CrossProduct( v1, v2, right ); - VectorNormalize( right ); - - DoLine( start, end, right, e->radius); + VectorNormalize(e->axis[1]); + VectorCopy(e->axis[1], right); + DoLine_Oriented(start, end, right, e->data.line.width*0.5); } +#endif -static void RB_SurfaceOrientedLine( void ) +//----------------- +// RB_SurfaceLine +//----------------- +static void RB_SurfaceLine( void ) { refEntity_t *e; vec3_t right; vec3_t start, end; + vec3_t v1, v2; e = &backEnd.currentEntity->e; @@ -940,9 +939,12 @@ static void RB_SurfaceOrientedLine( void ) VectorCopy( e->origin, start ); // compute side vector - VectorNormalize( e->axis[1] ); - VectorCopy(e->axis[1], right); - DoLine_Oriented( start, end, right, e->data.line.width*0.5 ); + VectorSubtract( start, backEnd.viewParms.ori.origin, v1 ); + VectorSubtract( end, backEnd.viewParms.ori.origin, v2 ); + CrossProduct( v1, v2, right ); + VectorNormalize( right ); + + DoLine( start, end, right, e->radius); } /* @@ -1112,6 +1114,7 @@ static float Q_crandom( int *seed ) { static void CreateShape() //---------------------------------------------------------------------------- { +#ifndef REND2_SP VectorSet( sh1, 0.66f + Q_flrand(-1.0f, 1.0f) * 0.1f, // fwd 0.07f + Q_flrand(-1.0f, 1.0f) * 0.025f, 0.07f + Q_flrand(-1.0f, 1.0f) * 0.025f ); @@ -1120,10 +1123,20 @@ static void CreateShape() VectorSet( sh2, 0.33f + Q_flrand(-1.0f, 1.0f) * 0.1f, // fwd -sh1[1] + Q_flrand(-1.0f, 1.0f) * 0.02f, // forcing point to be on the opposite side of the line -- right -sh1[2] + Q_flrand(-1.0f, 1.0f) * 0.02f );// up +#else + VectorSet(sh1, 0.66f,// + Q_flrand(-1.0f, 1.0f) * 0.1f, // fwd + 0.08f + Q_flrand(-1.0f, 1.0f) * 0.02f, + 0.08f + Q_flrand(-1.0f, 1.0f) * 0.02f); + + // it seems to look best to have a point on one side of the ideal line, then the other point on the other side. + VectorSet(sh2, 0.33f,// + Q_flrand(-1.0f, 1.0f) * 0.1f, // fwd + -sh1[1] + Q_flrand(-1.0f, 1.0f) * 0.02f, // forcing point to be on the opposite side of the line -- right + -sh1[2] + Q_flrand(-1.0f, 1.0f) * 0.02f);// up +#endif } //---------------------------------------------------------------------------- -static void ApplyShape( vec3_t start, vec3_t end, vec3_t right, float sradius, float eradius, int count ) +static void ApplyShape( vec3_t start, vec3_t end, vec3_t right, float sradius, float eradius, int count, float startPerc = 0.0f, float endPerc = 1.0f) //---------------------------------------------------------------------------- { vec3_t point1, point2, fwd; @@ -1133,7 +1146,7 @@ static void ApplyShape( vec3_t start, vec3_t end, vec3_t right, float sradius, f if ( count < 1 ) { // done recursing - DoLine2( start, end, right, sradius, eradius ); + DoLine2( start, end, right, sradius, eradius, startPerc, endPerc); return; } @@ -1157,7 +1170,11 @@ static void ApplyShape( vec3_t start, vec3_t end, vec3_t right, float sradius, f rads2 = sradius * 0.333f + eradius * 0.666f; // recursion +#ifndef REND2_SP ApplyShape( start, point1, right, sradius, rads1, count - 1 ); +#else + ApplyShape( start, point1, right, sradius, rads1, count - 1, startPerc, startPerc * 0.666f + endPerc * 0.333f); +#endif perc = sh2[0]; @@ -1167,8 +1184,13 @@ static void ApplyShape( vec3_t start, vec3_t end, vec3_t right, float sradius, f VectorMA( point2, dis * sh2[2], up, point2 ); // recursion +#ifndef REND2_SP ApplyShape( point2, point1, right, rads1, rads2, count - 1 ); ApplyShape( point2, end, right, rads2, eradius, count - 1 ); +#else + ApplyShape(point2, point1, right, rads1, rads2, count - 1, startPerc * 0.333f + endPerc * 0.666f, startPerc * 0.666f + endPerc * 0.333f); + ApplyShape(point2, end, right, rads2, eradius, count - 1, startPerc * 0.333f + endPerc * 0.666f, endPerc); +#endif } //---------------------------------------------------------------------------- @@ -1188,16 +1210,31 @@ static void DoBoltSeg( vec3_t start, vec3_t end, vec3_t right, float radius ) VectorSubtract( end, start, fwd ); dis = VectorNormalize( fwd ); +#ifdef REND2_SP + if (dis > 2000) //freaky long + { + // ri.Printf( PRINT_WARNING, "DoBoltSeg: insane distance.\n" ); + dis = 2000; + } +#endif + MakeNormalVectors( fwd, rt, up ); VectorCopy( start, old ); +#ifndef REND2_SP oldRadius = newRadius = radius; - - for ( i = 20; i <= dis; i+= 20 ) + for (i = 20; i <= dis; i += 20) { // because of our large step size, we may not actually draw to the end. In this case, fudge our percent so that we are basically complete - if ( i + 20 > dis ) + if (i + 20 > dis) +#else + newRadius = oldRadius = radius; + for (i = 16; i <= dis; i += 16) + { + // because of our large step size, we may not actually draw to the end. In this case, fudge our percent so that we are basically complete + if (i + 16 > dis) +#endif { perc = 1.0f; } @@ -1209,8 +1246,13 @@ static void DoBoltSeg( vec3_t start, vec3_t end, vec3_t right, float radius ) // create our level of deviation for this point VectorScale( fwd, Q_crandom(&e->frame) * 3.0f, temp ); // move less in fwd direction, chaos also does not affect this +#ifndef REND2_SP VectorMA( temp, Q_crandom(&e->frame) * 7.0f * e->axis[0][0], rt, temp ); // move more in direction perpendicular to line, angles is really the chaos VectorMA( temp, Q_crandom(&e->frame) * 7.0f * e->axis[0][0], up, temp ); // move more in direction perpendicular to line +#else + VectorMA(temp, Q_crandom(&e->frame) * 7.0f * e->angles[0], rt, temp); // move more in direction perpendicular to line, angles is really the chaos + VectorMA(temp, Q_crandom(&e->frame) * 7.0f * e->angles[0], up, temp); // move more in direction perpendicular to line +#endif // track our total level of offset from the ideal line VectorAdd( off, temp, off ); @@ -1230,7 +1272,11 @@ static void DoBoltSeg( vec3_t start, vec3_t end, vec3_t right, float radius ) } // Apply the random shape to our line seg to give it some micro-detail-jaggy-coolness. +#ifndef REND2_SP ApplyShape( cur, old, right, newRadius, oldRadius, LIGHTNING_RECURSION_LEVEL ); +#else + ApplyShape(cur, old, right, newRadius, oldRadius, 2 - r_lodbias->integer, 0, 1); +#endif // randomly split off to create little tendrils, but don't do it too close to the end and especially if we are not even of the forked variety if ( ( e->renderfx & RF_FORKED ) && f_count > 0 && Q_random(&e->frame) > 0.94f && radius * (1.0f - perc) > 0.2f ) @@ -1280,8 +1326,11 @@ static void RB_SurfaceElectricity() // see if we should grow from start to end if ( e->renderfx & RF_GROW ) { +#ifndef REND2_SP perc = 1.0f - ( e->axis[0][2]/*endTime*/ - tr.refdef.time ) / e->axis[0][1]/*duration*/; - +#else + perc = 1.0f - (e->endTime - tr.refdef.time) / e->angles[1]/*duration*/; +#endif if ( perc > 1.0f ) { perc = 1.0f; @@ -1300,7 +1349,9 @@ static void RB_SurfaceElectricity() VectorSubtract( end, backEnd.viewParms.ori.origin, v2 ); CrossProduct( v1, v2, right ); VectorNormalize( right ); - +#ifdef REND2_SP + f_count = 3; +#endif DoBoltSeg( start, end, right, radius ); } @@ -1959,6 +2010,333 @@ static void RB_SurfaceBSPGrid( srfBspSurface_t *srf ) { } } +#ifdef REND2_SP +#define LATHE_SEG_STEP 10 +#define BEZIER_STEP 0.05f // must be in the range of 0 to 1 + +// FIXME: This function is horribly expensive +static void RB_SurfaceLathe() +{ + refEntity_t *e; + vec2_t pt, oldpt, l_oldpt; + vec2_t pt2, oldpt2, l_oldpt2; + float bezierStep, latheStep; + float temp, mu, mum1; + float mum13, mu3, group1, group2; + float s, c, d = 1.0f, pain = 0.0f; + int i, t, vbase; + + e = &backEnd.currentEntity->e; + + if (e->endTime && e->endTime > backEnd.refdef.time) + { + d = 1.0f - (e->endTime - backEnd.refdef.time) / 1000.0f; + } + + if (e->frame && e->frame + 1000 > backEnd.refdef.time) + { + pain = (backEnd.refdef.time - e->frame) / 1000.0f; + // pain *= pain; + pain = (1.0f - pain) * 0.08f; + } + + VectorSet2(l_oldpt, e->axis[0][0], e->axis[0][1]); + + // do scalability stuff...r_lodbias 0-3 + int lod = r_lodbias->integer + 1; + if (lod > 4) + { + lod = 4; + } + if (lod < 1) + { + lod = 1; + } + bezierStep = BEZIER_STEP * lod; + latheStep = LATHE_SEG_STEP * lod; + + // Do bezier profile strip, then lathe this around to make a 3d model + for (mu = 0.0f; mu <= 1.01f * d; mu += bezierStep) + { + // Four point curve + mum1 = 1 - mu; + mum13 = mum1 * mum1 * mum1; + mu3 = mu * mu * mu; + group1 = 3 * mu * mum1 * mum1; + group2 = 3 * mu * mu *mum1; + + // Calc the current point on the curve + for (i = 0; i < 2; i++) + { + l_oldpt2[i] = mum13 * e->axis[0][i] + group1 * e->axis[1][i] + group2 * e->axis[2][i] + mu3 * e->oldorigin[i]; + } + + VectorSet2(oldpt, l_oldpt[0], 0); + VectorSet2(oldpt2, l_oldpt2[0], 0); + + // lathe patch section around in a complete circle + for (t = latheStep; t <= 360; t += latheStep) + { + VectorSet2(pt, l_oldpt[0], 0); + VectorSet2(pt2, l_oldpt2[0], 0); + + s = sin(DEG2RAD(t)); + c = cos(DEG2RAD(t)); + + // rotate lathe points +//c -s 0 +//s c 0 +//0 0 1 + temp = c * pt[0] - s * pt[1]; + pt[1] = s * pt[0] + c * pt[1]; + pt[0] = temp; + temp = c * pt2[0] - s * pt2[1]; + pt2[1] = s * pt2[0] + c * pt2[1]; + pt2[0] = temp; + + RB_CHECKOVERFLOW(4, 6); + + vbase = tess.numVertexes; + + vec3_t normal; + + // Actually generate the necessary verts + VectorSet(normal, oldpt[0], oldpt[1], l_oldpt[1]); + VectorAdd(e->origin, normal, tess.xyz[tess.numVertexes]); + VectorNormalize(normal); + tess.normal[tess.numVertexes] = R_VboPackNormal(normal); + i = oldpt[0] * 0.1f + oldpt[1] * 0.1f; + tess.texCoords[tess.numVertexes][0][0] = (t - latheStep) / 360.0f; + tess.texCoords[tess.numVertexes][0][1] = mu - bezierStep + cos(i + backEnd.refdef.floatTime) * pain; + tess.vertexColors[tess.numVertexes][0] = e->shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = e->shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[2]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + VectorSet(normal, oldpt2[0], oldpt2[1], l_oldpt2[1]); + VectorAdd(e->origin, normal, tess.xyz[tess.numVertexes]); + VectorNormalize(normal); + tess.normal[tess.numVertexes] = R_VboPackNormal(normal); + i = oldpt2[0] * 0.1f + oldpt2[1] * 0.1f; + tess.texCoords[tess.numVertexes][0][0] = (t - latheStep) / 360.0f; + tess.texCoords[tess.numVertexes][0][1] = mu + cos(i + backEnd.refdef.floatTime) * pain; + tess.vertexColors[tess.numVertexes][0] = e->shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = e->shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[2]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + VectorSet(normal, pt[0], pt[1], l_oldpt[1]); + VectorAdd(e->origin, normal, tess.xyz[tess.numVertexes]); + VectorNormalize(normal); + tess.normal[tess.numVertexes] = R_VboPackNormal(normal); + i = pt[0] * 0.1f + pt[1] * 0.1f; + tess.texCoords[tess.numVertexes][0][0] = t / 360.0f; + tess.texCoords[tess.numVertexes][0][1] = mu - bezierStep + cos(i + backEnd.refdef.floatTime) * pain; + tess.vertexColors[tess.numVertexes][0] = e->shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = e->shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[2]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + VectorSet(normal, pt2[0], pt2[1], l_oldpt2[1]); + VectorAdd(e->origin, normal, tess.xyz[tess.numVertexes]); + VectorNormalize(normal); + tess.normal[tess.numVertexes] = R_VboPackNormal(normal); + i = pt2[0] * 0.1f + pt2[1] * 0.1f; + tess.texCoords[tess.numVertexes][0][0] = t / 360.0f; + tess.texCoords[tess.numVertexes][0][1] = mu + cos(i + backEnd.refdef.floatTime) * pain; + tess.vertexColors[tess.numVertexes][0] = e->shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = e->shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[2]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = vbase; + tess.indexes[tess.numIndexes++] = vbase + 1; + tess.indexes[tess.numIndexes++] = vbase + 3; + + tess.indexes[tess.numIndexes++] = vbase + 3; + tess.indexes[tess.numIndexes++] = vbase + 2; + tess.indexes[tess.numIndexes++] = vbase; + + // Shuffle new points to old + VectorCopy2(pt, oldpt); + VectorCopy2(pt2, oldpt2); + } + + // shuffle lathe points + VectorCopy2(l_oldpt2, l_oldpt); + } +} + +#define DISK_DEF 4 +#define TUBE_DEF 6 + +static void RB_SurfaceClouds() +{ + // Disk definition + float diskStripDef[DISK_DEF] = { + 0.0f, + 0.4f, + 0.7f, + 1.0f }; + + float diskAlphaDef[DISK_DEF] = { + 1.0f, + 1.0f, + 0.4f, + 0.0f }; + + float diskCurveDef[DISK_DEF] = { + 0.0f, + 0.0f, + 0.008f, + 0.02f }; + + // tube definition + float tubeStripDef[TUBE_DEF] = { + 0.0f, + 0.05f, + 0.1f, + 0.5f, + 0.7f, + 1.0f }; + + float tubeAlphaDef[TUBE_DEF] = { + 0.0f, + 0.45f, + 1.0f, + 1.0f, + 0.45f, + 0.0f }; + + float tubeCurveDef[TUBE_DEF] = { + 0.0f, + 0.004f, + 0.006f, + 0.01f, + 0.006f, + 0.0f }; + + refEntity_t *e; + vec3_t pt, oldpt; + vec3_t pt2, oldpt2; + float latheStep = 30.0f; + float s, c, temp; + float *stripDef, *alphaDef, *curveDef, ct; + int i, t, vbase; + + e = &backEnd.currentEntity->e; + + // select which type we shall be doing + if (e->renderfx & RF_GROW) // doing tube type + { + ct = TUBE_DEF; + stripDef = tubeStripDef; + alphaDef = tubeAlphaDef; + curveDef = tubeCurveDef; + e->backlerp *= -1; // needs to be reversed + } + else + { + ct = DISK_DEF; + stripDef = diskStripDef; + alphaDef = diskAlphaDef; + curveDef = diskCurveDef; + } + + // do the strip def, then lathe this around to make a 3d model + for (i = 0; i < ct - 1; i++) + { + VectorSet(oldpt, (stripDef[i] * (e->radius - e->rotation)) + e->rotation, 0, curveDef[i] * e->radius * e->backlerp); + VectorSet(oldpt2, (stripDef[i + 1] * (e->radius - e->rotation)) + e->rotation, 0, curveDef[i + 1] * e->radius * e->backlerp); + + // lathe section around in a complete circle + for (t = latheStep; t <= 360; t += latheStep) + { + // rotate every time except last seg + if (t < 360.0f) + { + VectorCopy(oldpt, pt); + VectorCopy(oldpt2, pt2); + + s = sin(DEG2RAD(latheStep)); + c = cos(DEG2RAD(latheStep)); + + // rotate lathe points + temp = c * pt[0] - s * pt[1]; // c -s 0 + pt[1] = s * pt[0] + c * pt[1]; // s c 0 + pt[0] = temp; // 0 0 1 + + temp = c * pt2[0] - s * pt2[1]; // c -s 0 + pt2[1] = s * pt2[0] + c * pt2[1];// s c 0 + pt2[0] = temp; // 0 0 1 + } + else + { + // just glue directly to the def points. + VectorSet(pt, (stripDef[i] * (e->radius - e->rotation)) + e->rotation, 0, curveDef[i] * e->radius * e->backlerp); + VectorSet(pt2, (stripDef[i + 1] * (e->radius - e->rotation)) + e->rotation, 0, curveDef[i + 1] * e->radius * e->backlerp); + } + + RB_CHECKOVERFLOW(4, 6); + + vbase = tess.numVertexes; + + // Actually generate the necessary verts + VectorAdd(e->origin, oldpt, tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = tess.xyz[tess.numVertexes][0] * 0.1f; + tess.texCoords[tess.numVertexes][0][1] = tess.xyz[tess.numVertexes][1] * 0.1f; + tess.vertexColors[tess.numVertexes][0] = + tess.vertexColors[tess.numVertexes][1] = + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[0] * alphaDef[i]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + VectorAdd(e->origin, oldpt2, tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = tess.xyz[tess.numVertexes][0] * 0.1f; + tess.texCoords[tess.numVertexes][0][1] = tess.xyz[tess.numVertexes][1] * 0.1f; + tess.vertexColors[tess.numVertexes][0] = + tess.vertexColors[tess.numVertexes][1] = + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[0] * alphaDef[i + 1]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + VectorAdd(e->origin, pt, tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = tess.xyz[tess.numVertexes][0] * 0.1f; + tess.texCoords[tess.numVertexes][0][1] = tess.xyz[tess.numVertexes][1] * 0.1f; + tess.vertexColors[tess.numVertexes][0] = + tess.vertexColors[tess.numVertexes][1] = + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[0] * alphaDef[i]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + VectorAdd(e->origin, pt2, tess.xyz[tess.numVertexes]); + tess.texCoords[tess.numVertexes][0][0] = tess.xyz[tess.numVertexes][0] * 0.1f; + tess.texCoords[tess.numVertexes][0][1] = tess.xyz[tess.numVertexes][1] * 0.1f; + tess.vertexColors[tess.numVertexes][0] = + tess.vertexColors[tess.numVertexes][1] = + tess.vertexColors[tess.numVertexes][2] = e->shaderRGBA[0] * alphaDef[i + 1]; + tess.vertexColors[tess.numVertexes][3] = e->shaderRGBA[3]; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = vbase; + tess.indexes[tess.numIndexes++] = vbase + 1; + tess.indexes[tess.numIndexes++] = vbase + 3; + + tess.indexes[tess.numIndexes++] = vbase + 3; + tess.indexes[tess.numIndexes++] = vbase + 2; + tess.indexes[tess.numIndexes++] = vbase; + + // Shuffle new points to old + VectorCopy2(pt, oldpt); + VectorCopy2(pt2, oldpt2); + } + } +} +#endif /* =========================================================================== @@ -2022,15 +2400,16 @@ static void RB_SurfaceEntity( surfaceType_t *surfType ) { case RT_LINE: RB_SurfaceLine(); break; - case RT_ORIENTEDLINE: - RB_SurfaceOrientedLine(); - break; case RT_SABER_GLOW: RB_SurfaceSaberGlow(); break; case RT_CYLINDER: RB_SurfaceCylinder(); break; +#ifndef REND2_SP + case RT_ORIENTEDLINE: + RB_SurfaceOrientedLine(); + break; case RT_ENT_CHAIN: { static trRefEntity_t tempEnt = *backEnd.currentEntity; @@ -2055,6 +2434,14 @@ static void RB_SurfaceEntity( surfaceType_t *surfType ) { } } break; +#else + case RT_LATHE: + RB_SurfaceLathe(); + break; + case RT_CLOUDS: + RB_SurfaceClouds(); + break; +#endif default: RB_SurfaceAxis(); break; @@ -2094,7 +2481,12 @@ void RB_SurfaceVBOMDVMesh(srfVBOMDVMesh_t * surface) RB_EndSurface(); } - //drawSurf_t drawSurf = + //FIXME: Implement GPU vertex interpolation instead! + if (backEnd.currentEntity->e.oldframe != 0 || backEnd.currentEntity->e.frame != 0) + { + RB_SurfaceMesh(surface->mdvSurface); + return; + } R_BindVBO(surface->vbo); R_BindIBO(surface->ibo); diff --git a/codemp/rd-rend2/tr_tangentspace.cpp b/shared/rd-rend2/tr_tangentspace.cpp similarity index 100% rename from codemp/rd-rend2/tr_tangentspace.cpp rename to shared/rd-rend2/tr_tangentspace.cpp diff --git a/codemp/rd-rend2/tr_vbo.cpp b/shared/rd-rend2/tr_vbo.cpp similarity index 99% rename from codemp/rd-rend2/tr_vbo.cpp rename to shared/rd-rend2/tr_vbo.cpp index 4025bd31d5..b4f845388a 100644 --- a/codemp/rd-rend2/tr_vbo.cpp +++ b/shared/rd-rend2/tr_vbo.cpp @@ -91,7 +91,7 @@ VBO_t *R_CreateVBO(byte * vertexes, int vertexesSize, vboUsage_t usage) R_IssuePendingRenderCommands(); - vbo = tr.vbos[tr.numVBOs] = (VBO_t *)ri.Hunk_Alloc(sizeof(*vbo), h_low); + vbo = tr.vbos[tr.numVBOs] = (VBO_t *)Hunk_Alloc(sizeof(*vbo), h_low); memset(vbo, 0, sizeof(*vbo)); @@ -140,7 +140,7 @@ IBO_t *R_CreateIBO(byte * indexes, int indexesSize, vboUsage_t usage) R_IssuePendingRenderCommands(); - ibo = tr.ibos[tr.numIBOs] = (IBO_t *)ri.Hunk_Alloc(sizeof(*ibo), h_low); + ibo = tr.ibos[tr.numIBOs] = (IBO_t *)Hunk_Alloc(sizeof(*ibo), h_low); ibo->indexesSize = indexesSize; qglGenBuffers(1, &ibo->indexesVBO); diff --git a/codemp/rd-rend2/tr_weather.cpp b/shared/rd-rend2/tr_weather.cpp similarity index 91% rename from codemp/rd-rend2/tr_weather.cpp rename to shared/rd-rend2/tr_weather.cpp index f3a5620673..a9d15767d2 100644 --- a/codemp/rd-rend2/tr_weather.cpp +++ b/shared/rd-rend2/tr_weather.cpp @@ -23,6 +23,9 @@ along with this program; if not, see . #include #include +// Cached to save test tume +int CurrentWeatherBrushIndex; + namespace { const int CHUNK_COUNT = 9; // in 3x3 arrangement @@ -473,13 +476,16 @@ void R_InitWeatherSystem() { Com_Printf("Initializing weather system\n"); tr.weatherSystem = - (weatherSystem_t *)Z_Malloc(sizeof(*tr.weatherSystem), TAG_R_TERRAIN, qtrue); + (weatherSystem_t *)R_Malloc(sizeof(*tr.weatherSystem), TAG_R_TERRAIN, qtrue); tr.weatherSystem->weatherSurface.surfaceType = SF_WEATHER; tr.weatherSystem->frozen = false; + tr.weatherSystem->shaking = false; tr.weatherSystem->activeWeatherTypes = 0; tr.weatherSystem->constWindDirection[0] = .0f; tr.weatherSystem->constWindDirection[1] = .0f; + CurrentWeatherBrushIndex = -1; + for (int i = 0; i < NUM_WEATHER_TYPES; i++) tr.weatherSystem->weatherSlots[i].active = false; } @@ -547,15 +553,40 @@ void R_AddWeatherBrush(uint8_t numPlanes, vec4_t *planes) tr.weatherSystem->numWeatherBrushes++; } +void R_LoadWeatherImages() +{ + if (!tr.weatherSystem) + return; + + // Image flags and type + imgType_t type = IMGTYPE_COLORALPHA; + int flags = IMGFLAG_CLAMPTOEDGE; + if (tr.hdrLighting) + flags |= IMGFLAG_SRGB; + + if (tr.weatherSystem->weatherSlots[WEATHER_RAIN].active) + tr.weatherSystem->weatherSlots[WEATHER_RAIN].drawImage = R_FindImageFile("gfx/world/rain.jpg", type, flags); + if (tr.weatherSystem->weatherSlots[WEATHER_SNOW].active) + tr.weatherSystem->weatherSlots[WEATHER_SNOW].drawImage = R_FindImageFile("gfx/effects/snowflake1", type, flags); + if (tr.weatherSystem->weatherSlots[WEATHER_SPACEDUST].active) + tr.weatherSystem->weatherSlots[WEATHER_SPACEDUST].drawImage = R_FindImageFile("gfx/effects/snowpuff1", type, flags); + if (tr.weatherSystem->weatherSlots[WEATHER_SAND].active) + tr.weatherSystem->weatherSlots[WEATHER_SAND].drawImage = R_FindImageFile("gfx/effects/alpha_smoke2b", type, flags); + if (tr.weatherSystem->weatherSlots[WEATHER_FOG].active) + tr.weatherSystem->weatherSlots[WEATHER_FOG].drawImage = R_FindImageFile("gfx/effects/alpha_smoke2b", type, flags); +} + void RE_WorldEffectCommand(const char *command) { if (!command) { return; } - +#ifndef REND2_SP COM_BeginParseSession("RE_WorldEffectCommand"); - +#else + COM_BeginParseSession(); +#endif const char *token;//, *origCommand; token = COM_ParseExt(&command, qfalse); @@ -682,9 +713,7 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_RAIN].velocityOrientationScale = 1.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_RAIN].drawImage = R_FindImageFile("gfx/world/rain.jpg", type, flags); + VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_RAIN].color, 0.5f, 0.5f, 0.5f, 0.5f); VectorScale( @@ -720,10 +749,6 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_RAIN].velocityOrientationScale = 1.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_RAIN].drawImage = R_FindImageFile("gfx/world/rain.jpg", type, flags); - VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_RAIN].color, 0.5f, 0.5f, 0.5f, 0.5f); VectorScale( tr.weatherSystem->weatherSlots[WEATHER_RAIN].color, @@ -763,9 +788,7 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_RAIN].velocityOrientationScale = 1.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_RAIN].drawImage = R_FindImageFile("gfx/world/rain.jpg", type, flags); + tr.weatherSystem->pain = 0.1f; VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_RAIN].color, 0.34f, 0.7f, 0.34f, 0.7f); VectorScale( @@ -801,10 +824,6 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_RAIN].velocityOrientationScale = 1.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_RAIN].drawImage = R_FindImageFile("gfx/world/rain", type, flags); - VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_RAIN].color, 0.5f, 0.5f, 0.5f, 0.5f); VectorScale( tr.weatherSystem->weatherSlots[WEATHER_RAIN].color, @@ -834,10 +853,6 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_SNOW].velocityOrientationScale = 0.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_SNOW].drawImage = R_FindImageFile("gfx/effects/snowflake1", type, flags); - VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_SNOW].color, 0.75f, 0.75f, 0.75f, 0.75f); VectorScale( tr.weatherSystem->weatherSlots[WEATHER_SNOW].color, @@ -882,9 +897,7 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_SPACEDUST].velocityOrientationScale = 0.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_SPACEDUST].drawImage = R_FindImageFile("gfx/effects/snowpuff1", type, flags); + VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_SPACEDUST].color, 0.75f, 0.75f, 0.75f, 0.75f); VectorScale( @@ -926,10 +939,6 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_SAND].velocityOrientationScale = 0.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_SAND].drawImage = R_FindImageFile("gfx/effects/alpha_smoke2b", type, flags); - VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_SAND].color, 0.9f, 0.6f, 0.0f, 0.5f); } @@ -963,10 +972,6 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_FOG].velocityOrientationScale = 0.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_FOG].drawImage = R_FindImageFile("gfx/effects/alpha_smoke2b", type, flags); - VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_FOG].color, 0.2f, 0.2f, 0.2f, 0.2f); VectorScale(tr.weatherSystem->weatherSlots[WEATHER_FOG].color, 0.2f, tr.weatherSystem->weatherSlots[WEATHER_FOG].color); } @@ -1004,10 +1009,6 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_FOG].velocityOrientationScale = 0.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_FOG].drawImage = R_FindImageFile("gfx/effects/alpha_smoke2b", type, flags); - VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_FOG].color, 0.3f, 0.3f, 0.3f, 0.3f); VectorScale(tr.weatherSystem->weatherSlots[WEATHER_FOG].color, 0.3f, tr.weatherSystem->weatherSlots[WEATHER_FOG].color); } @@ -1045,21 +1046,25 @@ void RE_WorldEffectCommand(const char *command) tr.weatherSystem->weatherSlots[WEATHER_FOG].velocityOrientationScale = 0.0f; - imgType_t type = IMGTYPE_COLORALPHA; - int flags = IMGFLAG_CLAMPTOEDGE; - tr.weatherSystem->weatherSlots[WEATHER_FOG].drawImage = R_FindImageFile("gfx/effects/alpha_smoke2b", type, flags); - VectorSet4(tr.weatherSystem->weatherSlots[WEATHER_FOG].color, 0.19f, 0.6f, 0.7f, 0.12f); VectorScale(tr.weatherSystem->weatherSlots[WEATHER_FOG].color, 0.12f, tr.weatherSystem->weatherSlots[WEATHER_FOG].color); } else if (Q_stricmp(token, "outsideshake") == 0) { +#ifdef REND2_SP + tr.weatherSystem->shaking = true; +#else ri.Printf(PRINT_DEVELOPER, "outsideshake isn't supported in MP\n"); +#endif } else if (Q_stricmp(token, "outsidepain") == 0) { +#ifdef REND2_SP + tr.weatherSystem->pain = !tr.weatherSystem->pain; +#else ri.Printf(PRINT_DEVELOPER, "outsidepain isn't supported in MP\n"); +#endif } else { @@ -1085,6 +1090,11 @@ void RE_WorldEffectCommand(const char *command) ri.Printf(PRINT_ALL, " outsideshake\n"); // not available in MP ri.Printf(PRINT_ALL, " outsidepain\n"); // not available in MP } +#ifdef REND2_SP + COM_EndParseSession(); +#endif + if (tr.world) + R_LoadWeatherImages(); } void R_WorldEffect_f(void) @@ -1256,3 +1266,123 @@ void RB_SurfaceWeather( srfWeather_t *surf ) } } } + +bool IsInsideBrush(const weatherBrushes_t* Brush, const vec3_t pos) +{ + // RBSP brushes actually store their bounding box in the first 6 planes! Nice + const vec3_t mins = { + -Brush->planes[0][3], + -Brush->planes[2][3], + -Brush->planes[4][3], + }; + const vec3_t maxs = { + Brush->planes[1][3], + Brush->planes[3][3], + Brush->planes[5][3], + }; + + return (pos[0] > mins[0] && pos[1] > mins[1] && pos[2] > mins[2] + && pos[0] < maxs[0] && pos[1] < maxs[1] && pos[2] < maxs[2]); +} + +bool R_IsOutside(vec3_t pos) +{ + if (!tr.weatherSystem) + { + return false; + } + + // check cashed brush first + if (CurrentWeatherBrushIndex >= 0 && CurrentWeatherBrushIndex < tr.weatherSystem->numWeatherBrushes) + { + if (IsInsideBrush(&tr.weatherSystem->weatherBrushes[CurrentWeatherBrushIndex], pos)) + { + return (tr.weatherSystem->weatherBrushType == WEATHER_BRUSHES_OUTSIDE); + } + } + + // check all weather brushes + for (int i = 0; i < tr.weatherSystem->numWeatherBrushes; i++) + { + const weatherBrushes_t* WeatherBrush = &tr.weatherSystem->weatherBrushes[i]; + + if (IsInsideBrush(WeatherBrush, pos)) + { + CurrentWeatherBrushIndex = i; + return (tr.weatherSystem->weatherBrushType == WEATHER_BRUSHES_OUTSIDE); + } + } + + CurrentWeatherBrushIndex = -1; + return (tr.weatherSystem->weatherBrushType != WEATHER_BRUSHES_OUTSIDE); +} + +bool R_IsShaking(vec3_t pos) +{ + return (tr.weatherSystem + && tr.weatherSystem->shaking + && R_IsOutside(pos)); +} + +bool R_GetWindVector(vec3_t windVector, vec3_t atPoint) +{ + // @TODO: need to process "Windzone" command + + if (!tr.weatherSystem) + { + return false; + } + + VectorCopy(tr.weatherSystem->windDirection, windVector); + VectorNormalize(windVector); + // everything is processed in RB_SurfaceWeather, no need to add something here + return (VectorLength(windVector) > 0.0f); +} + +bool R_GetWindGusting(vec3_t atPoint) +{ + if (!tr.weatherSystem) + return false; + + float windSpeed = VectorLength(tr.weatherSystem->windDirection); + return (windSpeed > 1.0f); +} + +float R_IsOutsideCausingPain(vec3_t pos) +{ + return (R_IsOutside(pos) && tr.weatherSystem->pain); +} + +float R_GetChanceOfSaberFizz() +{ + float chance = 0.0f; + int numWater = 0; + if (tr.weatherSystem->weatherSlots[WEATHER_RAIN].active) + { + chance += (tr.weatherSystem->weatherSlots[WEATHER_RAIN].gravity / 20.0f); + numWater++; + } + if (tr.weatherSystem->weatherSlots[WEATHER_SNOW].active) + { + chance += (tr.weatherSystem->weatherSlots[WEATHER_SNOW].gravity / 20.0f); + numWater++; + } + if (numWater) + { + return (chance / numWater); + } + return 0.0f; +} + +bool R_IsRaining() +{ + if (!tr.weatherSystem) + return false; + + return tr.weatherSystem->weatherSlots[WEATHER_RAIN].active; +} + +bool R_IsPuffing() +{ + return false; +} \ No newline at end of file diff --git a/codemp/rd-rend2/tr_weather.h b/shared/rd-rend2/tr_weather.h similarity index 87% rename from codemp/rd-rend2/tr_weather.h rename to shared/rd-rend2/tr_weather.h index b03b25275d..3ac889c455 100644 --- a/codemp/rd-rend2/tr_weather.h +++ b/shared/rd-rend2/tr_weather.h @@ -100,6 +100,8 @@ struct weatherSystem_t int activeWindObjects = 0; int numWeatherBrushes = 0; bool frozen; + bool shaking; + float pain = 0.0f; srfWeather_t weatherSurface; @@ -115,7 +117,15 @@ void R_InitWeatherSystem(); void R_InitWeatherForMap(); void R_AddWeatherSurfaces(); void R_AddWeatherBrush(uint8_t numPlanes, vec4_t *planes); +void R_LoadWeatherImages(); void R_ShutdownWeatherSystem(); void RB_SurfaceWeather( srfWeather_t *surfaceType ); - +bool R_IsOutside(vec3_t pos); +bool R_IsShaking(vec3_t pos); +float R_IsOutsideCausingPain(vec3_t pos); +float R_GetChanceOfSaberFizz(); +bool R_GetWindVector(vec3_t windVector, vec3_t atPoint); // doesn't work? +bool R_GetWindGusting(vec3_t atPoint); // doesn't work + +void RE_WorldEffectCommand(const char *cmd); void R_WorldEffect_f(void); diff --git a/codemp/rd-rend2/tr_world.cpp b/shared/rd-rend2/tr_world.cpp similarity index 98% rename from codemp/rd-rend2/tr_world.cpp rename to shared/rd-rend2/tr_world.cpp index 0204652440..bd21855a4e 100644 --- a/codemp/rd-rend2/tr_world.cpp +++ b/shared/rd-rend2/tr_world.cpp @@ -757,19 +757,19 @@ static const byte *R_ClusterPVS (int cluster) { R_inPVS ================= */ +#ifndef REND2_SP qboolean R_inPVS( const vec3_t p1, const vec3_t p2, byte *mask ) { - int leafnum; - int cluster; - - leafnum = ri.CM_PointLeafnum (p1); - cluster = ri.CM_LeafCluster (leafnum); +#else +qboolean R_inPVS(vec3_t p1, vec3_t p2) { + byte *mask; +#endif + mnode_t* leaf = R_PointInLeaf(p1); //agh, the damn snapshot mask doesn't work for this - mask = (byte *) ri.CM_ClusterPVS (cluster); + mask = (byte *) ri.CM_ClusterPVS (leaf->cluster); - leafnum = ri.CM_PointLeafnum (p2); - cluster = ri.CM_LeafCluster (leafnum); - if ( !(mask[cluster>>3] & (1<<(cluster&7))) ) + leaf = R_PointInLeaf(p2); + if ( !(mask[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) return qfalse; return qtrue;