From b468bf726c50d3126fac2664164e1be0d6e209c5 Mon Sep 17 00:00:00 2001 From: Kester Maddock Date: Mon, 7 Jun 2004 11:01:31 +0000 Subject: Lighting updates: Added specular after texture. Added Light properties: Negative, No Diffuse, No Specular, Quad, Quad2 --- .../BlenderRoutines/KX_BlenderRenderTools.cpp | 44 ++++++++++++++-------- .../Converter/BL_BlenderDataConversion.cpp | 17 +++++++-- .../GamePlayer/common/GPC_RenderTools.cpp | 40 ++++++++++++++++---- .../gameengine/GamePlayer/common/GPC_RenderTools.h | 8 +--- .../GamePlayer/ghost/GPG_Application.cpp | 2 +- source/gameengine/Ketsji/KX_Light.cpp | 9 +++++ source/gameengine/PyDoc/KX_Light.py | 14 +++++-- source/gameengine/Rasterizer/RAS_LightObject.h | 5 ++- .../RAS_GLExtensionManager.cpp | 32 +++++++++++++++- .../RAS_OpenGLRasterizer/RAS_GLExtensionManager.h | 7 +++- .../RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp | 5 +++ .../Rasterizer/RAS_OpenGLRasterizer/mkglext.py | 8 ++-- 12 files changed, 146 insertions(+), 45 deletions(-) diff --git a/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp b/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp index 125f3265305..f2315fef0fb 100644 --- a/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp +++ b/source/gameengine/BlenderRoutines/KX_BlenderRenderTools.cpp @@ -49,6 +49,7 @@ #include "RAS_IRasterizer.h" #include "RAS_LightObject.h" #include "RAS_ICanvas.h" +#include "RAS_GLExtensionManager.h" // next two includes/dependencies come from the shadow feature @@ -297,6 +298,9 @@ void KX_BlenderRenderTools::EnableOpenGLLights() glEnableClientState(GL_NORMAL_ARRAY); #endif //SLOWPAINT glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, false); + if (bgl::QueryExtension(bgl::_GL_EXT_separate_specular_color) || bgl::QueryVersion(1, 2)) + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); + } @@ -352,22 +356,18 @@ int KX_BlenderRenderTools::applyLights(int objectlayer) //std::vector m_lights; std::vector::iterator lit = m_lights.begin(); + glPushMatrix(); + glLoadMatrixf(m_viewmat); for (lit = m_lights.begin(), count = 0; !(lit==m_lights.end()) && count < m_numgllights; ++lit) { RAS_LightObject* lightdata = (*lit); if (lightdata->m_layer & objectlayer) { - - glPushMatrix(); - glLoadMatrixf(m_viewmat); - - vec[0] = (*(lightdata->m_worldmatrix))(0,3); vec[1] = (*(lightdata->m_worldmatrix))(1,3); vec[2] = (*(lightdata->m_worldmatrix))(2,3); vec[3] = 1; - if(lightdata->m_type==RAS_LightObject::LIGHT_SUN) { vec[0] = (*(lightdata->m_worldmatrix))(0,2); @@ -384,9 +384,9 @@ int KX_BlenderRenderTools::applyLights(int objectlayer) glLightfv((GLenum)(GL_LIGHT0+count), GL_POSITION, vec); glLightf((GLenum)(GL_LIGHT0+count), GL_CONSTANT_ATTENUATION, 1.0); glLightf((GLenum)(GL_LIGHT0+count), GL_LINEAR_ATTENUATION, lightdata->m_att1/lightdata->m_distance); - // without this next line it looks backward compatible. - //attennuation still is acceptable - // glLightf((GLenum)(GL_LIGHT0+count), GL_QUADRATIC_ATTENUATION, la->att2/(la->dist*la->dist)); + // without this next line it looks backward compatible. + //attennuation still is acceptable + glLightf((GLenum)(GL_LIGHT0+count), GL_QUADRATIC_ATTENUATION, lightdata->m_att2/(lightdata->m_distance*lightdata->m_distance)); if(lightdata->m_type==RAS_LightObject::LIGHT_SPOT) { vec[0] = -(*(lightdata->m_worldmatrix))(0,2); @@ -402,19 +402,33 @@ int KX_BlenderRenderTools::applyLights(int objectlayer) else glLightf((GLenum)(GL_LIGHT0+count), GL_SPOT_CUTOFF, 180.0); } - vec[0]= lightdata->m_energy*lightdata->m_red; - vec[1]= lightdata->m_energy*lightdata->m_green; - vec[2]= lightdata->m_energy*lightdata->m_blue; - vec[3]= 1.0; - glLightfv((GLenum)(GL_LIGHT0+count), GL_DIFFUSE, vec); + if (lightdata->m_nodiffuse) + { + vec[0] = vec[1] = vec[2] = vec[3] = 0.0; + } else { + vec[0]= lightdata->m_energy*lightdata->m_red; + vec[1]= lightdata->m_energy*lightdata->m_green; + vec[2]= lightdata->m_energy*lightdata->m_blue; + vec[3]= 1.0; + } + glLightfv((GLenum)(GL_LIGHT0+count), GL_DIFFUSE, vec); + if (lightdata->m_nospecular) + { + vec[0] = vec[1] = vec[2] = vec[3] = 0.0; + } else if (lightdata->m_nodiffuse) { + vec[0]= lightdata->m_energy*lightdata->m_red; + vec[1]= lightdata->m_energy*lightdata->m_green; + vec[2]= lightdata->m_energy*lightdata->m_blue; + vec[3]= 1.0; + } glLightfv((GLenum)(GL_LIGHT0+count), GL_SPECULAR, vec); glEnable((GLenum)(GL_LIGHT0+count)); count++; - glPopMatrix(); } } + glPopMatrix(); return count; diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp index 301c8115162..33a048be4dd 100644 --- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp +++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp @@ -359,7 +359,7 @@ RAS_MeshObject* BL_ConvertMesh(Mesh* mesh, Object* blenderobj, RAS_IRenderTools* } - Material* ma = give_current_material(blenderobj, 1); + Material* ma = give_current_material(blenderobj, 1 /* mface->mat_nr */); const char* matnameptr = (ma ? ma->id.name : ""); @@ -372,8 +372,8 @@ RAS_MeshObject* BL_ConvertMesh(Mesh* mesh, Object* blenderobj, RAS_IRenderTools* if (ma) { - polymat->m_specular = MT_Vector3(ma->spec * ma->specr,ma->spec * ma->specg,ma->spec * ma->specb); - polymat->m_shininess = (float)ma->har; + polymat->m_specular = MT_Vector3(ma->specr, ma->specg, ma->specb)*ma->spec; + polymat->m_shininess = (float)ma->har/4.0; // 0 < ma->har <= 512 } else { @@ -809,6 +809,7 @@ static KX_LightObject *gamelight_from_blamp(Lamp *la, unsigned int layerflag, KX KX_LightObject *gamelight; lightobj.m_att1 = la->att1; + lightobj.m_att2 = (la->mode & LA_QUAD)?la->att2:0.0; lightobj.m_red = la->r; lightobj.m_green = la->g; lightobj.m_blue = la->b; @@ -818,6 +819,16 @@ static KX_LightObject *gamelight_from_blamp(Lamp *la, unsigned int layerflag, KX lightobj.m_spotblend = la->spotblend; lightobj.m_spotsize = la->spotsize; + lightobj.m_nodiffuse = (la->mode & LA_NO_DIFF) != 0; + lightobj.m_nospecular = (la->mode & LA_NO_SPEC) != 0; + + if (la->mode & LA_NEG) + { + lightobj.m_red = -lightobj.m_red; + lightobj.m_green = -lightobj.m_green; + lightobj.m_blue = -lightobj.m_blue; + } + if (la->type==LA_SUN) { lightobj.m_type = RAS_LightObject::LIGHT_SUN; } else if (la->type==LA_SPOT) { diff --git a/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp b/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp index 4e28bb4a449..743af35ae9b 100644 --- a/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp +++ b/source/gameengine/GamePlayer/common/GPC_RenderTools.cpp @@ -52,6 +52,7 @@ #include "RAS_IRasterizer.h" #include "RAS_LightObject.h" #include "RAS_ICanvas.h" +#include "RAS_GLExtensionManager.h" // next two includes/dependencies come from the shadow feature // it needs the gameobject and the sumo physics scene for a raycast @@ -153,6 +154,15 @@ int GPC_RenderTools::ProcessLighting(int layer) return result; } +void GPC_RenderTools::EnableOpenGLLights() +{ + glEnable(GL_LIGHTING); + glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + glEnableClientState(GL_NORMAL_ARRAY); + if (bgl::QueryExtension(bgl::_GL_EXT_separate_specular_color) || bgl::QueryVersion(1, 2)) + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); +} void GPC_RenderTools::RenderText2D(RAS_TEXT_RENDER_MODE mode, const char* text, @@ -368,9 +378,9 @@ int GPC_RenderTools::applyLights(int objectlayer) glLightfv((GLenum)(GL_LIGHT0+count), GL_POSITION, vec); glLightf((GLenum)(GL_LIGHT0+count), GL_CONSTANT_ATTENUATION, 1.0); glLightf((GLenum)(GL_LIGHT0+count), GL_LINEAR_ATTENUATION, lightdata->m_att1/lightdata->m_distance); - // without this next line it looks backward compatible. - //attennuation still is acceptable - // glLightf((GLenum)(GL_LIGHT0+count), GL_QUADRATIC_ATTENUATION, la->att2/(la->dist*la->dist)); + // without this next line it looks backward compatible. + //attennuation still is acceptable + glLightf((GLenum)(GL_LIGHT0+count), GL_QUADRATIC_ATTENUATION, lightdata->m_att2/(lightdata->m_distance*lightdata->m_distance)); if(lightdata->m_type==RAS_LightObject::LIGHT_SPOT) { vec[0] = -(*(lightdata->m_worldmatrix))(0,2); @@ -386,11 +396,25 @@ int GPC_RenderTools::applyLights(int objectlayer) else glLightf((GLenum)(GL_LIGHT0+count), GL_SPOT_CUTOFF, 180.0); } - vec[0]= lightdata->m_energy*lightdata->m_red; - vec[1]= lightdata->m_energy*lightdata->m_green; - vec[2]= lightdata->m_energy*lightdata->m_blue; - vec[3]= 1.0; - glLightfv((GLenum)(GL_LIGHT0+count), GL_DIFFUSE, vec); + if (lightdata->m_nodiffuse) + { + vec[0] = vec[1] = vec[2] = vec[3] = 0.0; + } else { + vec[0]= lightdata->m_energy*lightdata->m_red; + vec[1]= lightdata->m_energy*lightdata->m_green; + vec[2]= lightdata->m_energy*lightdata->m_blue; + vec[3]= 1.0; + } + glLightfv((GLenum)(GL_LIGHT0+count), GL_DIFFUSE, vec); + if (lightdata->m_nospecular) + { + vec[0] = vec[1] = vec[2] = vec[3] = 0.0; + } else if (lightdata->m_nodiffuse) { + vec[0]= lightdata->m_energy*lightdata->m_red; + vec[1]= lightdata->m_energy*lightdata->m_green; + vec[2]= lightdata->m_energy*lightdata->m_blue; + vec[3]= 1.0; + } glLightfv((GLenum)(GL_LIGHT0+count), GL_SPECULAR, vec); glEnable((GLenum)(GL_LIGHT0+count)); diff --git a/source/gameengine/GamePlayer/common/GPC_RenderTools.h b/source/gameengine/GamePlayer/common/GPC_RenderTools.h index 432bf393e3a..9c76ccd478d 100644 --- a/source/gameengine/GamePlayer/common/GPC_RenderTools.h +++ b/source/gameengine/GamePlayer/common/GPC_RenderTools.h @@ -67,13 +67,7 @@ public: glDisableClientState(GL_NORMAL_ARRAY); } - void EnableOpenGLLights() - { - glEnable(GL_LIGHTING); - glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); - glEnable(GL_COLOR_MATERIAL); - glEnableClientState(GL_NORMAL_ARRAY); - } + void EnableOpenGLLights(); int ProcessLighting(int layer); diff --git a/source/gameengine/GamePlayer/ghost/GPG_Application.cpp b/source/gameengine/GamePlayer/ghost/GPG_Application.cpp index 4c3773effcd..868f9f1ea40 100644 --- a/source/gameengine/GamePlayer/ghost/GPG_Application.cpp +++ b/source/gameengine/GamePlayer/ghost/GPG_Application.cpp @@ -327,7 +327,7 @@ bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode) return false; // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0); - SYS_WriteCommandLineInt(syshandle, "vertexarrays",1); + // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1); //bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0); //bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0); bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0); diff --git a/source/gameengine/Ketsji/KX_Light.cpp b/source/gameengine/Ketsji/KX_Light.cpp index 9be4256f58f..f5ec21ee1b8 100644 --- a/source/gameengine/Ketsji/KX_Light.cpp +++ b/source/gameengine/Ketsji/KX_Light.cpp @@ -98,6 +98,9 @@ PyObject* KX_LightObject::_getattr(const STR_String& attr) if (attr == "lin_attenuation") return PyFloat_FromDouble(m_lightobj.m_att1); + if (attr == "quad_attenuation") + return PyFloat_FromDouble(m_lightobj.m_att2); + if (attr == "spotsize") return PyFloat_FromDouble(m_lightobj.m_spotsize); @@ -165,6 +168,12 @@ int KX_LightObject::_setattr(const STR_String& attr, PyObject *pyvalue) return 0; } + if (attr == "quad_attenuation") + { + m_lightobj.m_att2 = value; + return 0; + } + if (attr == "spotsize") { m_lightobj.m_spotsize = value; diff --git a/source/gameengine/PyDoc/KX_Light.py b/source/gameengine/PyDoc/KX_Light.py index 39b10a88f97..ff0cf071d2d 100644 --- a/source/gameengine/PyDoc/KX_Light.py +++ b/source/gameengine/PyDoc/KX_Light.py @@ -23,13 +23,19 @@ class KX_Light(KX_GameObject): @ivar NORMAL: A point light source. See attribute 'type' @ivar type: The type of light - must be SPOT, SUN or NORMAL - @ivar layer: The layer mask that this light affects object on. (bitfield) - @ivar energy: The brightness of this light. (float) - @ivar distance: The maximum distance this light can illuminate. (float) (SPOT and NORMAL lights only) + @ivar layer: The layer mask that this light affects object on. + @type layer: bitfield + @ivar energy: The brightness of this light. + @type energy: float + @ivar distance: The maximum distance this light can illuminate. (SPOT and NORMAL lights only) + @type distance: float @ivar colour: The colour of this light. Black = [0.0, 0.0, 0.0], White = [1.0, 1.0, 1.0] @type colour: list [r, g, b] @ivar color: Synonym for colour. - @ivar lin_attenuation: The linear component of this lights attenuation. (SPOT and NORMAL lights only) + @ivar lin_attenuation: The linear component of this light's attenuation. (SPOT and NORMAL lights only) + @type lin_attenuation: float + @ivar quad_attenuation: The quadratic component of this light's attenuation (SPOT and NORMAL lights only) + @type quad_attenuation: float @ivar spotsize: The cone angle of the spot light, in degrees. (float) (SPOT lights only) 0.0 <= spotsize <= 180.0. Spotsize = 360.0 is also accepted. @ivar spotblend: Specifies the intensity distribution of the spot light. (float) (SPOT lights only) diff --git a/source/gameengine/Rasterizer/RAS_LightObject.h b/source/gameengine/Rasterizer/RAS_LightObject.h index 0c313df80c8..2c22a588d76 100644 --- a/source/gameengine/Rasterizer/RAS_LightObject.h +++ b/source/gameengine/Rasterizer/RAS_LightObject.h @@ -42,7 +42,7 @@ struct RAS_LightObject LIGHT_NORMAL }; bool m_modified; - int m_layer; + int m_layer; float m_energy; float m_distance; @@ -52,12 +52,15 @@ struct RAS_LightObject float m_blue; float m_att1; + float m_att2; float m_spotsize; float m_spotblend; LightType m_type; MT_CmMatrix4x4* m_worldmatrix; + bool m_nodiffuse; + bool m_nospecular; }; #endif //__RAS_LIGHTOBJECT_H diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp index 2c7112e2e3c..26727b1d1e4 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.cpp @@ -265,7 +265,10 @@ bool QueryVersion(int major, int minor) if (gl_major == 0) { - STR_String gl_version = STR_String((const char *) glGetString(GL_VERSION)); + const char *gl_version_str = (const char *) glGetString(GL_VERSION); + if (!gl_version_str) + return false; + STR_String gl_version = STR_String(gl_version_str); int i = gl_version.Find('.'); gl_major = gl_version.Left(i).ToInt(); gl_minor = gl_version.Mid(i+1, gl_version.FindOneOf(". ", i+1) - i - 1).ToInt(); @@ -308,6 +311,11 @@ to their source. Cunning like a weasel. ******************************************************************************/ +#if defined(GL_ATI_pn_triangles) +PFNGLPNTRIANGLESIATIPROC glPNTrianglesiATI; +PFNGLPNTRIANGLESFATIPROC glPNTrianglesfATI; +#endif + } // namespace bgl @@ -327,6 +335,28 @@ static void LinkExtensions() static bool doDebugMessages = m_debug; extensions = STR_String((const char *) glGetString(GL_EXTENSIONS)).Explode(' '); +#if defined(GL_ATI_pn_triangles) + if (QueryExtension("GL_ATI_pn_triangles")) + { + glPNTrianglesiATI = reinterpret_cast(bglGetProcAddress((const GLubyte *) "glPNTrianglesiATI")); + glPNTrianglesfATI = reinterpret_cast(bglGetProcAddress((const GLubyte *) "glPNTrianglesfATI")); + if (glPNTrianglesiATI && glPNTrianglesfATI) { + EnableExtension(_GL_ATI_pn_triangles); + if (doDebugMessages) + std::cout << "Enabled GL_ATI_pn_triangles" << std::endl; + } else { + std::cout << "ERROR: GL_ATI_pn_triangles implementation is broken!" << std::endl; + } + } +#endif + + if (QueryExtension("GL_EXT_separate_specular_color")) + { + EnableExtension(_GL_EXT_separate_specular_color); + if (doDebugMessages) + std::cout << "Detected GL_EXT_separate_specular_color" << std::endl; + } + doDebugMessages = false; } diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.h b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.h index 50a67ee7d6b..e43912ec2a5 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.h +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_GLExtensionManager.h @@ -38,6 +38,7 @@ #include #endif +#include "EXT_separate_specular_color.h" namespace bgl { @@ -363,8 +364,12 @@ namespace bgl */ void InitExtensions(int debug); - +#if defined(GL_ATI_pn_triangles) +extern PFNGLPNTRIANGLESIATIPROC glPNTrianglesiATI; +extern PFNGLPNTRIANGLESFATIPROC glPNTrianglesfATI; +#endif } /* namespace bgl */ + #endif /* __RAS_GLEXTENSIONMANAGER_H__ */ diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp index 3e93a7a74b9..4cf930072c5 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/RAS_OpenGLRasterizer.cpp @@ -48,6 +48,8 @@ #include "MT_CmMatrix4x4.h" #include "RAS_IRenderTools.h" // rendering text +#include "RAS_GLExtensionManager.h" + RAS_OpenGLRasterizer::RAS_OpenGLRasterizer(RAS_ICanvas* canvas) :RAS_IRasterizer(canvas), @@ -126,6 +128,7 @@ static void Myinit_gl_stuff(void) } glPolygonStipple(patc); + } @@ -264,6 +267,8 @@ void RAS_OpenGLRasterizer::Exit() glBlendFunc(GL_ONE, GL_ZERO); glDisable(GL_LIGHTING); + if (bgl::QueryExtension(bgl::_GL_EXT_separate_specular_color) || bgl::QueryVersion(1, 2)) + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); EndFrame(); } diff --git a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/mkglext.py b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/mkglext.py index 912b4785da1..8e64ecd1741 100644 --- a/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/mkglext.py +++ b/source/gameengine/Rasterizer/RAS_OpenGLRasterizer/mkglext.py @@ -468,7 +468,7 @@ for i in glext_h: fns = [] fnlist = [] if (ext != ""): - line = re.search('.*(gl.*) \(.*\);', i) + line = re.search('.* (gl.*) \(.*\);', i) if (line): fns += [line.group(1)] line = re.search('.*PFN(.*)PROC.*', i) @@ -501,7 +501,7 @@ for i in glext_h: fns = [] fnlist = [] if (ext != ""): - line = re.search('.*(gl.*) \(.*\);', i) + line = re.search('.* (gl.*) \(.*\);', i) if (line): fns += [line.group(1)] line = re.search('.*PFN(.*)PROC.*', i) @@ -534,7 +534,7 @@ for i in glext_h: fns = [] fnlist = [] if (ext != ""): - line = re.search('.*(gl.*) \(.*\);', i) + line = re.search('.* (gl.*) \(.*\);', i) if (line): fns += [line.group(1)] line = re.search('.*PFN(.*)PROC.*', i) @@ -578,7 +578,7 @@ for i in glext_h: if (line): defines += [(line.group(1), line.group(2))] - line = re.search('(.*)(gl.*)(\(.*\));', i) # GLAPI void APIENTRY glMultiTexCoord2f (GLenum, GLfloat, GLfloat); + line = re.search('(.* )(gl.*)(\(.*\));', i) # GLAPI void APIENTRY glMultiTexCoord2f (GLenum, GLfloat, GLfloat); if (line): fns += [(line.group(1), line.group(2), line.group(3))] -- cgit v1.2.3