From 4cd62ff157a5b490a4e4346addfd782edccd90d5 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 7 Apr 2024 22:25:57 -0600 Subject: [PATCH] qbsp: remove dead code from last commit --- qbsp/map.cc | 1107 --------------------------------------------------- 1 file changed, 1107 deletions(-) diff --git a/qbsp/map.cc b/qbsp/map.cc index cffd8e2e..fd0a2390 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -812,830 +812,6 @@ static surfflags_t SurfFlagsForEntity( return flags; } -#if 0 -static void ParseEpair(parser_t &parser, mapentity_t &entity) -{ - std::string key = parser.token; - - // trim whitespace from start/end - while (std::isspace(key.front())) { - key.erase(key.begin()); - } - while (std::isspace(key.back())) { - key.erase(key.end() - 1); - } - - parser.parse_token(PARSE_SAMELINE); - - entity.epairs.set(key, parser.token); - - if (string_iequals(key, "origin")) { - entity.epairs.get_vector(key, entity.origin); - } -} - -static void TextureAxisFromPlane(const qplane3d &plane, qvec3d &xv, qvec3d &yv, qvec3d &snapped_normal) -{ - constexpr qvec3d baseaxis[18] = { - {0, 0, 1}, {1, 0, 0}, {0, -1, 0}, // floor - {0, 0, -1}, {1, 0, 0}, {0, -1, 0}, // ceiling - {1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // west wall - {-1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // east wall - {0, 1, 0}, {1, 0, 0}, {0, 0, -1}, // south wall - {0, -1, 0}, {1, 0, 0}, {0, 0, -1} // north wall - }; - - int bestaxis; - double dot, best; - int i; - - best = 0; - bestaxis = 0; - - for (i = 0; i < 6; i++) { - dot = qv::dot(plane.normal, baseaxis[i * 3]); - if (dot > best || (dot == best && !qbsp_options.oldaxis.value())) { - best = dot; - bestaxis = i; - } - } - - xv = baseaxis[bestaxis * 3 + 1]; - yv = baseaxis[bestaxis * 3 + 2]; - snapped_normal = baseaxis[bestaxis * 3]; -} - -static quark_tx_info_t ParseExtendedTX(parser_t &parser) -{ - quark_tx_info_t result; - - if (parser.parse_token(PARSE_COMMENT | PARSE_OPTIONAL)) { - if (!strncmp(parser.token.c_str(), "//TX", 4)) { - if (parser.token[4] == '1') - result.quark_tx1 = true; - else if (parser.token[4] == '2') - result.quark_tx2 = true; - } - } else { - // Parse extra Quake 2 surface info - if (parser.parse_token(PARSE_OPTIONAL)) { - uint32_t native = std::stoul(parser.token); - result.info = extended_texinfo_t{.contents_native = native}; - - if (parser.parse_token(PARSE_OPTIONAL)) { - result.info->flags.native = std::stoi(parser.token); - } - if (parser.parse_token(PARSE_OPTIONAL)) { - result.info->value = std::stoi(parser.token); - } - } - } - - return result; -} - -static qmat4x4f texVecsTo4x4Matrix(const qplane3d &faceplane, const texvecf &in_vecs) -{ - // [s] - // T * vec = [t] - // [distOffPlane] - // [?] - - qmat4x4f T{ - in_vecs.at(0, 0), in_vecs.at(1, 0), static_cast(faceplane.normal[0]), 0, // col 0 - in_vecs.at(0, 1), in_vecs.at(1, 1), static_cast(faceplane.normal[1]), 0, // col 1 - in_vecs.at(0, 2), in_vecs.at(1, 2), static_cast(faceplane.normal[2]), 0, // col 2 - in_vecs.at(0, 3), in_vecs.at(1, 3), static_cast(-faceplane.dist), 1 // col 3 - }; - return T; -} - -static qmat2x2f scale2x2(float xscale, float yscale) -{ - qmat2x2f M{xscale, 0, // col 0 - 0, yscale}; // col1 - return M; -} - -static qmat2x2f rotation2x2_deg(float degrees) -{ - float r = degrees * (Q_PI / 180.0); - float cosr = cos(r); - float sinr = sin(r); - - // [ cosTh -sinTh ] - // [ sinTh cosTh ] - - qmat2x2f M{cosr, sinr, // col 0 - -sinr, cosr}; // col1 - - return M; -} - -static float extractRotation(qmat2x2f m) -{ - qvec2f point = m * qvec2f(1, 0); // choice of this matters if there's shearing - float rotation = atan2(point[1], point[0]) * 180.0 / Q_PI; - return rotation; -} - -static qvec2f evalTexDefAtPoint(const old_texdef_quake_ed_t &texdef, const qbsp_plane_t &faceplane, const qvec3f &point) -{ - texvecf temp; - SetTexinfo_QuakeEd_New(faceplane, texdef.shift, texdef.rotate, texdef.scale, temp); - - const qmat4x4f worldToTexSpace_res = texVecsTo4x4Matrix(faceplane, temp); - const qvec2f uv = qvec2f(worldToTexSpace_res * qvec4f(point[0], point[1], point[2], 1.0f)); - return uv; -} - -static old_texdef_quake_ed_t addShift(const old_texdef_quake_ed_noshift_t &texdef, const qvec2f shift) -{ - old_texdef_quake_ed_t res2; - res2.rotate = texdef.rotate; - res2.scale[0] = texdef.scale[0]; - res2.scale[1] = texdef.scale[1]; - - res2.shift[0] = shift[0]; - res2.shift[1] = shift[1]; - return res2; -} - -void checkEq(const qvec2f &a, const qvec2f &b, float epsilon) -{ - for (int i = 0; i < 2; i++) { - if (fabs(a[i] - b[i]) > epsilon) { - printf("warning, checkEq failed\n"); - } - } -} - -qvec2f normalizeShift(const std::optional &texture, const qvec2f &in) -{ - if (!texture) { - return in; // can't do anything without knowing the texture size. - } - - int fullWidthOffsets = static_cast(in[0]) / texture->width; - int fullHeightOffsets = static_cast(in[1]) / texture->height; - - qvec2f result(in[0] - static_cast(fullWidthOffsets * texture->width), - in[1] - static_cast(fullHeightOffsets * texture->height)); - return result; -} - -/// `texture` is optional. If given, the "shift" values can be normalized -static old_texdef_quake_ed_t TexDef_BSPToQuakeEd(const qbsp_plane_t &faceplane, - const std::optional &texture, const texvecf &in_vecs, const std::array &facepoints) -{ - // First get the un-rotated, un-scaled unit texture vecs (based on the face plane). - qvec3d snapped_normal; - qvec3d unrotated_vecs[2]; - TextureAxisFromPlane(faceplane, unrotated_vecs[0], unrotated_vecs[1], snapped_normal); - - const qmat4x4f worldToTexSpace = texVecsTo4x4Matrix(faceplane, in_vecs); - - // Grab the UVs of the 3 reference points - qvec2f facepoints_uvs[3]; - for (int i = 0; i < 3; i++) { - facepoints_uvs[i] = qvec2f(worldToTexSpace * qvec4f(facepoints[i][0], facepoints[i][1], facepoints[i][2], 1.0)); - } - - // Project the 3 reference points onto the axis plane. They are now 2d points. - qvec2f facepoints_projected[3]; - for (int i = 0; i < 3; i++) { - facepoints_projected[i] = projectToAxisPlane(snapped_normal, facepoints[i]); - } - - // Now make 2 vectors out of our 3 points (so we are ignoring translation for now) - const qvec2f p0p1 = facepoints_projected[1] - facepoints_projected[0]; - const qvec2f p0p2 = facepoints_projected[2] - facepoints_projected[0]; - - const qvec2f p0p1_uv = facepoints_uvs[1] - facepoints_uvs[0]; - const qvec2f p0p2_uv = facepoints_uvs[2] - facepoints_uvs[0]; - - /* - Find a 2x2 transformation matrix that maps p0p1 to p0p1_uv, and p0p2 to p0p2_uv - - [ a b ] [ p0p1.x ] = [ p0p1_uv.x ] - [ c d ] [ p0p1.y ] [ p0p1_uv.y ] - - [ a b ] [ p0p2.x ] = [ p0p1_uv.x ] - [ c d ] [ p0p2.y ] [ p0p2_uv.y ] - - writing as a system of equations: - - a * p0p1.x + b * p0p1.y = p0p1_uv.x - c * p0p1.x + d * p0p1.y = p0p1_uv.y - a * p0p2.x + b * p0p2.y = p0p2_uv.x - c * p0p2.x + d * p0p2.y = p0p2_uv.y - - back to a matrix equation, with the unknowns in a column vector: - - [ p0p1_uv.x ] [ p0p1.x p0p1.y 0 0 ] [ a ] - [ p0p1_uv.y ] = [ 0 0 p0p1.x p0p1.y ] [ b ] - [ p0p2_uv.x ] [ p0p2.x p0p2.y 0 0 ] [ c ] - [ p0p2_uv.y ] [ 0 0 p0p2.x p0p2.y ] [ d ] - - */ - - const qmat4x4f M{ - p0p1[0], 0, p0p2[0], 0, // col 0 - p0p1[1], 0, p0p2[1], 0, // col 1 - 0, p0p1[0], 0, p0p2[0], // col 2 - 0, p0p1[1], 0, p0p2[1] // col 3 - }; - - const qmat4x4f Minv = qv::inverse(M); - const qvec4f abcd = Minv * qvec4f(p0p1_uv[0], p0p1_uv[1], p0p2_uv[0], p0p2_uv[1]); - - const qmat2x2f texPlaneToUV{abcd[0], abcd[2], // col 0 - abcd[1], abcd[3]}; // col 1 - - { - // self check - // qvec2f uv01_test = texPlaneToUV * p0p1; - // qvec2f uv02_test = texPlaneToUV * p0p2; - - // these fail if one of the texture axes is 0 length. - // checkEq(uv01_test, p0p1_uv, 0.01); - // checkEq(uv02_test, p0p2_uv, 0.01); - } - - const old_texdef_quake_ed_noshift_t res = Reverse_QuakeEd(texPlaneToUV, faceplane, false); - - // figure out shift based on facepoints[0] - const qvec3f testpoint = facepoints[0]; - qvec2f uv0_actual = evalTexDefAtPoint(addShift(res, qvec2f(0, 0)), faceplane, testpoint); - qvec2f uv0_desired = qvec2f(worldToTexSpace * qvec4f(testpoint[0], testpoint[1], testpoint[2], 1.0f)); - qvec2f shift = uv0_desired - uv0_actual; - - // sometime we have very large shift values, normalize them to be smaller - shift = normalizeShift(texture, shift); - - const old_texdef_quake_ed_t res2 = addShift(res, shift); - return res2; -} - -float NormalizeDegrees(float degs) -{ - while (degs < 0) - degs += 360; - - while (degs > 360) - degs -= 360; - - if (fabs(degs - 360.0) < 0.001) - degs = 0; - - return degs; -} - -bool EqualDegrees(float a, float b) -{ - return fabs(NormalizeDegrees(a) - NormalizeDegrees(b)) < 0.001; -} - -static std::pair getSTAxes(const qvec3d &snapped_normal) -{ - if (snapped_normal[0]) { - return std::make_pair(1, 2); - } else if (snapped_normal[1]) { - return std::make_pair(0, 2); - } else { - return std::make_pair(0, 1); - } -} - -static qvec2f projectToAxisPlane(const qvec3d &snapped_normal, const qvec3d &point) -{ - const std::pair axes = getSTAxes(snapped_normal); - const qvec2f proj(point[axes.first], point[axes.second]); - return proj; -} - -float clockwiseDegreesBetween(qvec2f start, qvec2f end) -{ - start = qv::normalize(start); - end = qv::normalize(end); - - const float cosAngle = std::max(-1.0f, std::min(1.0f, qv::dot(start, end))); - const float unsigned_degrees = acos(cosAngle) * (360.0 / (2.0 * Q_PI)); - - if (unsigned_degrees < ANGLEEPSILON) - return 0; - - // get a normal for the rotation plane using the right-hand rule - // if this is pointing up (qvec3f(0,0,1)), it's counterclockwise rotation. - // if this is pointing down (qvec3f(0,0,-1)), it's clockwise rotation. - qvec3f rotationNormal = qv::normalize(qv::cross(qvec3f(start[0], start[1], 0.0f), qvec3f(end[0], end[1], 0.0f))); - - const float normalsCosAngle = qv::dot(rotationNormal, qvec3f(0, 0, 1)); - if (normalsCosAngle >= 0) { - // counterclockwise rotation - return -unsigned_degrees; - } - // clockwise rotation - return unsigned_degrees; -} - -static old_texdef_quake_ed_noshift_t Reverse_QuakeEd(qmat2x2f M, const qbsp_plane_t &plane, bool preserveX) -{ - // Check for shear, because we might tweak M to remove it - { - qvec2f Xvec = M.row(0); - qvec2f Yvec = M.row(1); - double cosAngle = qv::dot(qv::normalize(Xvec), qv::normalize(Yvec)); - - // const double oldXscale = sqrt(pow(M[0][0], 2.0) + pow(M[1][0], 2.0)); - // const double oldYscale = sqrt(pow(M[0][1], 2.0) + pow(M[1][1], 2.0)); - - if (fabs(cosAngle) > 0.001) { - // Detected shear - - if (preserveX) { - const float degreesToY = clockwiseDegreesBetween(Xvec, Yvec); - const bool CW = (degreesToY > 0); - - // turn 90 degrees from Xvec - const qvec2f newYdir = - qv::normalize(qvec2f(qv::cross(qvec3f(0, 0, CW ? -1.0f : 1.0f), qvec3f(Xvec[0], Xvec[1], 0.0)))); - - // scalar projection of the old Yvec onto newYDir to get the new Yscale - const float newYscale = qv::dot(Yvec, newYdir); - Yvec = newYdir * static_cast(newYscale); - } else { - // Preserve Y. - - const float degreesToX = clockwiseDegreesBetween(Yvec, Xvec); - const bool CW = (degreesToX > 0); - - // turn 90 degrees from Yvec - const qvec2f newXdir = - qv::normalize(qvec2f(qv::cross(qvec3f(0, 0, CW ? -1.0f : 1.0f), qvec3f(Yvec[0], Yvec[1], 0.0)))); - - // scalar projection of the old Xvec onto newXDir to get the new Xscale - const float newXscale = qv::dot(Xvec, newXdir); - Xvec = newXdir * static_cast(newXscale); - } - - // recheck - cosAngle = qv::dot(qv::normalize(Xvec), qv::normalize(Yvec)); - if (fabs(cosAngle) > 0.001) { - FError("SHEAR correction failed\n"); - } - - // update M - M.at(0, 0) = Xvec[0]; - M.at(0, 1) = Xvec[1]; - - M.at(1, 0) = Yvec[0]; - M.at(1, 1) = Yvec[1]; - } - } - - // extract abs(scale) - const double absXscale = sqrt(pow(M.at(0, 0), 2.0) + pow(M.at(0, 1), 2.0)); - const double absYscale = sqrt(pow(M.at(1, 0), 2.0) + pow(M.at(1, 1), 2.0)); - const qmat2x2f applyAbsScaleM{static_cast(absXscale), // col0 - 0, - 0, // col1 - static_cast(absYscale)}; - - qvec3d vecs[2]; - qvec3d snapped_normal; - TextureAxisFromPlane(plane, vecs[0], vecs[1], snapped_normal); - - const qvec2f sAxis = projectToAxisPlane(snapped_normal, vecs[0]); - const qvec2f tAxis = projectToAxisPlane(snapped_normal, vecs[1]); - - // This is an identity matrix possibly with negative signs. - const qmat2x2f axisFlipsM{sAxis[0], tAxis[0], // col0 - sAxis[1], tAxis[1]}; // col1 - - // N.B. this is how M is built in SetTexinfo_QuakeEd_New and guides how we - // strip off components of it later in this function. - // - // qmat2x2f M = scaleM * rotateM * axisFlipsM; - - // strip off the magnitude component of the scale, and `axisFlipsM`. - const qmat2x2f flipRotate = qv::inverse(applyAbsScaleM) * M * qv::inverse(axisFlipsM); - - // We don't know the signs on the scales, which will mess up figuring out the rotation, so try all 4 combinations - for (float xScaleSgn : std::vector{-1.0, 1.0}) { - for (float yScaleSgn : std::vector{-1.0, 1.0}) { - - // "apply" - matrix constructed to apply a guessed value - // "guess" - this matrix might not be what we think - - const qmat2x2f applyGuessedFlipM{xScaleSgn, // col0 - 0, - 0, // col1 - yScaleSgn}; - - const qmat2x2f rotateMGuess = qv::inverse(applyGuessedFlipM) * flipRotate; - const float angleGuess = extractRotation(rotateMGuess); - - // const qmat2x2f Mident = rotateMGuess * rotation2x2_deg(-angleGuess); - - const qmat2x2f applyAngleGuessM = rotation2x2_deg(angleGuess); - const qmat2x2f Mguess = applyGuessedFlipM * applyAbsScaleM * applyAngleGuessM * axisFlipsM; - - if (fabs(M.at(0, 0) - Mguess.at(0, 0)) < 0.001 && fabs(M.at(1, 0) - Mguess.at(1, 0)) < 0.001 && - fabs(M.at(0, 1) - Mguess.at(0, 1)) < 0.001 && fabs(M.at(1, 1) - Mguess.at(1, 1)) < 0.001) { - - old_texdef_quake_ed_noshift_t reversed; - reversed.rotate = angleGuess; - reversed.scale[0] = xScaleSgn / absXscale; - reversed.scale[1] = yScaleSgn / absYscale; - return reversed; - } - } - } - - // TODO: detect when we expect this to fail, i.e. invalid texture axes (0-length), - // and throw an error if it fails unexpectedly. - - // printf("Warning, Reverse_QuakeEd failed\n"); - - old_texdef_quake_ed_noshift_t fail; - return fail; -} - -static void SetTexinfo_QuakeEd_New( - const qbsp_plane_t &plane, const qvec2d &shift, double rotate, const qvec2d &scale, texvecf &out_vecs) -{ - double sanitized_scale[2]; - for (int i = 0; i < 2; i++) { - sanitized_scale[i] = (scale[i] != 0.0) ? scale[i] : 1.0; - } - - qvec3d vecs[2]; - qvec3d snapped_normal; - TextureAxisFromPlane(plane, vecs[0], vecs[1], snapped_normal); - - qvec2f sAxis = projectToAxisPlane(snapped_normal, vecs[0]); - qvec2f tAxis = projectToAxisPlane(snapped_normal, vecs[1]); - - // This is an identity matrix possibly with negative signs. - qmat2x2f axisFlipsM{sAxis[0], tAxis[0], // col0 - sAxis[1], tAxis[1]}; // col1 - - qmat2x2f rotateM = rotation2x2_deg(rotate); - qmat2x2f scaleM = scale2x2(1.0 / sanitized_scale[0], 1.0 / sanitized_scale[1]); - - qmat2x2f M = scaleM * rotateM * axisFlipsM; - - if (false) { - // Self-test for Reverse_QuakeEd - old_texdef_quake_ed_noshift_t reversed = Reverse_QuakeEd(M, plane, false); - - // normalize - if (!EqualDegrees(reversed.rotate, rotate)) { - reversed.rotate += 180; - reversed.scale[0] *= -1; - reversed.scale[1] *= -1; - } - - if (!EqualDegrees(reversed.rotate, rotate)) { - FError("wrong rotate got {} expected {}\n", reversed.rotate, rotate); - } - - if (fabs(reversed.scale[0] - sanitized_scale[0]) > 0.001 || - fabs(reversed.scale[1] - sanitized_scale[1]) > 0.001) { - FError("wrong scale, got {} {} exp {} {}\n", reversed.scale[0], reversed.scale[1], sanitized_scale[0], - sanitized_scale[1]); - } - } - - // copy M into the output vectors - out_vecs = {}; - - const std::pair axes = getSTAxes(snapped_normal); - - // M[col][row] - // S - out_vecs.at(0, axes.first) = M.at(0, 0); - out_vecs.at(0, axes.second) = M.at(0, 1); - out_vecs.at(0, 3) = shift[0]; - - // T - out_vecs.at(1, axes.first) = M.at(1, 0); - out_vecs.at(1, axes.second) = M.at(1, 1); - out_vecs.at(1, 3) = shift[1]; -} - -static void SetTexinfo_QuakeEd(const qbsp_plane_t &plane, const std::array &planepts, const qvec2d &shift, - const double &rotate, const qvec2d &scale, maptexinfo_t *out) -{ - int i, j; - qvec3d vecs[2]; - int sv, tv; - double ang, sinv, cosv; - double ns, nt; - qvec3d unused; - - TextureAxisFromPlane(plane, vecs[0], vecs[1], unused); - - /* Rotate axis */ - ang = rotate / 180.0 * Q_PI; - sinv = sin(ang); - cosv = cos(ang); - - if (vecs[0][0]) - sv = 0; - else if (vecs[0][1]) - sv = 1; - else - sv = 2; // unreachable, due to TextureAxisFromPlane lookup table - - if (vecs[1][0]) - tv = 0; // unreachable, due to TextureAxisFromPlane lookup table - else if (vecs[1][1]) - tv = 1; - else - tv = 2; - - for (i = 0; i < 2; i++) { - ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; - nt = sinv * vecs[i][sv] + cosv * vecs[i][tv]; - vecs[i][sv] = ns; - vecs[i][tv] = nt; - } - - for (i = 0; i < 2; i++) - for (j = 0; j < 3; j++) - /* Interpret zero scale as no scaling */ - out->vecs.at(i, j) = vecs[i][j] / (scale[i] ? scale[i] : 1); - - out->vecs.at(0, 3) = shift[0]; - out->vecs.at(1, 3) = shift[1]; - - if (false) { - // Self-test of SetTexinfo_QuakeEd_New - texvecf check; - SetTexinfo_QuakeEd_New(plane, shift, rotate, scale, check); - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 4; j++) { - if (fabs(check.at(i, j) - out->vecs.at(i, j)) > 0.001) { - SetTexinfo_QuakeEd_New(plane, shift, rotate, scale, check); - FError("fail"); - } - } - } - } - - if (false) { - // Self-test of TexDef_BSPToQuakeEd - old_texdef_quake_ed_t reversed = TexDef_BSPToQuakeEd(plane, std::nullopt, out->vecs, planepts); - - if (!EqualDegrees(reversed.rotate, rotate)) { - reversed.rotate += 180; - reversed.scale[0] *= -1; - reversed.scale[1] *= -1; - } - - if (!EqualDegrees(reversed.rotate, rotate)) { - fmt::print("wrong rotate got {} expected {}\n", reversed.rotate, rotate); - } - - if (fabs(reversed.scale[0] - scale[0]) > 0.001 || fabs(reversed.scale[1] - scale[1]) > 0.001) { - fmt::print("wrong scale, got {} {} exp {} {}\n", reversed.scale[0], reversed.scale[1], scale[0], scale[1]); - } - - if (fabs(reversed.shift[0] - shift[0]) > 0.1 || fabs(reversed.shift[1] - shift[1]) > 0.1) { - fmt::print("wrong shift, got {} {} exp {} {}\n", reversed.shift[0], reversed.shift[1], shift[0], shift[1]); - } - } -} - -static void SetTexinfo_QuArK( - parser_t &parser, const std::array &planepts, old_texcoord_style_t style, maptexinfo_t *out) -{ - int i; - qvec3d vecs[2]; - double a, b, c, d; - double determinant; - - /* - * Type 1 uses vecs[0] = (pt[2] - pt[0]) and vecs[1] = (pt[1] - pt[0]) - * Type 2 reverses the order of the vecs - * 128 is the scaling factor assumed by QuArK. - */ - switch (style) { - case TX_QUARK_TYPE1: - vecs[0] = planepts[2] - planepts[0]; - vecs[1] = planepts[1] - planepts[0]; - break; - case TX_QUARK_TYPE2: - vecs[0] = planepts[1] - planepts[0]; - vecs[1] = planepts[2] - planepts[0]; - break; - default: FError("{}: bad texture coordinate style", parser.location); - } - - vecs[0] *= 1.0 / 128.0; - vecs[1] *= 1.0 / 128.0; - - a = qv::dot(vecs[0], vecs[0]); - b = qv::dot(vecs[0], vecs[1]); - c = b; /* qv::dot(vecs[1], vecs[0]); */ - d = qv::dot(vecs[1], vecs[1]); - - /* - * Want to solve for out->vecs: - * - * | a b | | out->vecs[0] | = | vecs[0] | - * | c d | | out->vecs[1] | | vecs[1] | - * - * => | out->vecs[0] | = __ 1.0__ | d -b | | vecs[0] | - * | out->vecs[1] | a*d - b*c | -c a | | vecs[1] | - */ - determinant = a * d - b * c; - if (fabs(determinant) < ZERO_EPSILON) { - logging::print("WARNING: {}: Face with degenerate QuArK-style texture axes\n", parser.location); - for (i = 0; i < 3; i++) - out->vecs.at(0, i) = out->vecs.at(1, i) = 0; - } else { - for (i = 0; i < 3; i++) { - out->vecs.at(0, i) = (d * vecs[0][i] - b * vecs[1][i]) / determinant; - out->vecs.at(1, i) = -(a * vecs[1][i] - c * vecs[0][i]) / determinant; - } - } - - /* Finally, the texture offset is indicated by planepts[0] */ - for (i = 0; i < 3; ++i) { - vecs[0][i] = out->vecs.at(0, i); - vecs[1][i] = out->vecs.at(1, i); - } - out->vecs.at(0, 3) = -qv::dot(vecs[0], planepts[0]); - out->vecs.at(1, 3) = -qv::dot(vecs[1], planepts[0]); -} - -static void SetTexinfo_Valve220(qmat &axis, const qvec2d &shift, const qvec2d &scale, maptexinfo_t *out) -{ - int i; - - for (i = 0; i < 3; i++) { - out->vecs.at(0, i) = axis.at(0, i) / scale[0]; - out->vecs.at(1, i) = axis.at(1, i) / scale[1]; - } - out->vecs.at(0, 3) = shift[0]; - out->vecs.at(1, 3) = shift[1]; -} - -/* - ComputeAxisBase() - from q3map2 - - computes the base texture axis for brush primitive texturing - note: ComputeAxisBase here and in editor code must always BE THE SAME! - warning: special case behaviour of atan2( y, x ) <-> atan( y / x ) might not be the same everywhere when x == 0 - rotation by (0,RotY,RotZ) assigns X to normal - */ -static void ComputeAxisBase(const qvec3d &normal_unsanitized, qvec3d &texX, qvec3d &texY) -{ - double RotY, RotZ; - - qvec3d normal = normal_unsanitized; - - /* do some cleaning */ - if (fabs(normal[0]) < 1e-6) { - normal[0] = 0.0f; - } - if (fabs(normal[1]) < 1e-6) { - normal[1] = 0.0f; - } - if (fabs(normal[2]) < 1e-6) { - normal[2] = 0.0f; - } - - /* compute the two rotations around y and z to rotate x to normal */ - RotY = -atan2(normal[2], sqrt(normal[1] * normal[1] + normal[0] * normal[0])); - RotZ = atan2(normal[1], normal[0]); - - /* rotate (0,1,0) and (0,0,1) to compute texX and texY */ - texX[0] = -sin(RotZ); - texX[1] = cos(RotZ); - texX[2] = 0; - - /* the texY vector is along -z (t texture coorinates axis) */ - texY[0] = -sin(RotY) * cos(RotZ); - texY[1] = -sin(RotY) * sin(RotZ); - texY[2] = -cos(RotY); -} - -// From FaceToBrushPrimitFace in GtkRadiant -static texdef_brush_primitives_t TexDef_BSPToBrushPrimitives( - const qplane3d &plane, const int texSize[2], const texvecf &in_vecs) -{ - qvec3d texX, texY; - ComputeAxisBase(plane.normal, texX, texY); - - // compute projection vector - qvec3d proj = plane.normal * plane.dist; - - // (0,0) in plane axis base is (0,0,0) in world coordinates + projection on the affine plane - // (1,0) in plane axis base is texX in world coordinates + projection on the affine plane - // (0,1) in plane axis base is texY in world coordinates + projection on the affine plane - // use old texture code to compute the ST coords of these points - qvec2d st[] = {in_vecs.uvs(proj, texSize[0], texSize[1]), in_vecs.uvs(texX + proj, texSize[0], texSize[1]), - in_vecs.uvs(texY + proj, texSize[0], texSize[1])}; - // compute texture matrix - texdef_brush_primitives_t res; - res.set_col(2, st[0]); - res.set_col(0, st[1] - st[0]); - res.set_col(1, st[2] - st[0]); - return res; -} - -static void ParsePlaneDef(parser_t &parser, std::array &planepts) -{ - int i, j; - - for (i = 0; i < 3; i++) { - if (i != 0) - parser.parse_token(); - if (parser.token != "(") - goto parse_error; - - for (j = 0; j < 3; j++) { - parser.parse_token(PARSE_SAMELINE); - planepts[i][j] = std::stod(parser.token); - } - - parser.parse_token(PARSE_SAMELINE); - if (parser.token != ")") - goto parse_error; - } - return; - -parse_error: - FError("{}: Invalid brush plane format", parser.location); -} - -static void ParseValve220TX(parser_t &parser, qmat &axis, qvec2d &shift, double &rotate, qvec2d &scale) -{ - int i, j; - - for (i = 0; i < 2; i++) { - parser.parse_token(PARSE_SAMELINE); - if (parser.token != "[") - goto parse_error; - for (j = 0; j < 3; j++) { - parser.parse_token(PARSE_SAMELINE); - axis.at(i, j) = std::stod(parser.token); - } - parser.parse_token(PARSE_SAMELINE); - shift[i] = std::stod(parser.token); - parser.parse_token(PARSE_SAMELINE); - if (parser.token != "]") - goto parse_error; - } - parser.parse_token(PARSE_SAMELINE); - rotate = std::stod(parser.token); - parser.parse_token(PARSE_SAMELINE); - scale[0] = std::stod(parser.token); - parser.parse_token(PARSE_SAMELINE); - scale[1] = std::stod(parser.token); - return; - -parse_error: - FError("{}: couldn't parse Valve220 texture info", parser.location); -} - -static void ParseBrushPrimTX(parser_t &parser, qmat &texMat) -{ - parser.parse_token(PARSE_SAMELINE); - if (parser.token != "(") - goto parse_error; - - for (int i = 0; i < 2; i++) { - parser.parse_token(PARSE_SAMELINE); - if (parser.token != "(") - goto parse_error; - - for (int j = 0; j < 3; j++) { - parser.parse_token(PARSE_SAMELINE); - texMat.at(i, j) = std::stod(parser.token); - } - - parser.parse_token(PARSE_SAMELINE); - if (parser.token != ")") - goto parse_error; - } - - parser.parse_token(PARSE_SAMELINE); - if (parser.token != ")") - goto parse_error; - - return; - -parse_error: - FError("{}: couldn't parse Brush Primitives texture info", parser.location); -} -#endif - static void ParseTextureDef(const mapentity_t &entity, const mapfile::brush_side_t &input_side, mapface_t &mapface, const mapbrush_t &brush, maptexinfo_t *tx, std::array &planepts, const qplane3d &plane, texture_def_issues_t &issue_stats) { @@ -1653,58 +829,6 @@ static void ParseTextureDef(const mapentity_t &entity, const mapfile::brush_side mapface.raw_info = extinfo.info; } -#if 0 - if (brush.format == brushformat_t::BRUSH_PRIMITIVES) { - ParseBrushPrimTX(parser, texMat); - tx_type = TX_BRUSHPRIM; - - parser.parse_token(PARSE_SAMELINE); - mapface.texname = parser.token; - - // Read extra Q2 params - extinfo = ParseExtendedTX(parser); - - mapface.raw_info = extinfo.info; - } else if (brush.format == brushformat_t::NORMAL) { - parser.parse_token(PARSE_SAMELINE); - mapface.texname = parser.token; - - parser.parse_token(PARSE_SAMELINE | PARSE_PEEK); - if (parser.token == "[") { - ParseValve220TX(parser, axis, shift, rotate, scale); - tx_type = TX_VALVE_220; - - // Read extra Q2 params - extinfo = ParseExtendedTX(parser); - } else { - parser.parse_token(PARSE_SAMELINE); - shift[0] = std::stod(parser.token); - parser.parse_token(PARSE_SAMELINE); - shift[1] = std::stod(parser.token); - parser.parse_token(PARSE_SAMELINE); - rotate = std::stod(parser.token); - parser.parse_token(PARSE_SAMELINE); - scale[0] = std::stod(parser.token); - parser.parse_token(PARSE_SAMELINE); - scale[1] = std::stod(parser.token); - - // Read extra Q2 params and/or QuArK subtype - extinfo = ParseExtendedTX(parser); - if (extinfo.quark_tx1) { - tx_type = TX_QUARK_TYPE1; - } else if (extinfo.quark_tx2) { - tx_type = TX_QUARK_TYPE2; - } else { - tx_type = TX_QUAKED; - } - } - - mapface.raw_info = extinfo.info; - } else { - FError("{}: Bad brush format", parser.location); - } -#endif - // if we have texture defs, see if we should remap this one if (auto it = qbsp_options.loaded_texture_defs.find(mapface.texname); it != qbsp_options.loaded_texture_defs.end()) { @@ -1815,24 +939,6 @@ static void ParseTextureDef(const mapentity_t &entity, const mapfile::brush_side } tx->vecs = input_side.vecs; - -#if 0 - switch (tx_type) { - case TX_QUARK_TYPE1: - case TX_QUARK_TYPE2: SetTexinfo_QuArK(parser, planepts, tx_type, tx); break; - case TX_VALVE_220: SetTexinfo_Valve220(axis, shift, scale, tx); break; - case TX_BRUSHPRIM: { - const auto &texture = map.load_image_meta(mapface.texname.c_str()); - const int32_t width = texture ? texture->width : 64; - const int32_t height = texture ? texture->height : 64; - - SetTexinfo_BrushPrimitives(texMat, plane.normal, width, height, tx->vecs); - break; - } - case TX_QUAKED: - default: SetTexinfo_QuakeEd(plane, planepts, shift, rotate, scale, tx); break; - } -#endif } bool mapface_t::set_planepts(const std::array &pts) @@ -1881,52 +987,6 @@ const qbsp_plane_t &mapface_t::get_positive_plane() const return map.get_plane(planenum & ~1); } -#if 0 -bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec) -{ - // TODO: This doesn't match how light does it (TexSpaceToWorld) - - const qvec3f tex_normal = qv::normalize(qv::cross(s_vec, t_vec)); - - for (int i = 0; i < 3; i++) - if (std::isnan(tex_normal[i])) - return false; - - const float cosangle = qv::dot(tex_normal, faceNormal); - if (std::isnan(cosangle)) - return false; - if (fabs(cosangle) < ZERO_EPSILON) - return false; - - return true; -} - -inline bool IsValidTextureProjection(const mapface_t &mapface, const maptexinfo_t *tx) -{ - return IsValidTextureProjection(mapface.get_plane().get_normal(), tx->vecs.row(0).xyz(), tx->vecs.row(1).xyz()); -} - -static void ValidateTextureProjection(mapface_t &mapface, maptexinfo_t *tx, texture_def_issues_t &issue_stats) -{ - if (!IsValidTextureProjection(mapface, tx)) { - if (qbsp_options.verbose.value()) { - logging::print("WARNING: {}: repairing invalid texture projection (\"{}\" near {} {} {})\n", mapface.line, - mapface.texname, (int)mapface.planepts[0][0], (int)mapface.planepts[0][1], (int)mapface.planepts[0][2]); - } else { - issue_stats.num_repaired++; - } - - // Reset texturing to sensible defaults - const std::array shift{0, 0}; - const double rotate = 0; - const std::array scale = {1, 1}; - SetTexinfo_QuakeEd(mapface.get_plane(), mapface.planepts, shift, rotate, scale, tx); - - Q_assert(IsValidTextureProjection(mapface, tx)); - } -} -#endif - static std::optional ParseBrushFace( const mapfile::brush_side_t &input_side, const mapbrush_t &brush, const mapentity_t &entity, texture_def_issues_t &issue_stats) { @@ -3321,173 +2381,6 @@ void LoadMapFile() logging::print(logging::flag::STAT, "\n"); } -#if 0 -static old_texdef_valve_t TexDef_BSPToValve(const texvecf &in_vecs) -{ - old_texdef_valve_t res; - - // From the valve -> bsp code, - // - // for (i = 0; i < 3; i++) { - // out->vecs[0][i] = axis[0][i] / scale[0]; - // out->vecs[1][i] = axis[1][i] / scale[1]; - // } - // - // We'll generate axis vectors of length 1 and pick the necessary scale - - for (int i = 0; i < 2; i++) { - qvec3d axis = in_vecs.row(i).xyz(); - const double length = qv::normalizeInPlace(axis); - // avoid division by 0 - if (length != 0.0) { - res.scale[i] = 1.0 / length; - } else { - res.scale[i] = 0.0; - } - res.shift[i] = in_vecs.at(i, 3); - res.axis.set_row(i, axis); - } - - return res; -} - -static void fprintDoubleAndSpc(std::ofstream &f, double v) -{ - int rounded = rint(v); - if (static_cast(rounded) == v) { - ewt::print(f, "{} ", rounded); - } else if (std::isfinite(v)) { - ewt::print(f, "{:0.17} ", v); - } else { - printf("WARNING: suppressing nan or infinity\n"); - f << "0 "; - } -} - -static void ConvertMapFace(std::ofstream &f, const mapface_t &mapface, const conversion_t format) -{ - const auto &texture = map.load_image_meta(mapface.texname.c_str()); - - const maptexinfo_t &texinfo = mapface.get_texinfo(); - - // Write plane points - for (int i = 0; i < 3; i++) { - f << " ( "; - for (int j = 0; j < 3; j++) { - fprintDoubleAndSpc(f, mapface.planepts[i][j]); - } - f << ") "; - } - - switch (format) { - case conversion_t::quake: - case conversion_t::quake2: { - const old_texdef_quake_ed_t quakeed = - TexDef_BSPToQuakeEd(mapface.get_plane(), texture, texinfo.vecs, mapface.planepts); - - ewt::print(f, "{} ", mapface.texname); - fprintDoubleAndSpc(f, quakeed.shift[0]); - fprintDoubleAndSpc(f, quakeed.shift[1]); - fprintDoubleAndSpc(f, quakeed.rotate); - fprintDoubleAndSpc(f, quakeed.scale[0]); - fprintDoubleAndSpc(f, quakeed.scale[1]); - - if (mapface.raw_info.has_value()) { - f << mapface.raw_info->contents_native << " " << mapface.raw_info->flags.native << " " - << mapface.raw_info->value; - } - - break; - } - case conversion_t::valve: { - const old_texdef_valve_t valve = TexDef_BSPToValve(texinfo.vecs); - - ewt::print(f, "{} [ ", mapface.texname); - fprintDoubleAndSpc(f, valve.axis.at(0, 0)); - fprintDoubleAndSpc(f, valve.axis.at(0, 1)); - fprintDoubleAndSpc(f, valve.axis.at(0, 2)); - fprintDoubleAndSpc(f, valve.shift[0]); - f << "] [ "; - fprintDoubleAndSpc(f, valve.axis.at(1, 0)); - fprintDoubleAndSpc(f, valve.axis.at(1, 1)); - fprintDoubleAndSpc(f, valve.axis.at(1, 2)); - fprintDoubleAndSpc(f, valve.shift[1]); - f << "] 0 "; - fprintDoubleAndSpc(f, valve.scale[0]); - fprintDoubleAndSpc(f, valve.scale[1]); - - if (mapface.raw_info.has_value()) { - f << mapface.raw_info->contents_native << " " << mapface.raw_info->flags.native << " " - << mapface.raw_info->value; - } - - break; - } - case conversion_t::bp: { - int texSize[2]; - texSize[0] = texture ? texture->width : 64; - texSize[1] = texture ? texture->height : 64; - - const texdef_brush_primitives_t bp = - TexDef_BSPToBrushPrimitives(mapface.get_plane(), texSize, texinfo.vecs); - f << "( ( "; - fprintDoubleAndSpc(f, bp.at(0, 0)); - fprintDoubleAndSpc(f, bp.at(0, 1)); - fprintDoubleAndSpc(f, bp.at(0, 2)); - f << ") ( "; - fprintDoubleAndSpc(f, bp.at(1, 0)); - fprintDoubleAndSpc(f, bp.at(1, 1)); - fprintDoubleAndSpc(f, bp.at(1, 2)); - - // N.B.: always print the Q2/Q3 flags - ewt::print(f, ") ) {} ", mapface.texname); - - if (mapface.raw_info.has_value()) { - f << mapface.raw_info->contents_native << " " << mapface.raw_info->flags.native << " " - << mapface.raw_info->value; - } else { - f << "0 0 0"; - } - - break; - } - default: FError("Internal error: unknown texcoord_style_t\n"); - } - - f << '\n'; -} - -static void ConvertMapBrush(std::ofstream &f, const mapbrush_t &mapbrush, const conversion_t format) -{ - f << "{\n"; - if (format == conversion_t::bp) { - f << "brushDef\n"; - f << "{\n"; - } - for (int i = 0; i < mapbrush.faces.size(); i++) { - ConvertMapFace(f, mapbrush.faces[i], format); - } - if (format == conversion_t::bp) { - f << "}\n"; - } - f << "}\n"; -} - -static void ConvertEntity(std::ofstream &f, const mapentity_t &entity, const conversion_t format) -{ - f << "{\n"; - - for (const auto &[key, value] : entity.epairs) { - ewt::print(f, "\"{}\" \"{}\"\n", key, value); - } - - for (auto &mapbrush : entity.mapbrushes) { - ConvertMapBrush(f, mapbrush, format); - } - f << "}\n"; -} -#endif - void ConvertMapFile() { logging::funcheader();