diff --git a/README.md b/README.md new file mode 100644 index 000000000..f04f324f7 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Welcome to GZDoom! + +[![Build Status](https://ci.appveyor.com/api/projects/status/github/coelckers/gzdoom?branch=master&svg=true)](https://ci.appveyor.com/project/coelckers/gzdoom) [![Build Status](https://travis-ci.org/coelckers/gzdoom.svg?branch=master)](https://travis-ci.org/coelckers/gzdoom) + +## GZDoom is a modder-friendly OpenGL source port based on the DOOM engine + +Copyright (c) 1998-2018 ZDoom + GZDoom teams, and contributors + +Doom Source (c) 1997 id Software, Raven Software, and contributors + +Please see license files for individual contributor licenses + +Special thanks to Coraline of the 3DGE team for allowing us to use her README.md as a template for this one. + +### Licensed under the GPL v3 (or greater) +##### https://www.gnu.org/licenses/quick-guide-gplv3.en.html +--- + +## How to build GZDoom + +To build GZDoom, please see the [wiki](https://zdoom.org/wiki/) and see the "Programmer's Corner" on the bottom-right corner of the page to build for your platform. + diff --git a/src/g_level.cpp b/src/g_level.cpp index 0b211f3e6..17f76a67d 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -2028,6 +2028,10 @@ DEFINE_FIELD(FLevelLocals, F1Pic) DEFINE_FIELD(FLevelLocals, maptype) DEFINE_FIELD(FLevelLocals, Music) DEFINE_FIELD(FLevelLocals, musicorder) +DEFINE_FIELD(FLevelLocals, skytexture1) +DEFINE_FIELD(FLevelLocals, skytexture2) +DEFINE_FIELD(FLevelLocals, skyspeed1) +DEFINE_FIELD(FLevelLocals, skyspeed2) DEFINE_FIELD(FLevelLocals, total_secrets) DEFINE_FIELD(FLevelLocals, found_secrets) DEFINE_FIELD(FLevelLocals, total_items) @@ -2093,3 +2097,19 @@ CCMD(skyfog) } } + +//========================================================================== +// +// ZScript counterpart to ACS ChangeSky, uses TextureIDs +// +//========================================================================== +DEFINE_ACTION_FUNCTION(FLevelLocals, ChangeSky) +{ + PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals); + PARAM_INT(sky1); + PARAM_INT(sky2); + sky1texture = self->skytexture1 = FSetTextureID(sky1); + sky2texture = self->skytexture2 = FSetTextureID(sky2); + R_InitSkyMap(); + return 0; +} diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 142358af6..463895e8c 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -161,12 +161,13 @@ void FGLRenderer::RenderScreenQuad() GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, FFlatVertexBuffer::PRESENT_INDEX, 4); } -void FGLRenderer::PostProcessScene(int fixedcm) +void FGLRenderer::PostProcessScene(int fixedcm, const std::function &afterBloomDrawEndScene2D) { mBuffers->BlitSceneToTexture(); UpdateCameraExposure(); mCustomPostProcessShaders->Run("beforebloom"); BloomScene(fixedcm); + afterBloomDrawEndScene2D(); TonemapScene(); ColormapScene(fixedcm); LensDistortScene(); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 8b6ba46df..eb71cd4be 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -7,6 +7,7 @@ #include "r_renderer.h" #include "r_data/matrix.h" #include "gl/dynlights/gl_shadowmap.h" +#include struct particle_t; class FCanvasTexture; @@ -43,6 +44,7 @@ class F2DDrawer; class FHardwareTexture; class FShadowMapShader; class FCustomPostProcessShaders; +class GLSceneDrawer; inline float DEG2RAD(float deg) { @@ -174,7 +176,7 @@ public: void RenderView(player_t* player); void RenderScreenQuad(); - void PostProcessScene(int fixedcm); + void PostProcessScene(int fixedcm, const std::function &afterBloomDrawEndScene2D); void AmbientOccludeScene(); void UpdateCameraExposure(); void BloomScene(int fixedcm); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 2405aff09..119444af6 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -177,6 +177,7 @@ bool FRenderState::ApplyShader() activeShader->muAlphaThreshold.Set(mAlphaThreshold); activeShader->muLightIndex.Set(mLightIndex); // will always be -1 for now activeShader->muClipSplit.Set(mClipSplit); + activeShader->muViewHeight.Set(viewheight); if (mGlowEnabled) { diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index bbead7737..a0bcb4882 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -681,10 +681,29 @@ void GLSceneDrawer::EndDrawScene(sector_t * viewsector) Reset3DViewport(); - // [BB] Only draw the sprites if we didn't render a HUD model before. - if ( renderHUDModel == false ) + // Delay drawing psprites until after bloom has been applied, if enabled. + if (!FGLRenderBuffers::IsEnabled() || !gl_bloom || FixedColormap != CM_DEFAULT) { - DrawPlayerSprites (viewsector, false); + DrawEndScene2D(viewsector); + } + else + { + // Restore standard rendering state + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.ResetColor(); + gl_RenderState.EnableTexture(true); + glDisable(GL_SCISSOR_TEST); + } +} + +void GLSceneDrawer::DrawEndScene2D(sector_t * viewsector) +{ + const bool renderHUDModel = gl_IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player); + + // [BB] Only draw the sprites if we didn't render a HUD model before. + if (renderHUDModel == false) + { + DrawPlayerSprites(viewsector, false); } if (gl.legacyMode) { @@ -706,7 +725,6 @@ void GLSceneDrawer::EndDrawScene(sector_t * viewsector) glDisable(GL_SCISSOR_TEST); } - //----------------------------------------------------------------------------- // // R_RenderView - renders one view - either the screen or a camera texture @@ -846,7 +864,7 @@ sector_t * GLSceneDrawer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, f if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures. if (mainview && FGLRenderBuffers::IsEnabled()) { - GLRenderer->PostProcessScene(FixedColormap); + GLRenderer->PostProcessScene(FixedColormap, [&]() { if (gl_bloom && FixedColormap == CM_DEFAULT) DrawEndScene2D(lviewsector); }); // This should be done after postprocessing, not before. GLRenderer->mBuffers->BindCurrentFB(); diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h index 9209e6dad..9ade64b73 100644 --- a/src/gl/scene/gl_scenedrawer.h +++ b/src/gl/scene/gl_scenedrawer.h @@ -55,6 +55,7 @@ public: void ProcessScene(bool toscreen = false); void DrawBlend(sector_t * viewsector); void EndDrawScene(sector_t * viewsector); + void DrawEndScene2D(sector_t * viewsector); void RenderActorsInPortal(FGLLinePortal *glport); void CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector); diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index edd0da1d9..e287c7215 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -73,7 +73,7 @@ CVAR(Bool, gl_billboard_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, gl_enhanced_nv_stealth, 3, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE) { - if (self < 0 || self > 7) self = 0; + if (self < 0 || self > 8) self = 0; } EXTERN_CVAR (Float, transsouls) diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index d551106a4..4af1ef5a1 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -254,6 +254,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muClipHeight.Init(hShader, "uClipHeight"); muClipHeightDirection.Init(hShader, "uClipHeightDirection"); muAlphaThreshold.Init(hShader, "uAlphaThreshold"); + muViewHeight.Init(hShader, "uViewHeight"); muTimer.Init(hShader, "timer"); lights_index = glGetUniformLocation(hShader, "lights"); @@ -392,6 +393,7 @@ static const FDefaultShader defaultshaders[]= {"Jagged Fuzz", "shaders/glsl/fuzz_jagged.fp"}, {"Noise Fuzz", "shaders/glsl/fuzz_noise.fp"}, {"Smooth Noise Fuzz", "shaders/glsl/fuzz_smoothnoise.fp"}, + {"Software Fuzz", "shaders/glsl/fuzz_software.fp"}, {NULL,NULL} }; diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index f41f7467b..6265d62a9 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -284,6 +284,7 @@ class FShader FBufferedUniform1f muClipHeight; FBufferedUniform1f muClipHeightDirection; FBufferedUniform1f muAlphaThreshold; + FBufferedUniform1i muViewHeight; FBufferedUniform1f muTimer; int lights_index; @@ -397,7 +398,7 @@ public: } }; -#define FIRST_USER_SHADER 12 +#define FIRST_USER_SHADER 13 enum { diff --git a/src/gl/stereo3d/gl_stereo_cvars.cpp b/src/gl/stereo3d/gl_stereo_cvars.cpp index 201e33590..47b7ff217 100644 --- a/src/gl/stereo3d/gl_stereo_cvars.cpp +++ b/src/gl/stereo3d/gl_stereo_cvars.cpp @@ -32,6 +32,7 @@ #include "gl/stereo3d/gl_sidebyside3d.h" #include "gl/stereo3d/gl_interleaved3d.h" #include "gl/system/gl_cvars.h" +#include "version.h" // Set up 3D-specific console variables: CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG) @@ -42,7 +43,10 @@ CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG) // For broadest GL compatibility, require user to explicitly enable quad-buffered stereo mode. // Setting vr_enable_quadbuffered_stereo does not automatically invoke quad-buffered stereo, // but makes it possible for subsequent "vr_mode 7" to invoke quad-buffered stereo -CVAR(Bool, vr_enable_quadbuffered, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Bool, vr_enable_quadbuffered, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + Printf("You must restart " GAMENAME " to switch quad stereo mode\n"); +} // intraocular distance in meters CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e2fccad14..3e5358a3c 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -9852,7 +9852,7 @@ scriptwait: if (STACK(2) != 0) { AActor *marine; - NActorIterator iterator("ScriptedMarine", STACK(2)); + NActorIterator iterator(NAME_ScriptedMarine, STACK(2)); while ((marine = iterator.Next()) != NULL) { @@ -9861,7 +9861,7 @@ scriptwait: } else { - if (activator != nullptr && activator->IsKindOf (PClass::FindClass("ScriptedMarine"))) + if (activator != nullptr && activator->IsKindOf (NAME_ScriptedMarine)) { SetMarineWeapon(activator, STACK(1)); } @@ -9878,7 +9878,7 @@ scriptwait: if (STACK(2) != 0) { AActor *marine; - NActorIterator iterator("ScriptedMarine", STACK(2)); + NActorIterator iterator(NAME_ScriptedMarine, STACK(2)); while ((marine = iterator.Next()) != NULL) { @@ -9887,7 +9887,7 @@ scriptwait: } else { - if (activator != nullptr && activator->IsKindOf("ScriptedMarine")) + if (activator != nullptr && activator->IsKindOf(NAME_ScriptedMarine)) { SetMarineSprite(activator, type); } diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index 369c5de61..fb4789e17 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -1659,7 +1659,7 @@ DEFINE_ACTION_FUNCTION(_Sector, NextLowestFloorAt) PARAM_SELF_STRUCT_PROLOGUE(sector_t); PARAM_INT(pos); PARAM_FLOAT(o); - self->SetXScale(pos, o); + self->SetYScale(pos, o); return 0; } diff --git a/src/p_trace.cpp b/src/p_trace.cpp index d00a5c4cc..fd79861ec 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -955,8 +955,8 @@ static bool EditTraceResult (uint32_t flags, FTraceResults &res) // [ZZ] here go the methods for the ZScript interface // //========================================================================== -IMPLEMENT_CLASS(DTracer, false, false) -DEFINE_FIELD_X(Tracer, DTracer, Results) +IMPLEMENT_CLASS(DLineTracer, false, false) +DEFINE_FIELD(DLineTracer, Results) // define TraceResults fields DEFINE_FIELD_NAMED_X(TraceResults, FTraceResults, Sector, HitSector) @@ -978,9 +978,9 @@ DEFINE_FIELD_X(TraceResults, FTraceResults, CrossedWaterPos) DEFINE_FIELD_X(TraceResults, FTraceResults, Crossed3DWater) DEFINE_FIELD_X(TraceResults, FTraceResults, Crossed3DWaterPos) -DEFINE_ACTION_FUNCTION(DTracer, Trace) +DEFINE_ACTION_FUNCTION(DLineTracer, Trace) { - PARAM_SELF_PROLOGUE(DTracer); + PARAM_SELF_PROLOGUE(DLineTracer); /*bool Trace(const DVector3 &start, sector_t *sector, const DVector3 &direction, double maxDist, ActorFlags ActorMask, uint32_t WallMask, AActor *ignore, FTraceResults &res, uint32_t traceFlags = 0, ETraceStatus(*callback)(FTraceResults &res, void *) = NULL, void *callbackdata = NULL);*/ @@ -1001,13 +1001,13 @@ DEFINE_ACTION_FUNCTION(DTracer, Trace) // Trace(vector3 start, Sector sector, vector3 direction, double maxDist, ETraceFlags traceFlags) bool res = Trace(DVector3(start_x, start_y, start_z), sector, DVector3(direction_x, direction_y, direction_z), maxDist, - (ActorFlag)0xFFFFFFFF, 0xFFFFFFFF, nullptr, self->Results, traceFlags, &DTracer::TraceCallback, self); + (ActorFlag)0xFFFFFFFF, 0xFFFFFFFF, nullptr, self->Results, traceFlags, &DLineTracer::TraceCallback, self); ACTION_RETURN_BOOL(res); } -ETraceStatus DTracer::TraceCallback(FTraceResults& res, void* pthis) +ETraceStatus DLineTracer::TraceCallback(FTraceResults& res, void* pthis) { - DTracer* self = (DTracer*)pthis; + DLineTracer* self = (DLineTracer*)pthis; // "res" here should refer to self->Results anyway. // patch results a bit. modders don't expect it to work like this most likely. @@ -1036,13 +1036,13 @@ ETraceStatus DTracer::TraceCallback(FTraceResults& res, void* pthis) return self->CallZScriptCallback(); } -ETraceStatus DTracer::CallZScriptCallback() +ETraceStatus DLineTracer::CallZScriptCallback() { - IFVIRTUAL(DTracer, TraceCallback) + IFVIRTUAL(DLineTracer, TraceCallback) { int status; VMReturn results[1] = { &status }; - VMValue params[1] = { (DTracer*)this }; + VMValue params[1] = { (DLineTracer*)this }; VMCall(func, params, 1, results, 1); return (ETraceStatus)status; } diff --git a/src/p_trace.h b/src/p_trace.h index af4187f78..0a61cc725 100644 --- a/src/p_trace.h +++ b/src/p_trace.h @@ -113,9 +113,9 @@ bool Trace(const DVector3 &start, sector_t *sector, const DVector3 &direction, d ETraceStatus(*callback)(FTraceResults &res, void *) = NULL, void *callbackdata = NULL); // [ZZ] this is the object that's used for ZScript -class DTracer : public DObject +class DLineTracer : public DObject { - DECLARE_CLASS(DTracer, DObject) + DECLARE_CLASS(DLineTracer, DObject) public: FTraceResults Results; static ETraceStatus TraceCallback(FTraceResults& res, void* pthis); diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 7b5eed9a0..501436552 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2729,7 +2729,7 @@ GLPREFMNU_AMBLIGHT = "Ambient light level"; GLPREFMNU_RENDERQUALITY = "Rendering quality"; GLPREFMNU_MENUBLUR = "Menu Blur"; GLPREFMNU_VRMODE = "Stereo 3D VR"; -GLPREFMNU_VRQUADSTEREO = "Enable Quad Stereo (Requires Restart)"; +GLPREFMNU_VRQUADSTEREO = "Enable Quad Stereo"; GLPREFMNU_MULTISAMPLE = "Multisample"; GLPREFMNU_TONEMAP = "Tonemap Mode"; GLPREFMNU_BLOOM = "Bloom effect"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 3c727247f..6e02c1d5e 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2120,6 +2120,7 @@ OptionValue "FuzzStyle" 4, "$OPTVAL_TRANSLUCENTFUZZ" 6, "$OPTVAL_NOISE" 7, "$OPTVAL_SMOOTHNOISE" + 8, "$OPTVAL_SOFTWARE" //5, "$OPTVAL_JAGGEDFUZZ" I can't see any difference between this and 4 so it's disabled for now. } diff --git a/wadsrc/static/shaders/glsl/fuzz_software.fp b/wadsrc/static/shaders/glsl/fuzz_software.fp new file mode 100644 index 000000000..b17b49b8a --- /dev/null +++ b/wadsrc/static/shaders/glsl/fuzz_software.fp @@ -0,0 +1,52 @@ +// Fuzz effect as rendered by the software renderer +uniform float timer; + +#define FUZZTABLE 50 +#define FUZZ_RANDOM_X_SIZE 100 +#define FRACBITS 16 +#define fixed_t int + +int fuzz_random_x_offset[FUZZ_RANDOM_X_SIZE] = int[] +( + 75, 76, 21, 91, 56, 33, 62, 99, 61, 79, + 95, 54, 41, 18, 69, 43, 49, 59, 10, 84, + 94, 17, 57, 46, 9, 39, 55, 34,100, 81, + 73, 88, 92, 3, 63, 36, 7, 28, 13, 80, + 16, 96, 78, 29, 71, 58, 89, 24, 1, 35, + 52, 82, 4, 14, 22, 53, 38, 66, 12, 72, + 90, 44, 77, 83, 6, 27, 48, 30, 42, 32, + 65, 15, 97, 20, 67, 74, 98, 85, 60, 68, + 19, 26, 8, 87, 86, 64, 11, 37, 31, 47, + 25, 5, 50, 51, 23, 2, 93, 70, 40, 45 +); + +int fuzzoffset[FUZZTABLE] = int[] +( + 6, 11, 6, 11, 6, 6, 11, 6, 6, 11, + 6, 6, 6, 11, 6, 6, 6, 11, 15, 18, + 21, 6, 11, 15, 6, 6, 6, 6, 11, 6, + 11, 6, 6, 11, 15, 6, 6, 11, 15, 18, + 21, 6, 6, 6, 6, 11, 6, 6, 11, 6 +); + +vec4 ProcessTexel() +{ + vec2 texCoord = vTexCoord.st; + vec4 basicColor = getTexel(texCoord); + + // Ideally fuzzpos would be an uniform and differ from each sprite so that overlapping demons won't get the same shade for the same pixel + int next_random = int(abs(mod(timer * 35.0, float(FUZZ_RANDOM_X_SIZE)))); + int fuzzpos = (/*fuzzpos +*/ fuzz_random_x_offset[next_random] * FUZZTABLE / 100) % FUZZTABLE; + + int x = int(gl_FragCoord.x); + int y = int(gl_FragCoord.y); + + fixed_t fuzzscale = (200 << FRACBITS) / uViewHeight; + int scaled_x = (x * fuzzscale) >> FRACBITS; + int fuzz_x = fuzz_random_x_offset[scaled_x % FUZZ_RANDOM_X_SIZE] + fuzzpos; + fixed_t fuzzcount = FUZZTABLE << FRACBITS; + fixed_t fuzz = ((fuzz_x << FRACBITS) + y * fuzzscale) % fuzzcount; + float alpha = float(fuzzoffset[fuzz >> FRACBITS]) / 32.0; + + return vec4(0.0, 0.0, 0.0, basicColor.a * alpha); +} diff --git a/wadsrc/static/shaders/glsl/shaderdefs.i b/wadsrc/static/shaders/glsl/shaderdefs.i index c8fcf7b6b..15d68428e 100644 --- a/wadsrc/static/shaders/glsl/shaderdefs.i +++ b/wadsrc/static/shaders/glsl/shaderdefs.i @@ -48,6 +48,9 @@ uniform float uGlobVis; // uGlobVis = R_GetGlobVis(r_visibility) / 32.0 // dynamic lights uniform int uLightIndex; +// Software fuzz scaling +uniform int uViewHeight; + // quad drawer stuff #ifdef USE_QUAD_DRAWER uniform mat4 uQuadVertices; diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 12be10d64..d2c923b39 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -524,7 +524,7 @@ struct TraceResults native native vector3 Crossed3DWaterPos; } -class Tracer : Object native +class LineTracer : Object native { native @TraceResults Results; native bool Trace(vector3 start, Sector sec, vector3 direction, double maxDist, ETraceFlags traceFlags); @@ -591,6 +591,10 @@ struct LevelLocals native native readonly int maptype; native readonly String Music; native readonly int musicorder; + native readonly TextureID skytexture1; + native readonly TextureID skytexture2; + native float skyspeed1; + native float skyspeed2; native int total_secrets; native int found_secrets; native int total_items; @@ -640,8 +644,10 @@ struct LevelLocals native native static clearscope vector3 Vec3Diff(vector3 v1, vector3 v2); native clearscope String GetChecksum() const; - - clearscope String TimeFormatted(bool totals = false) + + native void ChangeSky( TextureID sky1, TextureID sky2 ); + + String clearscope TimeFormatted(bool totals = false) { int sec = Thinker.Tics2Seconds(totals? totaltime : time); return String.Format("%02d:%02d:%02d", sec / 3600, (sec % 3600) / 60, sec % 60);