diff --git a/common/lc_colors.cpp b/common/lc_colors.cpp index c870c838..e0ec391c 100644 --- a/common/lc_colors.cpp +++ b/common/lc_colors.cpp @@ -85,6 +85,8 @@ static std::vector lcParseColorFile(lcFile& File) Color.Code = ~0U; Color.Translucent = false; + Color.Chrome = false; + Color.Rubber = false; Color.Group = LC_COLORGROUP_SOLID; Color.Value[0] = FLT_MAX; Color.Value[1] = FLT_MAX; @@ -157,8 +159,17 @@ static std::vector lcParseColorFile(lcFile& File) else if (Value != 0) Color.Group = LC_COLORGROUP_SPECIAL; } - else if (!strcmp(Token, "CHROME") || !strcmp(Token, "PEARLESCENT") || !strcmp(Token, "RUBBER") || - !strcmp(Token, "MATTE_METALIC") || !strcmp(Token, "METAL") || !strcmp(Token, "LUMINANCE")) + else if (!strcmp(Token, "CHROME")) + { + Color.Chrome = true; + Color.Group = LC_COLORGROUP_SPECIAL; + } + else if (!strcmp(Token, "RUBBER")) + { + Color.Rubber = true; + Color.Group = LC_COLORGROUP_SPECIAL; + } + else if (!strcmp(Token, "PEARLESCENT") || !strcmp(Token, "MATTE_METALIC") || !strcmp(Token, "METAL") || !strcmp(Token, "LUMINANCE")) { Color.Group = LC_COLORGROUP_SPECIAL; } @@ -236,6 +247,8 @@ bool lcLoadColorFile(lcFile& File, lcStudStyle StudStyle) MainColor.Code = 16; MainColor.Translucent = false; + MainColor.Chrome = false; + MainColor.Rubber = false; MainColor.Group = LC_COLORGROUP_SOLID; MainColor.Value[0] = 1.0f; MainColor.Value[1] = 1.0f; @@ -257,6 +270,8 @@ bool lcLoadColorFile(lcFile& File, lcStudStyle StudStyle) EdgeColor.Code = 24; EdgeColor.Translucent = false; + EdgeColor.Chrome = false; + EdgeColor.Rubber = false; EdgeColor.Group = LC_NUM_COLORGROUPS; EdgeColor.Value[0] = 0.5f; EdgeColor.Value[1] = 0.5f; @@ -279,6 +294,8 @@ bool lcLoadColorFile(lcFile& File, lcStudStyle StudStyle) StudCylinderColor.Code = LC_STUD_CYLINDER_COLOR_CODE; StudCylinderColor.Translucent = false; + StudCylinderColor.Chrome = false; + StudCylinderColor.Rubber = false; StudCylinderColor.Group = LC_NUM_COLORGROUPS; StudCylinderColor.Value = lcVector4FromColor(Preferences.mStudCylinderColor); StudCylinderColor.Edge = lcVector4FromColor(Preferences.mPartEdgeColor); @@ -294,6 +311,8 @@ bool lcLoadColorFile(lcFile& File, lcStudStyle StudStyle) NoColor.Code = LC_COLOR_NOCOLOR; NoColor.Translucent = false; + NoColor.Chrome = false; + NoColor.Rubber = false; NoColor.Group = LC_NUM_COLORGROUPS; NoColor.Value[0] = 0.5f; NoColor.Value[1] = 0.5f; @@ -362,6 +381,8 @@ int lcGetColorIndex(quint32 ColorCode) Color.Code = ColorCode; Color.Translucent = false; + Color.Chrome = false; + Color.Rubber = false; Color.Edge[0] = 0.2f; Color.Edge[1] = 0.2f; Color.Edge[2] = 0.2f; diff --git a/common/lc_colors.h b/common/lc_colors.h index c2559d0a..90fa9cf5 100644 --- a/common/lc_colors.h +++ b/common/lc_colors.h @@ -13,6 +13,8 @@ struct lcColor quint32 Code; int Group; bool Translucent = false; + bool Chrome = false; + bool Rubber = false; bool Adjusted = false; lcVector4 Value; lcVector4 Edge; @@ -64,3 +66,13 @@ inline bool lcIsColorTranslucent(size_t ColorIndex) { return gColorList[ColorIndex].Translucent; } + +inline bool lcIsColorChrome(size_t ColorIndex) +{ + return gColorList[ColorIndex].Chrome; +} + +inline bool lcIsColorRubber(size_t ColorIndex) +{ + return gColorList[ColorIndex].Rubber; +} diff --git a/common/lc_mesh.cpp b/common/lc_mesh.cpp index 79ae599c..388f4bc9 100644 --- a/common/lc_mesh.cpp +++ b/common/lc_mesh.cpp @@ -305,14 +305,14 @@ void lcMesh::ExportPOVRay(lcFile& File, const char* MeshName, const char** Color const lcVector3 n2 = lcUnpackNormal(Verts[Indices[Idx + 1]].Normal); const lcVector3 n3 = lcUnpackNormal(Verts[Indices[Idx + 2]].Normal); - sprintf(Line, " smooth_triangle { <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f>, <%.2f, %.2f, %.2f> }\n", + sprintf(Line, " smooth_triangle { <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g> }\n", -v1.y, -v1.x, v1.z, -n1.y, -n1.x, n1.z, -v2.y, -v2.x, v2.z, -n2.y, -n2.x, n2.z, -v3.y, -v3.x, v3.z, -n3.y, -n3.x, n3.z); File.WriteLine(Line); } if (Section->ColorIndex != gDefaultColor) { - sprintf(Line, "material { texture { %s normal { bumps 0.1 scale 2 } } }", ColorTable[Section->ColorIndex]); + sprintf(Line, " material { texture { %s normal { bumps 0.1 scale 2 } } }", ColorTable[Section->ColorIndex]); File.WriteLine(Line); } diff --git a/common/project.cpp b/common/project.cpp index 5d7cc23c..1190cd79 100644 --- a/common/project.cpp +++ b/common/project.cpp @@ -7,6 +7,7 @@ #include "project.h" #include "lc_instructions.h" #include "image.h" +#include "light.h" #include "lc_mainwindow.h" #include "lc_view.h" #include "lc_library.h" @@ -1828,15 +1829,6 @@ bool Project::ExportPOVRay(const QString& FileName) return false; } - POVFile.WriteLine("#version 3.7;\n\nglobal_settings {\n assumed_gamma 1.0\n}\n\n"); - - char Line[1024]; - - lcPiecesLibrary* Library = lcGetPiecesLibrary(); - std::map> PieceTable; - size_t NumColors = gColorList.size(); - std::vector> ColorTable(NumColors); - enum { LGEO_PIECE_LGEO = 0x01, @@ -1855,10 +1847,386 @@ bool Project::ExportPOVRay(const QString& FileName) LGEO_COLOR_GLITTER = 0x40 }; - QString LGEOPath; // todo: load lgeo from registry and make sure it still works + char Line[1024]; - if (!LGEOPath.isEmpty()) + sprintf(Line, "// Generated By: LeoCAD %s\n// LDraw File: %s\n// Date: %s\n\n", + LC_VERSION_TEXT, + mModels[0]->GetProperties().mFileName.toLatin1().constData(), + QDateTime::currentDateTime().toString(Qt::ISODate).toLatin1().constData()); + POVFile.WriteLine(Line); + + POVFile.WriteLine("#version 3.7;\n\n"); + + POVFile.WriteLine("global_settings { assumed_gamma 1.0 }\n\n"); + + /* POV-Ray uses a left-handed coordinate system + * - positive x-axis pointing right + * - positive y-axis pointing up + * - positive z-axis pointing away from you + * POVRay [0]x,[1]y,[2]z = LeoCAD [1]y,[0]x,[2]z */ + + lcPiecesLibrary* Library = lcGetPiecesLibrary(); + std::map> PieceTable; + size_t NumColors = gColorList.size(); + std::vector> LgeoColorTable(NumColors); + std::vector> ColorTable(NumColors); + + const lcArray Lights = gMainWindow->GetActiveModel()->GetLights(); + const lcCamera* Camera = gMainWindow->GetActiveView()->GetCamera(); + const QString CameraName = QString(Camera->GetName()).replace(" ","_"); + const lcVector3& Position = Camera->mPosition; + const lcVector3& Target = Camera->mTargetPosition; + const lcVector3& Up = Camera->mUpVector; + const lcVector3 BackgroundColor = lcVector3FromColor(lcGetPreferences().mBackgroundSolidColor); + const lcPOVRayOptions& POVRayOptions = mModels[0]->GetPOVRayOptions(); + const QString TopModelName = QString("LC_%1").arg(QString(mModels[0]->GetFileName()).replace(" ","_").replace(".","_dot_")); + const QString LGEOPath = lcGetProfileString(LC_PROFILE_POVRAY_LGEO_PATH); + const bool UseLGEO = POVRayOptions.UseLGEO && !LGEOPath.isEmpty(); + const int TopModelColorCode = 7; + QStringList ColorMacros, MaterialColors; + + lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX); + lcVector3 Max(-FLT_MAX, -FLT_MAX, -FLT_MAX); + + for (const lcModelPartsEntry& ModelPart : ModelParts) { + lcVector3 Points[8]; + + lcGetBoxCorners(ModelPart.Info->GetBoundingBox(), Points); + + for (int PointIdx = 0; PointIdx < 8; PointIdx++) + { + lcVector3 Point = lcMul31(Points[PointIdx], ModelPart.WorldMatrix); + + Min = lcMin(Point, Min); + Max = lcMax(Point, Max); + } + } + + lcVector3 Center = (Min + Max) / 2.0f; + float Radius = (Max - Center).Length() / 25.0f; + Center = lcVector3(Center[1], Center[0], Center[2]) / 25.0f; + + lcVector3 FloorColor = POVRayOptions.FloorColor; + float FloorAmbient = POVRayOptions.FloorAmbient; + float FloorDiffuse = POVRayOptions.FloorDiffuse; + char FloorLocation[32]; + char FloorAxis[16]; + if (POVRayOptions.FloorAxis == 0) + { + sprintf(FloorAxis, "x"); + sprintf(FloorLocation, "MaxX"); + } + else if (POVRayOptions.FloorAxis == 1) + { + sprintf(FloorAxis, "y"); + sprintf(FloorLocation, "MaxY"); + } + else + { + sprintf(FloorAxis, "z"); + sprintf(FloorLocation, "MaxZ"); + } + + for (const lcLight* Light : Lights) + { + if (Light->mLightType == LC_AREALIGHT) + { + if (FloorColor == lcVector3(0.8f,0.8f,0.8f)) + FloorColor = {1.0f,1.0f,1.0f}; + if (FloorAmbient == 0.4f) + FloorAmbient = 0.0f; + if (FloorDiffuse == 0.4f) + FloorDiffuse = 0.9f; + break; + } + } + + if (!POVRayOptions.HeaderIncludeFile.isEmpty()) + { + sprintf(Line, "#include \"%s\"\n\n", POVRayOptions.HeaderIncludeFile.toLatin1().constData()); + POVFile.WriteLine(Line); + } + + sprintf(Line, + "#ifndef (MinX) #declare MinX = %g; #end\n" + "#ifndef (MinY) #declare MinY = %g; #end\n" + "#ifndef (MinZ) #declare MinZ = %g; #end\n" + "#ifndef (MaxX) #declare MaxX = %g; #end\n" + "#ifndef (MaxY) #declare MaxY = %g; #end\n" + "#ifndef (MaxZ) #declare MaxZ = %g; #end\n" + "#ifndef (CenterX) #declare CenterX = %g; #end\n" + "#ifndef (CenterY) #declare CenterY = %g; #end\n" + "#ifndef (CenterZ) #declare CenterZ = %g; #end\n" + "#ifndef (Center) #declare Center = ; #end\n" + "#ifndef (Radius) #declare Radius = %g; #end\n", + Min[0], Min[1], Min[2], Max[0], -Max[1], Max[2], Center[0], Center[1], Center[2], Radius); + POVFile.WriteLine(Line); + sprintf(Line, + "#ifndef (CameraSky) #declare CameraSky = <%g,%g,%g>; #end\n" + "#ifndef (CameraLocation) #declare CameraLocation = <%g, %g, %g>; #end\n" + "#ifndef (CameraTarget) #declare CameraTarget = <%g, %g, %g>; #end\n" + "#ifndef (CameraAngle) #declare CameraAngle = %g; #end\n", + Up[1], Up[0], Up[2], Position[1] / 25.0f, Position[0] / 25.0f, Position[2] / 25.0f, Target[1] / 25.0f, Target[0] / 25.0f, Target[2] / 25.0f, Camera->m_fovy); + POVFile.WriteLine(Line); + sprintf(Line, + "#ifndef (BackgroundColor) #declare BackgroundColor = <%1g, %1g, %1g>; #end\n" + "#ifndef (Background) #declare Background = %s; #end\n", + BackgroundColor[0], BackgroundColor[1], BackgroundColor[2], (POVRayOptions.ExcludeBackground ? "false" : "true")); + POVFile.WriteLine(Line); + sprintf(Line, + "#ifndef (FloorAxis) #declare FloorAxis = %s; #end\n" + "#ifndef (FloorLocation) #declare FloorLocation = %s; #end\n" + "#ifndef (FloorColor) #declare FloorColor = <%1g, %1g, %1g>; #end\n" + "#ifndef (FloorAmbient) #declare FloorAmbient = %1g; #end\n" + "#ifndef (FloorDiffuse) #declare FloorDiffuse = %1g; #end\n" + "#ifndef (Floor) #declare Floor = %s; #end\n", + FloorAxis, FloorLocation, FloorColor[0], FloorColor[1], FloorColor[2], FloorAmbient, FloorDiffuse, (POVRayOptions.ExcludeFloor ? "false" : "true")); + POVFile.WriteLine(Line); + sprintf(Line, + "#ifndef (Ambient) #declare Ambient = 0.4; #end\n" + "#ifndef (Diffuse) #declare Diffuse = 0.4; #end\n" + "#ifndef (Reflection) #declare Reflection = 0.08; #end\n" + "#ifndef (Phong) #declare Phong = 0.5; #end\n" + "#ifndef (PhongSize) #declare PhongSize = 40; #end\n" + "#ifndef (TransReflection) #declare TransReflection = 0.2; #end\n" + "#ifndef (TransFilter) #declare TransFilter = 0.85; #end\n" + "#ifndef (TransIoR) #declare TransIoR = 1.25; #end\n" + "#ifndef (RubberReflection) #declare RubberReflection = 0; #end\n" + "#ifndef (RubberPhong) #declare RubberPhong = 0.1; #end\n" + "#ifndef (RubberPhongS) #declare RubberPhongS = 10; #end\n" + "#ifndef (ChromeReflection) #declare ChromeReflection = 0.85; #end\n" + "#ifndef (ChromeBrilliance) #declare ChromeBrilliance = 5; #end\n" + "#ifndef (ChromeSpecular) #declare ChromeSpecular = 0.8; #end\n" + "#ifndef (ChromeRough) #declare ChromeRough = 0.01; #end\n" + "#ifndef (OpaqueNormal) #declare OpaqueNormal = normal { bumps 0.001 scale 0.5 }; #end\n" + "#ifndef (TransNormal) #declare TransNormal = normal { bumps 0.001 scale 0.5 }; #end\n"); + POVFile.WriteLine(Line); + sprintf(Line, + "#ifndef (Quality) #declare Quality = 3; #end\n" + "#ifndef (Studs) #declare Studs = 1; #end\n" + "#ifndef (LgeoLibrary) #declare LgeoLibrary = %s; #end\n" + "#ifndef (ModelReflection) #declare ModelReflection = %i; #end\n" + "#ifndef (ModelShadow) #declare ModelShadow = %i; #end\n\n", + (POVRayOptions.UseLGEO ? "true" : "false"), (POVRayOptions.NoReflection ? 0 : 1), (POVRayOptions.NoShadow ? 0 : 1)); + POVFile.WriteLine(Line); + + sprintf(Line, + "#ifndef (SkipWriteLightMacro)\n" + "#macro WriteLight(Type, Shadowless, Location, Target, Color, Power, SpotRadius, SpotFalloff, SpotTightness, AreaCircle, AreaWidth, AreaHeight, AreaRows, AreaColumns)\n" + " #local PointLight = %i;\n" + " #local AreaLight = %i;\n" + " #local SunLight = %i;\n" + " #local SpotLight = %i;\n" + " light_source {\n" + " Location\n" + " color rgb Color*Power\n" + " #if (Shadowless > 0)\n" + " shadowless\n" + " #end\n" + " #if (Type = AreaLight)\n" + " area_light AreaWidth, AreaHeight, AreaRows, AreaColumns\n" + " jitter\n" + " #if (AreaCircle > 0 & AreaWidth > 2 & AreaHeight > 2 & AreaRows > 1 & AreaColumns > 1 )\n" + " circular \n" + " #if (AreaWidth = AreaHeight & AreaRows = AreaColumns)\n" + " orient\n" + " #end\n" + " #end\n" + " #elseif (Type = SunLight)\n" + " parallel\n" + " point_at Target\n" + " #elseif (Type = SpotLight)\n" + " spotlight\n" + " radius SpotRadius\n" + " falloff SpotFalloff\n" + " tightness SpotTightness\n" + " point_at Target\n" + " #end\n" + " }\n" + "#end\n" + "#end\n\n", + LC_POINTLIGHT, LC_AREALIGHT, LC_SUNLIGHT, LC_SPOTLIGHT); + POVFile.WriteLine(Line); + + for (const lcModelPartsEntry& ModelPart : ModelParts) + { + int ColorIdx = ModelPart.ColorIndex; + + if (lcIsColorTranslucent(ColorIdx)) + { + if (!ColorMacros.contains("TranslucentColor")) + { + sprintf(Line, + "#ifndef (SkipTranslucentColorMacro)\n" + "#macro TranslucentColor(r, g, b, f)\n" + " material {\n" + " texture {\n" + " pigment { srgbf }\n" + " finish { emission 0 ambient Ambient diffuse Diffuse }\n" + " finish { phong Phong phong_size PhongSize reflection TransReflection }\n" + " normal { TransNormal }\n" + " }\n" + " interior { ior TransIoR }\n" + " }\n" + "#end\n" + "#end\n\n"); + POVFile.WriteLine(Line); + ColorMacros.append("TranslucentColor"); + } + } + else if (lcIsColorChrome(ColorIdx)) + { + if (!ColorMacros.contains("ChromeColor")) + { + sprintf(Line, + "#ifndef (SkipChromeColorMacro)\n" + "#macro ChromeColor(r, g, b)\n" + "#if (LgeoLibrary) material { #end\n" + " texture {\n" + " pigment { srgbf }\n" + " finish { emission 0 ambient Ambient diffuse Diffuse }\n" + " finish { phong Phong phong_size PhongSize reflection ChromeReflection brilliance ChromeBrilliance metallic specular ChromeSpecular roughness ChromeRough }\n" + " }\n" + "#if (LgeoLibrary) } #end\n" + "#end\n" + "#end\n\n"); + POVFile.WriteLine(Line); + ColorMacros.append("ChromeColor"); + } + } + else if (lcIsColorRubber(ColorIdx)) + { + if (!ColorMacros.contains("RubberColor")) + { + sprintf(Line, + "#ifndef (SkipRubberColorMacro)\n" + "#macro RubberColor(r, g, b)\n" + "#if (LgeoLibrary) material { #end\n" + " texture {\n" + " pigment { srgbf }\n" + " finish { emission 0 ambient Ambient diffuse Diffuse }\n" + " finish { phong RubberPhong phong_size RubberPhongS reflection RubberReflection }\n" + " }\n" + "#if (LgeoLibrary) } #end\n" + "#end\n" + "#end\n\n"); + POVFile.WriteLine(Line); + ColorMacros.append("RubberColor"); + } + } + else + { + if (!ColorMacros.contains("OpaqueColor")) + { + sprintf(Line, + "#ifndef (SkipOpaqueColorMacro)\n" + "#macro OpaqueColor(r, g, b)\n" + "#if (LgeoLibrary) material { #end\n" + " texture {\n" + " pigment { srgbf }\n" + " finish { emission 0 ambient Ambient diffuse Diffuse }\n" + " finish { phong Phong phong_size PhongSize reflection Reflection }\n" + " normal { OpaqueNormal }\n" + " }\n" + "#if (LgeoLibrary) } #end\n" + "#end\n" + "#end\n\n"); + POVFile.WriteLine(Line); + ColorMacros.append("OpaqueColor"); + } + } + } + + sprintf(Line, "#if (Background)\n background {\n color rgb BackgroundColor\n }\n#end\n\n"); + POVFile.WriteLine(Line); + + sprintf(Line, "#ifndef (Skip%s)\n camera {\n perspective\n right x * image_width / image_height\n sky CameraSky\n location CameraLocation\n look_at CameraTarget\n angle CameraAngle * image_width / image_height\n }\n#end\n\n", + (CameraName.isEmpty() ? "Camera" : CameraName.toLatin1().constData())); + POVFile.WriteLine(Line); + + lcVector3 LightTarget(0.0f, 0.0f, 0.0f), LightColor(1.0f, 1.0f, 1.0f); + lcVector2 AreaSize(200.0f, 200.0f), AreaGrid(10.0f, 10.0f); + int AreaCircle = 0, Shadowless = 0, LightType = LC_AREALIGHT; + float Power = 0, SpotRadius = 0, SpotFalloff = 0, SpotTightness = 0; + if (Lights.IsEmpty()) + { + lcVector3 Location[4]; + Location[0] = {0.0f * Radius + Center[0], -1.5f * Radius + Center[1], -1.5f * Radius + Center[2]}; + Location[1] = {1.5f * Radius + Center[0], -1.0f * Radius + Center[1], 0.866026f * Radius + Center[2]}; + Location[2] = {0.0f * Radius + Center[0], -2.0f * Radius + Center[1], 0.0f * Radius + Center[2]}; + Location[3] = {2.0f * Radius + Center[0], 0.0f * Radius + Center[1], -2.0f * Radius + Center[2]}; + for (int Idx = 0; Idx < 4; Idx++) + { + Power = Idx < 2 ? 0.75f : 0.5f; + sprintf(Line,"#ifndef (SkipLight%i)\nWriteLight(%i, %i, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, %g, %g, %g, %g, %i, %i, %i, %i, %i)\n#end\n\n", + Idx, + LightType, + Shadowless, + Location[Idx][0], Location[Idx][1], Location[Idx][2], + LightTarget[0], LightTarget[1], LightTarget[2], + LightColor[0], LightColor[1], LightColor[2], + Power, + SpotRadius, SpotFalloff, SpotTightness, + AreaCircle, (int)AreaSize[0], (int)AreaSize[1], (int)AreaGrid[0], (int)AreaGrid[1]); + POVFile.WriteLine(Line); + } + } + else + { + for (const lcLight* Light : Lights) + { + const lcVector3& Location = Light->mPosition; + const QString LightName = QString(Light->mName).replace(" ","_"); + LightType = Light->mLightType; + Shadowless = static_cast(Light->mShadowless); + LightColor = Light->mLightColor; + Power = Light->mPOVRayExponent; + switch(LightType) + { + case LC_AREALIGHT: + AreaCircle = Light->mLightShape == LC_LIGHT_SHAPE_DISK ? 1 : 0; + AreaSize = Light->mAreaSize; + AreaGrid = Light->mAreaGrid; + break; + case LC_SUNLIGHT: + LightTarget = Light->mTargetPosition; + break; + case LC_SPOTLIGHT: + LightTarget = Light->mTargetPosition; + SpotFalloff = Light->mSpotFalloff; + SpotRadius = Light->mSpotSize - SpotFalloff; + break; + default: + break; + } + + sprintf(Line,"#ifndef (Skip%s)\n WriteLight(%i, %i, <%g, %g, %g>, <%g, %g, %g>, <%g, %g, %g>, %g, %g, %g, %g, %i, %i, %i, %i, %i)\n#end\n\n", + LightName.toLatin1().constData(), + LightType, + Shadowless, + Location[1], Location[0], Location[2], + LightTarget[1], LightTarget[0], LightTarget[2], + LightColor[0], LightColor[1], LightColor[2], + Power, + SpotRadius, SpotFalloff, SpotTightness, + AreaCircle, (int)AreaSize[0], (int)AreaSize[1], (int)AreaGrid[0], (int)AreaGrid[1]); + POVFile.WriteLine(Line); + } + } + + POVFile.WriteLine("#ifndef (lg_quality) #declare lg_quality = Quality; #end\n\n"); + + if (UseLGEO) + { + memset(Line, 0, 1024); + + POVFile.WriteLine("#ifndef (lg_studs) #declare lg_studs = Studs; #end\n\n"); + + POVFile.WriteLine("#if (lg_quality = 3) #declare lg_quality = 4; #end\n\n"); + + POVFile.WriteLine("#include \"lg_defs.inc\"\n\n#include \"lg_color.inc\"\n\n"); + lcDiskFile TableFile(QFileInfo(QDir(LGEOPath), QLatin1String("lg_elements.lst")).absoluteFilePath()); if (!TableFile.Open(QIODevice::ReadOnly)) @@ -1869,6 +2237,7 @@ bool Project::ExportPOVRay(const QString& FileName) while (TableFile.ReadLine(Line, sizeof(Line))) { + char Src[129], Dst[129], Flags[11]; if (*Line == ';') @@ -1877,20 +2246,22 @@ bool Project::ExportPOVRay(const QString& FileName) if (sscanf(Line,"%128s%128s%10s", Src, Dst, Flags) != 3) continue; - strcat(Src, ".dat"); + strncat(Src, ".dat", 4); PieceInfo* Info = Library->FindPiece(Src, nullptr, false, false); if (!Info) continue; - if (strchr(Flags, 'L')) + bool LgeoPartFound = false; + + if ((LgeoPartFound = strchr(Flags, 'L'))) { std::pair& Entry = PieceTable[Info]; Entry.second |= LGEO_PIECE_LGEO; sprintf(Entry.first, "lg_%s", Dst); } - if (strchr(Flags, 'A')) + if (strchr(Flags, 'A') && !LgeoPartFound) { std::pair& Entry = PieceTable[Info]; Entry.second |= LGEO_PIECE_AR; @@ -1905,15 +2276,15 @@ bool Project::ExportPOVRay(const QString& FileName) } } - lcDiskFile ColorFile(QFileInfo(QDir(LGEOPath), QLatin1String("lg_colors.lst")).absoluteFilePath()); + lcDiskFile LgeoColorFile(QFileInfo(QDir(LGEOPath), QLatin1String("lg_colors.lst")).absoluteFilePath()); - if (!ColorFile.Open(QIODevice::ReadOnly)) + if (!LgeoColorFile.Open(QIODevice::ReadOnly)) { QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Could not find LGEO files in folder '%1'.").arg(LGEOPath)); return false; } - while (ColorFile.ReadLine(Line, sizeof(Line))) + while (LgeoColorFile.ReadLine(Line, sizeof(Line))) { char Name[1024], Flags[1024]; int Code; @@ -1924,20 +2295,82 @@ bool Project::ExportPOVRay(const QString& FileName) if (sscanf(Line,"%d%s%s", &Code, Name, Flags) != 3) continue; - size_t Color = lcGetColorIndex(Code); - if (Color >= NumColors) + size_t ColorIdx = lcGetColorIndex(Code); + if (ColorIdx >= NumColors) continue; - strcpy(ColorTable[Color].data(), Name); + strncpy(LgeoColorTable[ColorIdx].data(), Name, LC_MAX_COLOR_NAME); } } + for (const lcModelPartsEntry& ModelPart : ModelParts) + { + size_t ColorIdx = ModelPart.ColorIndex; + + if (!ColorTable[ColorIdx][0]) + { + lcColor* Color = &gColorList[ColorIdx]; + + if (!LgeoColorTable[ColorIdx][0]) + { + sprintf(ColorTable[ColorIdx].data(), "lc_%s", Color->SafeName); + + if (lcIsColorTranslucent(ColorIdx)) + { + if (!UseLGEO && !MaterialColors.contains(ColorTable[ColorIdx].data())) + MaterialColors.append(ColorTable[ColorIdx].data()); + + sprintf(Line, "#ifndef (lc_%s)\n#declare lc_%s = TranslucentColor(%g, %g, %g, TransFilter)\n#end\n\n", + Color->SafeName, Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]); + } + else + { + char MacroName[LC_MAX_COLOR_NAME]; + if (lcIsColorChrome(ColorIdx)) + sprintf(MacroName, "Chrome"); + else if (lcIsColorRubber(ColorIdx)) + sprintf(MacroName, "Rubber"); + else + sprintf(MacroName, "Opaque"); + + sprintf(Line, "#ifndef (lc_%s)\n#declare lc_%s = %sColor(%g, %g, %g)\n#end\n\n", + Color->SafeName, Color->SafeName, MacroName, Color->Value[0], Color->Value[1], Color->Value[2]); + } + } + else + { + sprintf(ColorTable[ColorIdx].data(), "LDXColor%i", Color->Code); + + sprintf(Line,"#ifndef (LDXColor%i) // %s\n#declare LDXColor%i = material { texture { %s } }\n#end\n\n", + Color->Code, Color->Name, Color->Code, LgeoColorTable[ColorIdx].data()); + } + + POVFile.WriteLine(Line); + } + } + + if (!ColorTable[lcGetColorIndex(TopModelColorCode)][0]) + { + size_t ColorIdx = lcGetColorIndex(TopModelColorCode); + + lcColor* Color = &gColorList[ColorIdx]; + + sprintf(ColorTable[ColorIdx].data(), "LDXColor%i", Color->Code); + + if (!LgeoColorTable[ColorIdx][0]) + sprintf(Line, "#ifndef (lc_%s)\n#declare lc_%s = OpaqueColor(%g, %g, %g)\n#end\n\n", + Color->SafeName, Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]); + else + sprintf(Line,"#ifndef (LDXColor%i) // %s\n#declare LDXColor%i = material { texture { %s } }\n#end\n\n", + Color->Code, Color->Name, Color->Code, LgeoColorTable[ColorIdx].data()); + + POVFile.WriteLine(Line); + } + std::set AddedMeshes; - if (!LGEOPath.isEmpty()) + if (UseLGEO) { - POVFile.WriteLine("#include \"lg_defs.inc\"\n#include \"lg_color.inc\"\n\n"); - for (const lcModelPartsEntry& ModelPart : ModelParts) { if (ModelPart.Mesh) @@ -1958,7 +2391,7 @@ bool Project::ExportPOVRay(const QString& FileName) const std::pair& Entry = Search->second; if (Entry.first[0]) { - sprintf(Line, "#include \"%s.inc\"\n", Entry.first); + sprintf(Line, "#include \"%s.inc\" // %s\n", Entry.first, ModelPart.Info->m_strDescription); POVFile.WriteLine(Line); } } @@ -1966,29 +2399,6 @@ bool Project::ExportPOVRay(const QString& FileName) POVFile.WriteLine("\n"); } - for (size_t ColorIdx = 0; ColorIdx < gColorList.size(); ColorIdx++) - { - lcColor* Color = &gColorList[ColorIdx]; - - if (lcIsColorTranslucent(ColorIdx)) - { - sprintf(Line, "#declare lc_%s = texture { pigment { rgb <%f, %f, %f> filter 0.9 } finish { ambient 0.3 diffuse 0.2 reflection 0.25 phong 0.3 phong_size 60 } }\n", - Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]); - } - else - { - sprintf(Line, "#declare lc_%s = texture { pigment { rgb <%f, %f, %f> } finish { ambient 0.1 phong 0.2 phong_size 20 } }\n", - Color->SafeName, Color->Value[0], Color->Value[1], Color->Value[2]); - } - - POVFile.WriteLine(Line); - - if (!ColorTable[ColorIdx][0]) - sprintf(ColorTable[ColorIdx].data(), "lc_%s", Color->SafeName); - } - - POVFile.WriteLine("\n"); - std::vector ColorTablePointer; ColorTablePointer.resize(NumColors); for (size_t ColorIdx = 0; ColorIdx < NumColors; ColorIdx++) @@ -1996,7 +2406,7 @@ bool Project::ExportPOVRay(const QString& FileName) auto GetMeshName = [](const lcModelPartsEntry& ModelPart, char (&Name)[LC_PIECE_NAME_LEN]) { - strcpy(Name, ModelPart.Info->mFileName); + strncpy(Name, ModelPart.Info->mFileName, sizeof(Name)); for (char* c = Name; *c; c++) if (*c == '-' || *c == '.') @@ -2006,7 +2416,7 @@ bool Project::ExportPOVRay(const QString& FileName) { char Suffix[32]; sprintf(Suffix, "_%p", ModelPart.Mesh); - strncat(Name, Suffix, sizeof(Name) - 1); + strncat(Name, Suffix, sizeof(Name) - strlen(Name) - 1); Name[sizeof(Name) - 1] = 0; } }; @@ -2027,7 +2437,7 @@ bool Project::ExportPOVRay(const QString& FileName) if (!ModelPart.Mesh) { std::pair& Entry = PieceTable[ModelPart.Info]; - strcpy(Entry.first, "lc_"); + strncpy(Entry.first, "lc_", 3); strncat(Entry.first, Name, sizeof(Entry.first) - 1); Entry.first[sizeof(Entry.first) - 1] = 0; } @@ -2038,65 +2448,30 @@ bool Project::ExportPOVRay(const QString& FileName) POVFile.WriteLine(Line); } - const lcCamera* Camera = gMainWindow->GetActiveView()->GetCamera(); - const lcVector3& Position = Camera->mPosition; - const lcVector3& Target = Camera->mTargetPosition; - const lcVector3& Up = Camera->mUpVector; - - sprintf(Line, "camera {\n perspective\n right x * image_width / image_height\n sky<%1g,%1g,%1g>\n location <%1g, %1g, %1g>\n look_at <%1g, %1g, %1g>\n angle %.0f * image_width / image_height\n}\n\n", - Up[1], Up[0], Up[2], Position[1] / 25.0f, Position[0] / 25.0f, Position[2] / 25.0f, Target[1] / 25.0f, Target[0] / 25.0f, Target[2] / 25.0f, Camera->m_fovy); - POVFile.WriteLine(Line); - lcVector3 BackgroundColor = lcVector3FromColor(lcGetPreferences().mBackgroundSolidColor); - sprintf(Line, "background { color rgb <%1g, %1g, %1g> }\n\n", BackgroundColor[0], BackgroundColor[1], BackgroundColor[2]); - POVFile.WriteLine(Line); - - lcVector3 Min(FLT_MAX, FLT_MAX, FLT_MAX); - lcVector3 Max(-FLT_MAX, -FLT_MAX, -FLT_MAX); - - for (const lcModelPartsEntry& ModelPart : ModelParts) - { - lcVector3 Points[8]; - - lcGetBoxCorners(ModelPart.Info->GetBoundingBox(), Points); - - for (int PointIdx = 0; PointIdx < 8; PointIdx++) - { - lcVector3 Point = lcMul31(Points[PointIdx], ModelPart.WorldMatrix); - - Min = lcMin(Point, Min); - Max = lcMax(Point, Max); - } - } - - lcVector3 Center = (Min + Max) / 2.0f; - float Radius = (Max - Center).Length() / 25.0f; - Center = lcVector3(Center[1], Center[0], Center[2]) / 25.0f; - - sprintf(Line, "light_source{ <%f, %f, %f>\n color rgb 0.75\n area_light 200, 200, 10, 10\n jitter\n}\n\n", 0.0f * Radius + Center.x, -1.5f * Radius + Center.y, -1.5f * Radius + Center.z); - POVFile.WriteLine(Line); - sprintf(Line, "light_source{ <%f, %f, %f>\n color rgb 0.75\n area_light 200, 200, 10, 10\n jitter\n}\n\n", 1.5f * Radius + Center.x, -1.0f * Radius + Center.y, 0.866026f * Radius + Center.z); - POVFile.WriteLine(Line); - sprintf(Line, "light_source{ <%f, %f, %f>\n color rgb 0.5\n area_light 200, 200, 10, 10\n jitter\n}\n\n", 0.0f * Radius + Center.x, -2.0f * Radius + Center.y, 0.0f * Radius + Center.z); - POVFile.WriteLine(Line); - sprintf(Line, "light_source{ <%f, %f, %f>\n color rgb 0.5\n area_light 200, 200, 10, 10\n jitter\n}\n\n", 2.0f * Radius + Center.x, 0.0f * Radius + Center.y, -2.0f * Radius + Center.z); + sprintf(Line, "#declare %s = union {\n", TopModelName.toLatin1().constData()); POVFile.WriteLine(Line); for (const lcModelPartsEntry& ModelPart : ModelParts) { - int Color = ModelPart.ColorIndex; - const char* Suffix = lcIsColorTranslucent(Color) ? "_clear" : ""; + int ColorIdx = ModelPart.ColorIndex; + const char* Suffix = lcIsColorTranslucent(ColorIdx) ? "_clear" : ""; const float* f = ModelPart.WorldMatrix; - + char Modifier[32]; + if (UseLGEO || MaterialColors.contains(ColorTable[ColorIdx].data())) + sprintf(Modifier, "material"); + else + sprintf(Modifier, "texture"); if (!ModelPart.Mesh) { std::pair& Entry = PieceTable[ModelPart.Info]; if (Entry.second & LGEO_PIECE_SLOPE) { - sprintf(Line, "merge {\n object {\n %s%s\n texture { %s }\n }\n" - " object {\n %s_slope\n texture { %s normal { bumps 0.3 scale 0.02 } }\n }\n" - " matrix <%.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f>\n}\n", - Entry.first, Suffix, ColorTable[Color].data(), Entry.first, ColorTable[Color].data(), + sprintf(Line, + " merge {\n object {\n %s%s\n %s { %s }\n }\n" + " object {\n %s_slope\n texture {\n %s normal { bumps 0.3 scale 0.02 }\n }\n }\n" + " matrix <%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g>\n }\n", + Entry.first, Suffix, Modifier, ColorTable[ColorIdx].data(), Entry.first, ColorTable[ColorIdx].data(), -f[5], -f[4], -f[6], -f[1], -f[0], -f[2], f[9], f[8], f[10], f[13] / 25.0f, f[12] / 25.0f, f[14] / 25.0f); } else @@ -2104,8 +2479,8 @@ bool Project::ExportPOVRay(const QString& FileName) if (!ModelPart.Info || !ModelPart.Info->GetMesh()) continue; - sprintf(Line, "object {\n %s%s\n texture { %s }\n matrix <%.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f>\n}\n", - Entry.first, Suffix, ColorTable[Color].data(), -f[5], -f[4], -f[6], -f[1], -f[0], -f[2], f[9], f[8], f[10], f[13] / 25.0f, f[12] / 25.0f, f[14] / 25.0f); + sprintf(Line, " object {\n %s%s\n %s { %s }\n matrix <%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g>\n }\n", + Entry.first, Suffix, Modifier, ColorTable[ColorIdx].data(), -f[5], -f[4], -f[6], -f[1], -f[0], -f[2], f[9], f[8], f[10], f[13] / 25.0f, f[12] / 25.0f, f[14] / 25.0f); } } @@ -2114,13 +2489,29 @@ bool Project::ExportPOVRay(const QString& FileName) char Name[LC_PIECE_NAME_LEN]; GetMeshName(ModelPart, Name); - sprintf(Line, "object {\n lc_%s%s\n texture { %s }\n matrix <%.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f, %.4f>\n}\n", - Name, Suffix, ColorTable[Color].data(), -f[5], -f[4], -f[6], -f[1], -f[0], -f[2], f[9], f[8], f[10], f[13] / 25.0f, f[12] / 25.0f, f[14] / 25.0f); + sprintf(Line, " object {\n lc_%s%s\n %s { %s }\n matrix <%g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g>\n }\n", + Name, Suffix, Modifier, ColorTable[ColorIdx].data(), -f[5], -f[4], -f[6], -f[1], -f[0], -f[2], f[9], f[8], f[10], f[13] / 25.0f, f[12] / 25.0f, f[14] / 25.0f); } POVFile.WriteLine(Line); } + sprintf(Line, "\n #if (ModelReflection = 0)\n no_reflection\n #end\n #if (ModelShadow = 0)\n no_shadow\n #end\n}\n\n"); + POVFile.WriteLine(Line); + + sprintf(Line, "object {\n %s\n %s { %s }\n}\n\n", + TopModelName.toLatin1().constData(), (UseLGEO ? "material" : "texture"), ColorTable[lcGetColorIndex(TopModelColorCode)].data()); + POVFile.WriteLine(Line); + + sprintf(Line, "#if (Floor)\n object {\n plane { FloorAxis, FloorLocation hollow }\n texture {\n pigment { color srgb FloorColor }\n finish { emission 0 ambient FloorAmbient diffuse FloorDiffuse }\n }\n }\n#end\n"); + POVFile.WriteLine(Line); + + if (!POVRayOptions.FooterIncludeFile.isEmpty()) + { + sprintf(Line, "\n#include \"%s\"\n", POVRayOptions.FooterIncludeFile.toLatin1().constData()); + POVFile.WriteLine(Line); + } + return true; } @@ -2239,7 +2630,7 @@ void Project::SaveImage() if (Dialog.exec() != QDialog::Accepted) return; - + QString Extension = QFileInfo(Dialog.mFileName).suffix(); if (!Extension.isEmpty())