qbsp: fix QuakeEd map conversion
This commit is contained in:
parent
ce673b813e
commit
94ba5560c7
710
qbsp/map.cc
710
qbsp/map.cc
|
|
@ -30,12 +30,86 @@
|
|||
#include "parser.hh"
|
||||
#include "wad.hh"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#define info_player_start 1
|
||||
#define info_player_deathmatch 2
|
||||
#define info_player_coop 4
|
||||
|
||||
static int rgfStartSpots;
|
||||
|
||||
class texdef_valve_t {
|
||||
public:
|
||||
vec3_t axis[2];
|
||||
vec_t scale[2];
|
||||
vec_t shift[2];
|
||||
|
||||
texdef_valve_t() {
|
||||
for (int i=0;i<2;i++)
|
||||
for (int j=0;j<3;j++)
|
||||
axis[i][j] = 0;
|
||||
|
||||
for (int i=0;i<2;i++)
|
||||
scale[i] = 0;
|
||||
|
||||
for (int i=0;i<2;i++)
|
||||
shift[i] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class texdef_quake_ed_t {
|
||||
public:
|
||||
vec_t rotate;
|
||||
vec_t scale[2];
|
||||
vec_t shift[2];
|
||||
|
||||
texdef_quake_ed_t() : rotate(0) {
|
||||
scale[0] = 0;
|
||||
scale[1] = 0;
|
||||
shift[0] = 0;
|
||||
shift[1] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class texdef_quake_ed_noshift_t {
|
||||
public:
|
||||
vec_t rotate;
|
||||
vec_t scale[2];
|
||||
|
||||
texdef_quake_ed_noshift_t() : rotate(0) {
|
||||
scale[0] = 0;
|
||||
scale[1] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class texdef_etp_t {
|
||||
public:
|
||||
vec3_t planepoints[3];
|
||||
bool tx2;
|
||||
|
||||
texdef_etp_t() : tx2(false) {
|
||||
for (int i=0;i<3;i++)
|
||||
for (int j=0;j<3;j++)
|
||||
planepoints[i][j] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class texdef_brush_primitives_t {
|
||||
public:
|
||||
vec3_t texMat[2];
|
||||
|
||||
texdef_brush_primitives_t() {
|
||||
for (int i=0;i<2;i++)
|
||||
for (int j=0;j<3;j++)
|
||||
texMat[i][j] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
static texdef_valve_t TexDef_BSPToValve(const float in_vecs[2][4]);
|
||||
static glm::vec2 projectToAxisPlane(const vec3_t snapped_normal, glm::vec3 point);
|
||||
static texdef_quake_ed_noshift_t Reverse_QuakeEd(glm::mat2x2 M, const plane_t *plane, bool preserveX);
|
||||
static void SetTexinfo_QuakeEd_New(const plane_t *plane, const vec_t shift[2], vec_t rotate, const vec_t scale[2], float out_vecs[2][4]);
|
||||
|
||||
const mapface_t &mapbrush_t::face(int i) const {
|
||||
if (i < 0 || i >= this->numfaces)
|
||||
Error("mapbrush_t::face: %d out of bounds (numfaces %d)", i, this->numfaces);
|
||||
|
|
@ -357,111 +431,466 @@ ParseExtendedTX(parser_t *parser)
|
|||
return style;
|
||||
}
|
||||
|
||||
class texdef_quake_ed_t {
|
||||
public:
|
||||
vec_t rotate;
|
||||
vec_t scale[2];
|
||||
vec_t shift[2];
|
||||
};
|
||||
static glm::mat4x4 texVecsTo4x4Matrix(const plane_t &faceplane, const float in_vecs[2][4])
|
||||
{
|
||||
// [s]
|
||||
// T * vec = [t]
|
||||
// [distOffPlane]
|
||||
// [?]
|
||||
|
||||
glm::mat4x4 T(in_vecs[0][0], in_vecs[1][0], faceplane.normal[0], 0, // col 0
|
||||
in_vecs[0][1], in_vecs[1][1], faceplane.normal[1], 0, // col 1
|
||||
in_vecs[0][2], in_vecs[1][2], faceplane.normal[2], 0, // col 2
|
||||
in_vecs[0][3], in_vecs[1][3], -faceplane.dist, 1 // col 3
|
||||
);
|
||||
return T;
|
||||
}
|
||||
|
||||
static inline glm::vec3 vec3_t_to_glm(const vec3_t vec) {
|
||||
return glm::vec3(vec[0], vec[1], vec[2]);
|
||||
}
|
||||
|
||||
static glm::mat2x2 scale2x2(float xscale, float yscale)
|
||||
{
|
||||
glm::mat2x2 M( xscale, 0, // col 0
|
||||
0, yscale); // col1
|
||||
return M;
|
||||
}
|
||||
|
||||
static glm::mat2x2 rotation2x2_deg(float degrees)
|
||||
{
|
||||
float r = degrees * (Q_PI / 180.0);
|
||||
float cosr = cos(r);
|
||||
float sinr = sin(r);
|
||||
|
||||
// [ cosTh -sinTh ]
|
||||
// [ sinTh cosTh ]
|
||||
|
||||
glm::mat2x2 M( cosr, sinr, // col 0
|
||||
-sinr, cosr); // col1
|
||||
|
||||
return M;
|
||||
}
|
||||
|
||||
static float extractRotation(glm::mat2x2 m) {
|
||||
glm::vec2 point = m * glm::vec2(1, 0); // choice of this matters if there's shearing
|
||||
float rotation = atan2(point.y, point.x) * 180.0 / Q_PI;
|
||||
return rotation;
|
||||
}
|
||||
|
||||
static glm::vec2 evalTexDefAtPoint(const texdef_quake_ed_t &texdef, const plane_t *faceplane, const glm::vec3 point)
|
||||
{
|
||||
float temp[2][4];
|
||||
SetTexinfo_QuakeEd_New(faceplane, texdef.shift, texdef.rotate, texdef.scale, temp);
|
||||
|
||||
const glm::mat4x4 worldToTexSpace_res = texVecsTo4x4Matrix(*faceplane, temp);
|
||||
const glm::vec2 uv = glm::vec2(worldToTexSpace_res * glm::vec4(point, 1.0f));
|
||||
return uv;
|
||||
}
|
||||
|
||||
static float
|
||||
TexDefRMSE(const texdef_quake_ed_t &texdef, const plane_t *faceplane, const glm::mat4x4 referenceXform, const vec3_t facepoints[3])
|
||||
{
|
||||
float avgSquaredDist = 0;
|
||||
for (int i=0; i<3; i++) {
|
||||
glm::vec3 worldPoint = vec3_t_to_glm(facepoints[i]);
|
||||
glm::vec2 observed = evalTexDefAtPoint(texdef, faceplane, worldPoint);
|
||||
glm::vec2 expected = glm::vec2(referenceXform * glm::vec4(worldPoint, 1.0f));
|
||||
|
||||
glm::vec2 distVec = observed - expected;
|
||||
float dist2 = glm::dot(distVec, distVec);
|
||||
avgSquaredDist += dist2;
|
||||
}
|
||||
avgSquaredDist /= 3.0;
|
||||
return sqrt(avgSquaredDist);
|
||||
}
|
||||
|
||||
static texdef_quake_ed_t addShift(const texdef_quake_ed_noshift_t &texdef, const glm::vec2 shift)
|
||||
{
|
||||
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.x;
|
||||
res2.shift[1] = shift.y;
|
||||
return res2;
|
||||
}
|
||||
|
||||
void checkEq(const glm::vec2 &a, const glm::vec2 &b, float epsilon)
|
||||
{
|
||||
for (int i=0; i<2; i++) {
|
||||
if (fabs(a[i] - b[i]) > epsilon) {
|
||||
printf("warning, checkEq failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static texdef_quake_ed_t
|
||||
TexDef_BSPToQuakeEd(const plane_t &faceplane, const float in_vecs[2][4])
|
||||
TexDef_BSPToQuakeEd(const plane_t &faceplane, const float in_vecs[2][4], const vec3_t facepoints[3])
|
||||
{
|
||||
// First get the un-rotated, un-scaled texture vecs (based on the face plane).
|
||||
// First get the un-rotated, un-scaled unit texture vecs (based on the face plane).
|
||||
vec3_t snapped_normal;
|
||||
vec3_t unrotated_vecs[2];
|
||||
TextureAxisFromPlane(&faceplane, unrotated_vecs[0], unrotated_vecs[1], snapped_normal);
|
||||
|
||||
// These axes rotate counterclockwise
|
||||
const vec3_t posX { 1,0,0};
|
||||
const vec3_t negY { 0,-1,0};
|
||||
const vec3_t posZ { 0,0,1};
|
||||
const glm::mat4x4 worldToTexSpace = texVecsTo4x4Matrix(faceplane, in_vecs);
|
||||
|
||||
const bool ccw = (VectorCompare(posX, snapped_normal)
|
||||
|| VectorCompare(negY, snapped_normal)
|
||||
|| VectorCompare(posZ, snapped_normal));
|
||||
|
||||
// Extract the final scale (magnitude only, we don't know the sign yet) and shift values
|
||||
// Also normalize the BSP texture axes and store in rotated_vec.
|
||||
vec_t scale[2], shift[2];
|
||||
vec3_t rotated_vec[2];
|
||||
for (int i=0; i<2; i++) {
|
||||
for (int j=0; j<3; j++) {
|
||||
rotated_vec[i][j] = in_vecs[i][j];
|
||||
}
|
||||
const vec_t length = VectorNormalize(rotated_vec[i]);
|
||||
if (length == 0.0) {
|
||||
// Hack around bad input
|
||||
scale[i] = 0.0;
|
||||
} else {
|
||||
scale[i] = 1.0 / length;
|
||||
}
|
||||
|
||||
shift[i] = in_vecs[i][3];
|
||||
// Grab the UVs of the 3 reference points
|
||||
glm::vec2 facepoints_uvs[3];
|
||||
for (int i=0; i<3; i++) {
|
||||
facepoints_uvs[i] = glm::vec2(worldToTexSpace * glm::vec4(facepoints[i][0], facepoints[i][1], facepoints[i][2], 1.0));
|
||||
}
|
||||
|
||||
// We know that both unrotated_vecs were rotated by the same amount,
|
||||
// then each possibly was multiplied by -1, to get rotated_vecs.
|
||||
// Project the 3 reference points onto the axis plane. They are now 2d points.
|
||||
glm::vec2 facepoints_projected[3];
|
||||
for (int i=0; i<3; i++) {
|
||||
facepoints_projected[i] = projectToAxisPlane(snapped_normal, vec3_t_to_glm(facepoints[i]));
|
||||
}
|
||||
|
||||
// Now make 2 vectors out of our 3 points (so we are ignoring translation for now)
|
||||
const glm::vec2 p0p1 = facepoints_projected[1] - facepoints_projected[0];
|
||||
const glm::vec2 p0p2 = facepoints_projected[2] - facepoints_projected[0];
|
||||
|
||||
const glm::vec2 p0p1_uv = facepoints_uvs[1] - facepoints_uvs[0];
|
||||
const glm::vec2 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 glm::mat4x4 M(p0p1.x, 0, p0p2.x, 0, // col 0
|
||||
p0p1.y, 0, p0p2.y, 0, // col 1
|
||||
0, p0p1.x, 0, p0p2.x, // col 2
|
||||
0, p0p1.y, 0, p0p2.y // col 3
|
||||
);
|
||||
|
||||
const glm::mat4x4 Minv = glm::inverse(M);
|
||||
const glm::vec4 abcd = Minv * glm::vec4(p0p1_uv.x,
|
||||
p0p1_uv.y,
|
||||
p0p2_uv.x,
|
||||
p0p2_uv.y);
|
||||
|
||||
const glm::mat2x2 texPlaneToUV(abcd[0], abcd[2], // col 0
|
||||
abcd[1], abcd[3]);// col 1
|
||||
|
||||
{
|
||||
// self check
|
||||
glm::vec2 uv01_test = texPlaneToUV * p0p1;
|
||||
glm::vec2 uv02_test = texPlaneToUV * p0p2;
|
||||
checkEq(uv01_test, p0p1_uv, 0.01);
|
||||
checkEq(uv02_test, p0p2_uv, 0.01);
|
||||
}
|
||||
|
||||
// Generate 2 scenarios, snapping to X and Y, and see which is better
|
||||
// (they are the same unless there is shearing involved)
|
||||
texdef_quake_ed_t texdefs[2];
|
||||
|
||||
for (int i=0; i<2; i++) {
|
||||
const bool preserveX = (i == 0);
|
||||
const texdef_quake_ed_noshift_t res = Reverse_QuakeEd(texPlaneToUV, &faceplane, preserveX);
|
||||
|
||||
// figure out shift based on the average of the facepoints, which is hopefully in the middle of the face
|
||||
|
||||
glm::vec3 testpoint = (vec3_t_to_glm(facepoints[0])
|
||||
+ vec3_t_to_glm(facepoints[1])
|
||||
+ vec3_t_to_glm(facepoints[2])) / 3.0f;
|
||||
|
||||
glm::vec2 uv0_actual = evalTexDefAtPoint(addShift(res, glm::vec2(0,0)), &faceplane, testpoint);
|
||||
glm::vec2 uv0_desired = glm::vec2(worldToTexSpace * glm::vec4(testpoint, 1.0f));
|
||||
glm::vec2 shift = uv0_desired - uv0_actual;
|
||||
|
||||
const texdef_quake_ed_t res2 = addShift(res, shift);
|
||||
texdefs[i] = res2;
|
||||
}
|
||||
|
||||
// See which has less distortion
|
||||
float rmse[2];
|
||||
for (int i=0; i<2; i++)
|
||||
rmse[i] = TexDefRMSE(texdefs[i], &faceplane, worldToTexSpace, facepoints);
|
||||
|
||||
if (rmse[0] < rmse[1]) {
|
||||
return texdefs[0];
|
||||
} else {
|
||||
return texdefs[1];
|
||||
}
|
||||
}
|
||||
|
||||
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<int,int> getSTAxes(const vec3_t 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 glm::vec2 projectToAxisPlane(const vec3_t snapped_normal, glm::vec3 point)
|
||||
{
|
||||
const std::pair<int,int> axes = getSTAxes(snapped_normal);
|
||||
|
||||
const glm::vec2 proj(point[axes.first],
|
||||
point[axes.second]);
|
||||
return proj;
|
||||
}
|
||||
|
||||
float clockwiseDegreesBetween(glm::vec2 start, glm::vec2 end)
|
||||
{
|
||||
start = glm::normalize(start);
|
||||
end = glm::normalize(end);
|
||||
|
||||
const float cosAngle = qmax(-1.0f, qmin(1.0f, glm::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 (glm::vec3(0,0,1)), it's counterclockwise rotation.
|
||||
// if this is pointing down (glm::vec3(0,0,-1)), it's clockwise rotation.
|
||||
glm::vec3 rotationNormal = glm::normalize(glm::cross(glm::vec3(start, 0.0f), glm::vec3(end, 0.0f)));
|
||||
|
||||
const float normalsCosAngle = glm::dot(rotationNormal, glm::vec3(0,0,1));
|
||||
if (normalsCosAngle >= 0) {
|
||||
// counterclockwise rotation
|
||||
return -unsigned_degrees;
|
||||
}
|
||||
// clockwise rotation
|
||||
return unsigned_degrees;
|
||||
}
|
||||
|
||||
static texdef_quake_ed_noshift_t
|
||||
Reverse_QuakeEd(glm::mat2x2 M, const plane_t *plane, bool preserveX)
|
||||
{
|
||||
// Check for shear, because we might tweak M to remove it
|
||||
{
|
||||
glm::vec2 Xvec = glm::vec2(M[0][0], M[1][0]);
|
||||
glm::vec2 Yvec = glm::vec2(M[0][1], M[1][1]);
|
||||
double cosAngle = glm::dot(glm::normalize(Xvec), glm::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 glm::vec2 newYdir = glm::normalize(
|
||||
glm::vec2(glm::cross(glm::vec3(0, 0, CW ? -1.0f : 1.0f), glm::vec3(Xvec, 0.0))));
|
||||
|
||||
// scalar projection of the old Yvec onto newYDir to get the new Yscale
|
||||
const float newYscale = glm::dot(Yvec, newYdir);
|
||||
Yvec = newYdir * static_cast<float>(newYscale);
|
||||
} else {
|
||||
// Preserve Y.
|
||||
|
||||
const float degreesToX = clockwiseDegreesBetween(Yvec, Xvec);
|
||||
const bool CW = (degreesToX > 0);
|
||||
|
||||
// turn 90 degrees from Yvec
|
||||
const glm::vec2 newXdir = glm::normalize(
|
||||
glm::vec2(glm::cross(glm::vec3(0, 0, CW ? -1.0f : 1.0f), glm::vec3(Yvec, 0.0))));
|
||||
|
||||
// scalar projection of the old Xvec onto newXDir to get the new Xscale
|
||||
const float newXscale = glm::dot(Xvec, newXdir);
|
||||
Xvec = newXdir * static_cast<float>(newXscale);
|
||||
}
|
||||
|
||||
// recheck
|
||||
cosAngle = glm::dot(glm::normalize(Xvec), glm::normalize(Yvec));
|
||||
if (fabs(cosAngle) > 0.001) {
|
||||
Error("SHEAR correction failed\n");
|
||||
}
|
||||
|
||||
// update M
|
||||
M[0][0] = Xvec[0];
|
||||
M[1][0] = Xvec[1];
|
||||
|
||||
M[0][1] = Yvec[0];
|
||||
M[1][1] = Yvec[1];
|
||||
}
|
||||
}
|
||||
|
||||
// extract abs(scale)
|
||||
const double absXscale = sqrt(pow(M[0][0], 2.0) + pow(M[1][0], 2.0));
|
||||
const double absYscale = sqrt(pow(M[0][1], 2.0) + pow(M[1][1], 2.0));
|
||||
const glm::mat2x2 applyAbsScaleM(absXscale, // col0
|
||||
0,
|
||||
0, // col1
|
||||
absYscale);
|
||||
|
||||
vec3_t vecs[2];
|
||||
vec3_t snapped_normal;
|
||||
TextureAxisFromPlane(plane, vecs[0], vecs[1], snapped_normal);
|
||||
|
||||
const glm::vec2 sAxis = projectToAxisPlane(snapped_normal, vec3_t_to_glm(vecs[0]));
|
||||
const glm::vec2 tAxis = projectToAxisPlane(snapped_normal, vec3_t_to_glm(vecs[1]));
|
||||
|
||||
// This is an identity matrix possibly with negative signs.
|
||||
const glm::mat2x2 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.
|
||||
//
|
||||
// To figure out the signs on the scales, try all 4 possibilities.
|
||||
float angles[4][2];
|
||||
for (int sign_index=0; sign_index<4; sign_index++) {
|
||||
float sign0 = (sign_index / 2) == 1 ? -1.0 : 1.0;
|
||||
float sign1 = (sign_index % 2) == 1 ? -1.0 : 1.0;
|
||||
// glm::mat2x2 M = scaleM * rotateM * axisFlipsM;
|
||||
|
||||
vec3_t rotated_flipped_vec[2];
|
||||
VectorScale(rotated_vec[0], sign0, rotated_flipped_vec[0]);
|
||||
VectorScale(rotated_vec[1], sign1, rotated_flipped_vec[1]);
|
||||
// strip off the magnitude component of the scale, and `axisFlipsM`.
|
||||
const glm::mat2x2 flipRotate = glm::inverse(applyAbsScaleM) * M * glm::inverse(axisFlipsM);
|
||||
|
||||
// Try to reproduce the rotation
|
||||
for (int i=0; i<2; i++)
|
||||
{
|
||||
float angle = SignedDegreesBetweenUnitVectors(unrotated_vecs[i], rotated_flipped_vec[i], snapped_normal);
|
||||
// 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<float>{ -1.0, 1.0 }) {
|
||||
for (float yScaleSgn : std::vector<float>{ -1.0, 1.0 }) {
|
||||
|
||||
if (ccw)
|
||||
angle *= -1;
|
||||
// "apply" - matrix constructed to apply a guessed value
|
||||
// "guess" - this matrix might not be what we think
|
||||
|
||||
angles[sign_index][i] = angle;
|
||||
const glm::mat2x2 applyGuessedFlipM(
|
||||
xScaleSgn, // col0
|
||||
0,
|
||||
0, // col1
|
||||
yScaleSgn);
|
||||
|
||||
const glm::mat2x2 rotateMGuess = glm::inverse(applyGuessedFlipM) * flipRotate;
|
||||
const float angleGuess = extractRotation(rotateMGuess);
|
||||
|
||||
const glm::mat2x2 Mident = rotateMGuess * rotation2x2_deg(-angleGuess);
|
||||
|
||||
const glm::mat2x2 applyAngleGuessM = rotation2x2_deg(angleGuess);
|
||||
const glm::mat2x2 Mguess = applyGuessedFlipM * applyAbsScaleM * applyAngleGuessM * axisFlipsM;
|
||||
|
||||
if (fabs(M[0][0] - Mguess[0][0]) < 0.001
|
||||
&& fabs(M[0][1] - Mguess[0][1]) < 0.001
|
||||
&& fabs(M[1][0] - Mguess[1][0]) < 0.001
|
||||
&& fabs(M[1][1] - Mguess[1][1]) < 0.001) {
|
||||
|
||||
texdef_quake_ed_noshift_t reversed;
|
||||
reversed.rotate = angleGuess;
|
||||
reversed.scale[0] = xScaleSgn / absXscale;
|
||||
reversed.scale[1] = yScaleSgn / absYscale;
|
||||
return reversed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float bestangle = 0;
|
||||
printf("Warning, Reverse_QuakeEd failed\n");
|
||||
|
||||
float closest_angle_dist = FLT_MAX;
|
||||
int best_signindex = -1;
|
||||
|
||||
// There should be a combination of signs that give both vectors the same amount of rotation
|
||||
for (int i=0; i<4; i++) {
|
||||
const float dist = fabs(angles[i][0] - angles[i][1]);
|
||||
if (dist < closest_angle_dist) {
|
||||
closest_angle_dist = dist;
|
||||
bestangle = angles[i][0];
|
||||
best_signindex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest_angle_dist > 0.01) {
|
||||
// printf("unequal rotation detected\n");
|
||||
}
|
||||
|
||||
float sign0 = (best_signindex / 2) == 1 ? -1.0 : 1.0;
|
||||
float sign1 = (best_signindex % 2) == 1 ? -1.0 : 1.0;
|
||||
|
||||
scale[0] *= sign0;
|
||||
scale[1] *= sign1;
|
||||
|
||||
texdef_quake_ed_t res;
|
||||
res.rotate = bestangle;
|
||||
for (int i=0; i<2; i++) {
|
||||
res.scale[i] = scale[i];
|
||||
res.shift[i] = shift[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
texdef_quake_ed_noshift_t fail;
|
||||
return fail;
|
||||
}
|
||||
|
||||
static void
|
||||
SetTexinfo_QuakeEd(const plane_t *plane, const vec_t shift[2], vec_t rotate,
|
||||
SetTexinfo_QuakeEd_New(const plane_t *plane, const vec_t shift[2], vec_t rotate, const vec_t scale[2], float out_vecs[2][4])
|
||||
{
|
||||
vec3_t vecs[2];
|
||||
vec3_t snapped_normal;
|
||||
TextureAxisFromPlane(plane, vecs[0], vecs[1], snapped_normal);
|
||||
|
||||
glm::vec2 sAxis = projectToAxisPlane(snapped_normal, vec3_t_to_glm(vecs[0]));
|
||||
glm::vec2 tAxis = projectToAxisPlane(snapped_normal, vec3_t_to_glm(vecs[1]));
|
||||
|
||||
// This is an identity matrix possibly with negative signs.
|
||||
glm::mat2x2 axisFlipsM(sAxis[0], tAxis[0], // col0
|
||||
sAxis[1], tAxis[1]); // col1
|
||||
|
||||
glm::mat2x2 rotateM = rotation2x2_deg(rotate);
|
||||
glm::mat2x2 scaleM = scale2x2(1.0/scale[0], 1.0/scale[1]);
|
||||
|
||||
glm::mat2x2 M = scaleM * rotateM * axisFlipsM;
|
||||
|
||||
if (false) {
|
||||
// Self-test for Reverse_QuakeEd
|
||||
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)) {
|
||||
Error("wrong rotat got %f expected %f\n",
|
||||
reversed.rotate, rotate);
|
||||
}
|
||||
|
||||
if (fabs(reversed.scale[0] - scale[0]) > 0.001
|
||||
|| fabs(reversed.scale[1] - scale[1]) > 0.001) {
|
||||
Error("wrong scale, got %f %f exp %f %f\n",
|
||||
reversed.scale[0], reversed.scale[1],
|
||||
scale[0], scale[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// copy M into the output vectors
|
||||
|
||||
for (int i=0; i<2; i++) {
|
||||
for (int j=0; j<4; j++) {
|
||||
out_vecs[i][j] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
const std::pair<int,int> axes = getSTAxes(snapped_normal);
|
||||
|
||||
// M[col][row]
|
||||
// S
|
||||
out_vecs[0][axes.first] = M[0][0];
|
||||
out_vecs[0][axes.second] = M[1][0];
|
||||
out_vecs[0][3] = shift[0];
|
||||
|
||||
// T
|
||||
out_vecs[1][axes.first] = M[0][1];
|
||||
out_vecs[1][axes.second] = M[1][1];
|
||||
out_vecs[1][3] = shift[1];
|
||||
}
|
||||
|
||||
static void
|
||||
SetTexinfo_QuakeEd(const plane_t *plane, const vec3_t planepts[3], const vec_t shift[2], vec_t rotate,
|
||||
const vec_t scale[2], mtexinfo_t *out)
|
||||
{
|
||||
int i, j;
|
||||
|
|
@ -507,6 +936,58 @@ SetTexinfo_QuakeEd(const plane_t *plane, const vec_t shift[2], vec_t rotate,
|
|||
|
||||
out->vecs[0][3] = shift[0];
|
||||
out->vecs[1][3] = shift[1];
|
||||
|
||||
if (false) {
|
||||
// Self-test of SetTexinfo_QuakeEd_New
|
||||
float check[2][4];
|
||||
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[i][j] - out->vecs[i][j]) > 0.001) {
|
||||
SetTexinfo_QuakeEd_New(plane, shift, rotate, scale, check);
|
||||
Error("fail");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false) {
|
||||
// Self-test of TexDef_BSPToQuakeEd
|
||||
texdef_quake_ed_t reversed = TexDef_BSPToQuakeEd(*plane, out->vecs, planepts);
|
||||
|
||||
if (!EqualDegrees(reversed.rotate, rotate)) {
|
||||
reversed.rotate += 180;
|
||||
reversed.scale[0] *= -1;
|
||||
reversed.scale[1] *= -1;
|
||||
}
|
||||
|
||||
if (!EqualDegrees(reversed.rotate, rotate)) {
|
||||
printf("wrong rotat got %f expected %f\n",
|
||||
reversed.rotate, rotate);
|
||||
}
|
||||
|
||||
if (fabs(reversed.scale[0] - scale[0]) > 0.001
|
||||
|| fabs(reversed.scale[1] - scale[1]) > 0.001) {
|
||||
printf("wrong scale, got %f %f exp %f %f\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) {
|
||||
printf("wrong shift, got %f %f exp %f %f\n",
|
||||
reversed.shift[0], reversed.shift[1],
|
||||
shift[0], shift[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
texdef_etp_t TexDef_BSPToETP(const plane_t &faceplane, const float in_vecs[2][4])
|
||||
{
|
||||
Error("unimplemented");
|
||||
texdef_etp_t res;
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -669,11 +1150,6 @@ SetTexinfo_BrushPrimitives(const vec3_t texMat[2], const vec3_t faceNormal, int
|
|||
vecs[1][3] = texHeight * texMat[1][2];
|
||||
}
|
||||
|
||||
class texdef_brush_primitives_t {
|
||||
public:
|
||||
vec3_t texMat[2];
|
||||
};
|
||||
|
||||
static void BSP_GetSTCoordsForPoint(const vec_t *point, const int texSize[2], const float in_vecs[2][4], vec_t *st_out)
|
||||
{
|
||||
for (int i=0; i<2; i++) {
|
||||
|
|
@ -879,7 +1355,7 @@ ParseTextureDef(parser_t *parser, mapface_t &mapface, const mapbrush_t *brush, m
|
|||
break;
|
||||
case TX_QUAKED:
|
||||
default:
|
||||
SetTexinfo_QuakeEd(plane, shift, rotate, scale, tx);
|
||||
SetTexinfo_QuakeEd(plane, planepts, shift, rotate, scale, tx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1095,12 +1571,7 @@ TexDefToString_QuarkType1(const mapface_t &mapface, const mtexinfo_t &texinfo)
|
|||
return "";
|
||||
}
|
||||
|
||||
class texdef_valve_t {
|
||||
public:
|
||||
vec3_t axis[2];
|
||||
vec_t scale[2];
|
||||
vec_t shift[2];
|
||||
};
|
||||
|
||||
|
||||
static texdef_valve_t
|
||||
TexDef_BSPToValve(const float in_vecs[2][4])
|
||||
|
|
@ -1138,21 +1609,38 @@ TexDef_BSPToValve(const float in_vecs[2][4])
|
|||
static void
|
||||
ConvertMapFace(FILE *f, const mapface_t &mapface, const texcoord_style_t format)
|
||||
{
|
||||
// All formats write the plane points the same way
|
||||
// FIXME: Not QaArK
|
||||
for (int i=0; i<3; i++) {
|
||||
fprintf(f, " ( ");
|
||||
for (int j=0; j<3; j++) {
|
||||
fprintf(f, "%0.17f ", mapface.planepts[i][j]);
|
||||
}
|
||||
fprintf(f, ") ");
|
||||
}
|
||||
|
||||
const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo);
|
||||
|
||||
bool tx2 = false;
|
||||
|
||||
|
||||
// Write plane points
|
||||
if (format == texcoord_style_t::TX_QUARK_TYPE1) {
|
||||
const texdef_etp_t etp = TexDef_BSPToETP(mapface.plane, texinfo.vecs);
|
||||
tx2 = etp.tx2;
|
||||
|
||||
for (int i=0; i<3; i++) {
|
||||
fprintf(f, " ( ");
|
||||
for (int j=0; j<3; j++) {
|
||||
fprintf(f, "%0.17f ", etp.planepoints[i][j]);
|
||||
}
|
||||
fprintf(f, ") ");
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<3; i++) {
|
||||
fprintf(f, " ( ");
|
||||
for (int j=0; j<3; j++) {
|
||||
fprintf(f, "%0.17f ", mapface.planepts[i][j]);
|
||||
}
|
||||
fprintf(f, ") ");
|
||||
}
|
||||
}
|
||||
|
||||
switch(format) {
|
||||
case texcoord_style_t::TX_QUARK_TYPE1:
|
||||
// fallthrough
|
||||
case texcoord_style_t::TX_QUAKED: {
|
||||
const texdef_quake_ed_t quakeed = TexDef_BSPToQuakeEd(mapface.plane, texinfo.vecs);
|
||||
const texdef_quake_ed_t quakeed = TexDef_BSPToQuakeEd(mapface.plane, texinfo.vecs, mapface.planepts);
|
||||
|
||||
fprintf(f, "%s %0.17f %0.17f %0.17f %0.17f %0.17f",
|
||||
mapface.texname.c_str(),
|
||||
|
|
@ -1163,9 +1651,6 @@ ConvertMapFace(FILE *f, const mapface_t &mapface, const texcoord_style_t format)
|
|||
quakeed.scale[1]);
|
||||
break;
|
||||
}
|
||||
case texcoord_style_t::TX_QUARK_TYPE1:
|
||||
Error("Unimplemented");
|
||||
break;
|
||||
case texcoord_style_t::TX_VALVE_220: {
|
||||
const texdef_valve_t valve = TexDef_BSPToValve(texinfo.vecs);
|
||||
|
||||
|
|
@ -1206,6 +1691,10 @@ ConvertMapFace(FILE *f, const mapface_t &mapface, const texcoord_style_t format)
|
|||
Error("Internal error: unknown texcoord_style_t\n");
|
||||
}
|
||||
|
||||
if (format == texcoord_style_t::TX_QUARK_TYPE1) {
|
||||
fprintf(f, (tx2 ? " //TX2" : " //TX1"));
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
|
|
@ -1252,6 +1741,9 @@ void ConvertMapFile(void)
|
|||
std::string filename = stripExt(options.szBSPName);
|
||||
|
||||
switch(options.convertMapTexFormat) {
|
||||
case texcoord_style_t::TX_QUARK_TYPE1:
|
||||
filename += "-etp.map";
|
||||
break;
|
||||
case texcoord_style_t::TX_QUAKED:
|
||||
filename += "-quake.map";
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Game: Quake
|
||||
// Format: Valve
|
||||
// entity 0
|
||||
{
|
||||
"classname" "worldspawn"
|
||||
"wad" "/Users/ericwa/quake/id1/maps/Q.wad"
|
||||
// brush 0
|
||||
{
|
||||
( -277.28203230275506 -57.788957970715167 36.889705641190304 ) ( -146.71796769724492 40.041972769559663 39.366401863926193 ) ( -138.71796769724489 19.485108132410282 16.184182032988584 ) nail0sid [ 0.866025 0.482963 -0.12941 -3.79573 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 351.264 4 4
|
||||
( -242.64101615137753 -38.470441444933805 31.713324739139875 ) ( -306.06664199358158 58.250883629404711 -31.770336642264994 ) ( -250.64101615137753 -17.913576807784423 54.895544570077483 ) nail0sid [ 0.433013 -0.595035 0.677077 -0.824245 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 316.991 4 4
|
||||
( -284.78203230275506 -38.5168973733876 58.623036732694317 ) ( -201.64359353944897 114.92162916692698 -48.748368087849883 ) ( -340.20765814495917 37.647563063801499 -28.04284447964816 ) nail0sid [ -0.866025 -0.482963 0.12941 3.79573 ] [ 0.433013 -0.595035 0.677077 -0.824245 ] 29.8988 4 4
|
||||
( -294.28203230275506 -14.105620616772715 86.15192278193274 ) ( -211.14359353944897 139.3329059235418 -21.219482038611467 ) ( -155.71796769724492 63.168445486352724 65.446399173731024 ) nail0sid [ 0.866025 0.482963 -0.12941 -3.79573 ] [ 0.433013 -0.595035 0.677077 -0.824245 ] 330.101 4 4
|
||||
( -279.0140831103239 -55.408818582053001 34.181396853304605 ) ( -148.45001850481378 42.422112158221836 36.658093076040487 ) ( -287.01408311032401 -34.851953944903627 57.36361668424221 ) plat_top1 [ -0.866025 -0.482963 0.12941 3.79573 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 8.73599 4 4
|
||||
( -177.26794919243548 -2.0133505539329053 21.944676676508113 ) ( -240.69357503463604 94.707974520405614 -41.538984704896748 ) ( -232.69357503463604 74.151109883256211 -64.72120453583436 ) nail0sid [ -0.433013 0.595035 -0.677077 0.824245 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 43.0089 4 4
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Game: Quake
|
||||
// Format: Valve
|
||||
// entity 0
|
||||
{
|
||||
"classname" "worldspawn"
|
||||
"wad" "/Users/ericwa/quake/id1/maps/Q.wad"
|
||||
// brush 0
|
||||
{
|
||||
( 268 704 429.65685424949152 ) ( 194 704 429.6568542494922 ) ( 194 706.82842712474667 426.82842712475201 ) nail0sid [ -0.333333 0.666667 0.666667 -22.5 ] [ -1.43302e-13 0.707107 -0.707107 0.497475 ] -0 4 4
|
||||
( 194 706.82842712474667 426.82842712475201 ) ( 194 704 429.6568542494922 ) ( -30 312.44365081390578 38.100505063377973 ) nail0sid [ 3.97817e-13 -0.707107 0.707107 0.497475 ] [ -0.374999 -0.655506 -0.655506 22.468 ] 315 4 4
|
||||
( -30 312.44365081390578 38.100505063377973 ) ( 194 704 429.6568542494922 ) ( 268 704 429.65685424949152 ) nail0sid [ -0.333333 0.760839 -0.572494 -138.457 ] [ 1.27182e-14 0.707107 0.707107 23.4041 ] -0 -1.2517 -4
|
||||
( -30 315.27207793863823 35.272077938640045 ) ( 44 315.27207793863818 35.272077938639995 ) ( 268 706.82842712474633 426.82842712475167 ) plat_top1 [ 0.329466 -0.551536 0.766329 39.7436 ] [ -0.0506272 0.800158 0.597649 0.313782 ] 8.73599 4 4
|
||||
( 268 706.82842712474633 426.82842712475167 ) ( 44 315.27207793863818 35.272077938639995 ) ( 44 312.44365081390606 38.100505063377568 ) nail0sid [ 3.25542e-13 0.707107 -0.707107 -0.497475 ] [ -0.374999 -0.655506 -0.655506 22.468 ] 45 4 4
|
||||
( 44 312.44365081390606 38.100505063377568 ) ( 44 315.27207793863818 35.272077938639995 ) ( -30 315.27207793863823 35.272077938640045 ) nail0sid [ 0.333333 -0.666667 -0.666667 22.5 ] [ 7.66137e-13 0.707107 -0.707107 0.497475 ] -0 4 4
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Game: Quake
|
||||
// Format: Valve
|
||||
// entity 0
|
||||
{
|
||||
"classname" "worldspawn"
|
||||
"wad" "/Users/ericwa/quake/id1/maps/Q.wad"
|
||||
// brush 0
|
||||
{
|
||||
( 12 150.14213562372692 62.142135623730631 ) ( -318 150.14213562372697 62.14213562373061 ) ( -190 123.27207793864181 35.272077938643633 ) plat_top1 [ -0.988399 -0.107397 -0.107397 -4.94739 ] [ 0.151882 -0.698903 -0.698903 36.8417 ] 8.73599 4 4
|
||||
( 140 120.44365081389603 38.100505063385761 ) ( 12 147.31370849898212 64.970562748475487 ) ( 12 150.14213562372692 62.142135623730631 ) nail0sid [ -3.21965e-15 0.707107 -0.707107 -30.5564 ] [ 0.958647 -0.201241 -0.201241 20.5269 ] 45 4 4
|
||||
( -190 123.27207793864181 35.272077938643633 ) ( -190 120.44365081389697 38.100505063386663 ) ( 140 120.44365081389603 38.100505063385761 ) nail0sid [ -1 4.01478e-16 4.67348e-13 14.5 ] [ 3.30319e-13 -0.707107 0.707107 30.5564 ] -0 4 4
|
||||
( 12 147.31370849898212 64.970562748475487 ) ( -318 147.31370849898215 64.970562748475473 ) ( -318 150.14213562372697 62.14213562373061 ) nail0sid [ 1 -3.89195e-16 2.27596e-15 -14.5 ] [ -1.7486e-15 -0.707107 0.707107 30.5564 ] -0 4 4
|
||||
( -318 150.14213562372697 62.14213562373061 ) ( -318 147.31370849898215 64.970562748475473 ) ( -190 120.44365081389697 38.100505063386663 ) nail0sid [ 9.29812e-15 -0.707107 0.707107 30.5564 ] [ 0.958647 -0.201241 -0.201241 20.5269 ] 315 4 4
|
||||
( -190 120.44365081389697 38.100505063386663 ) ( -318 147.31370849898215 64.970562748475473 ) ( 12 147.31370849898212 64.970562748475487 ) nail0sid [ 1 2.4885 2.4885 -113.134 ] [ 8.32667e-17 -0.707107 -0.707107 20.5269 ] -0 4 4
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
// Game: Quake
|
||||
// Format: Valve
|
||||
// entity 0
|
||||
{
|
||||
"classname" "worldspawn"
|
||||
"wad" "/Users/ericwa/quake/id1/maps/Q.wad"
|
||||
// brush 0
|
||||
{
|
||||
( -277.28203230275506 -57.788957970715167 36.889705641190304 ) ( -146.71796769724492 40.041972769559663 39.366401863926193 ) ( -138.71796769724489 19.485108132410282 16.184182032988584 ) nail0sid [ 0.866025 0.482963 -0.12941 -3.79573 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 351.264 4 4
|
||||
( -242.64101615137753 -38.470441444933805 31.713324739139875 ) ( -306.06664199358158 58.250883629404711 -31.770336642264994 ) ( -250.64101615137753 -17.913576807784423 54.895544570077483 ) nail0sid [ 0.433013 -0.595035 0.677077 -0.824245 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 316.991 4 4
|
||||
( -284.78203230275506 -38.5168973733876 58.623036732694317 ) ( -201.64359353944897 114.92162916692698 -48.748368087849883 ) ( -340.20765814495917 37.647563063801499 -28.04284447964816 ) nail0sid [ -0.866025 -0.482963 0.12941 3.79573 ] [ 0.433013 -0.595035 0.677077 -0.824245 ] 29.8988 4 4
|
||||
( -294.28203230275506 -14.105620616772715 86.15192278193274 ) ( -211.14359353944897 139.3329059235418 -21.219482038611467 ) ( -155.71796769724492 63.168445486352724 65.446399173731024 ) nail0sid [ 0.866025 0.482963 -0.12941 -3.79573 ] [ 0.433013 -0.595035 0.677077 -0.824245 ] 330.101 4 4
|
||||
( -279.0140831103239 -55.408818582053001 34.181396853304605 ) ( -148.45001850481378 42.422112158221836 36.658093076040487 ) ( -287.01408311032401 -34.851953944903627 57.36361668424221 ) plat_top1 [ -0.866025 -0.482963 0.12941 3.79573 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 8.73599 4 4
|
||||
( -177.26794919243548 -2.0133505539329053 21.944676676508113 ) ( -240.69357503463604 94.707974520405614 -41.538984704896748 ) ( -232.69357503463604 74.151109883256211 -64.72120453583436 ) nail0sid [ -0.433013 0.595035 -0.677077 0.824245 ] [ 0.25 -0.642402 -0.724444 14.7303 ] 43.0089 4 4
|
||||
}
|
||||
// brush 1
|
||||
{
|
||||
( -198 3.2304473782995284 16.88730162779191 ) ( -38 25.857864376269056 39.514718625761432 ) ( -38 3.2304473782995284 16.88730162779191 ) nail0sid [ 1 0 0 -22.5 ] [ 0 -0.707107 -0.707107 3.55635 ] -0 4 4
|
||||
( -158 3.2304473782995284 16.88730162779191 ) ( -158 116.36753236814714 -50.994949366116671 ) ( -158 25.857864376269056 39.514718625761432 ) nail0sid [ 0 -0.707107 0.707107 13.5858 ] [ 0 -0.707107 -0.707107 3.55635 ] 315 4 4
|
||||
( -198 24.443650813895957 38.100505063388333 ) ( -38 114.95331880577403 -52.409162928489764 ) ( -198 114.95331880577403 -52.409162928489764 ) nail0sid [ -1 0 0 22.5 ] [ 0 -0.707107 0.707107 13.5858 ] -0 4 4
|
||||
( -198 51.313708498984774 64.970562748477136 ) ( -38 141.82337649086287 -25.539105243400961 ) ( -38 51.313708498984774 64.970562748477136 ) nail0sid [ 1 0 0 -22.5 ] [ 0 -0.707107 0.707107 13.5858 ] -0 4 4
|
||||
( -198 6.0588745030457183 14.058874503045722 ) ( -38 28.686291501015248 36.686291501015241 ) ( -198 28.686291501015248 36.686291501015241 ) plat_top1 [ -0.988399 -0.107397 -0.107397 0.382277 ] [ 0.151882 -0.698903 -0.698903 18.8529 ] 8.73599 4 4
|
||||
( -84 3.2304473782995284 16.88730162779191 ) ( -84 116.36753236814714 -50.994949366116671 ) ( -84 93.740115370177605 -73.62236636408619 ) nail0sid [ 0 0.707107 -0.707107 -13.5858 ] [ 0 -0.707107 -0.707107 3.55635 ] 45 4 4
|
||||
}
|
||||
// brush 2
|
||||
{
|
||||
( 312 150.14213562373328 62.1421356237297 ) ( 312 147.31370849898707 64.970562748475899 ) ( -22 51.313708498986671 64.970562748474549 ) nail0sid [ 0.979965 0.140833 0.140833 -24.5 ] [ 4.16334e-16 -0.707107 0.707107 13.5858 ] -0 4 4
|
||||
( 312 123.27207793863909 35.27207793863554 ) ( 312 150.14213562373328 62.1421356237297 ) ( -22 54.142135623731519 62.1421356237297 ) plat_top1 [ -0.968596 -0.246596 0.0318029 33.9878 ] [ 0.148839 -0.677513 -0.720293 13.689 ] 8.73599 4 4
|
||||
( -20.159430628490551 24.443650813892877 38.10050506338257 ) ( -20.159430628490551 27.272077938638176 35.272077938636357 ) ( -20.159430628490551 54.142135623731519 62.1421356237297 ) nail0sid [ 0 -0.707107 0.707107 13.5858 ] [ 0 -0.707107 -0.707107 3.55635 ] 315 4 4
|
||||
( 304 123.27207793863909 35.27207793863554 ) ( 304 120.44365081389242 38.100505063383061 ) ( 304 147.31370849898707 64.970562748475899 ) nail0sid [ 0 0.707107 -0.707107 -13.5858 ] [ 0 -0.707107 -0.707107 3.55635 ] 45 4 4
|
||||
( 312 147.31370849898707 64.970562748475899 ) ( 312 120.44365081389242 38.100505063383061 ) ( -22 24.443650813892877 38.10050506338257 ) nail0sid [ 0.979965 0.140833 -0.140833 -24.5 ] [ 0.196985 -0.678798 -0.735416 4.73642 ] -0 4 4
|
||||
( -21.731518310108669 23.509558267812626 37.166412517302597 ) ( 312.2684816898913 119.50955826781217 37.166412517303087 ) ( 312.2684816898913 122.33798539255883 34.337985392555566 ) nail0sid [ -0.979965 -0.140833 -0.140833 24.5 ] [ -1.41553e-14 -0.707107 0.707107 13.5858 ] -0 4 4
|
||||
}
|
||||
// brush 3
|
||||
{
|
||||
( 211.10320981796136 13.05939320487942 -173.64935895242516 ) ( 129.00808099567936 -6.5574149764743268 141.24246041780955 ) ( 110.89245854681336 -39.229412069668228 134.28797774590251 ) plat_top1 [ 0.91948 -0.188171 -0.345179 8.0044 ] [ 0.245536 0.960576 0.130404 -18.9273 ] 335.805 4 4
|
||||
( 110.89245854681336 -39.229412069668228 134.28797774590251 ) ( 129.00808099567936 -6.5574149764743268 141.24246041780955 ) ( 132.37909023699581 -8.5823590028436172 141.9745112253749 ) nail0sid [ 0.842752 -0.506236 0.183013 -12.0446 ] [ -0.476727 -0.859789 -0.183013 1.48365 ] 300.506 4 4
|
||||
( 132.37909023699581 -8.5823590028436172 141.9745112253749 ) ( 129.00808099567936 -6.5574149764743268 141.24246041780955 ) ( 211.10320981796136 13.05939320487942 -173.64935895242516 ) nail0sid [ 0.316653 0.650252 0.690581 -21.8008 ] [ -0.749837 0.617487 -0.237603 25.0727 ] 329.462 4 4
|
||||
( 214.47421905927393 11.034449178509824 -172.91730814486081 ) ( 196.35859661040263 -21.637547914676425 -179.87179081676871 ) ( 114.26346778811788 -41.25435609604196 135.02002855346447 ) nail0sid [ 0.251819 0.0601728 -0.965902 28.4648 ] [ -0.477989 -0.8601 -0.178198 4.88401 ] 54.1333 3.91106 3.6911
|
||||
( 114.26346778811788 -41.25435609604196 135.02002855346447 ) ( 196.35859661040263 -21.637547914676425 -179.87179081676871 ) ( 192.98758736909812 -19.61260388830264 -180.60384162433067 ) nail0sid [ -0.316653 -0.650252 -0.690581 21.8008 ] [ -0.749837 0.617487 -0.237603 25.0727 ] 30.5385 4 4
|
||||
( 192.98758736909812 -19.61260388830264 -180.60384162433067 ) ( 196.35859661040263 -21.637547914676425 -179.87179081676871 ) ( 214.47421905927393 11.034449178509824 -172.91730814486081 ) nail0sid [ -0.842752 0.506236 -0.183013 12.0446 ] [ -0.476727 -0.859789 -0.183013 1.48366 ] 59.4944 4 4
|
||||
}
|
||||
// brush 4
|
||||
{
|
||||
( 158.92635901947892 -83.611196691342144 175.21577660425433 ) ( 155.28740058571424 -82.161706948558958 176.0263079132728 ) ( 43.3444236360627 1.9840000126387736 -142.03071513707602 ) nail0sid [ 0.147252 -0.174713 0.973546 7.26375 ] [ -0.90974 0.362372 0.202633 0.873325 ] 96.3589 4 4
|
||||
( 143.35625389871132 -116.38134924778888 163.9158240399278 ) ( 158.92635901947892 -83.611196691342144 175.21577660425433 ) ( 46.983382069825623 0.53451026985629824 -142.84124644609412 ) plat_top1 [ -0.306205 0.282808 -0.908988 7.56999 ] [ -0.367496 -0.915952 -0.161178 -8.80708 ] 346.049 4 4
|
||||
( 27.897613284471973 -31.436892085725191 -151.61339309906967 ) ( 31.536571718235862 -32.886381828508718 -152.42392440808825 ) ( 47.106676839003015 -0.11622927206303757 -141.12397184376204 ) nail0sid [ -0.90974 0.362372 0.202633 0.873318 ] [ -0.40974 -0.862372 -0.297367 -22.6911 ] 287.248 4 4
|
||||
( 142.82035551384905 -113.55292212304269 156.45172242479001 ) ( 139.18139708008346 -112.10343238025854 157.26225373380893 ) ( 154.75150220085197 -79.333279823812774 168.56220629813501 ) nail0sid [ 0.90974 -0.362372 -0.202633 -0.873322 ] [ -0.40974 -0.862372 -0.297367 -22.6911 ] 72.7522 4 4
|
||||
( 155.28740058571424 -82.161706948558958 176.0263079132728 ) ( 139.71729546494572 -114.9318595050047 164.72635534894667 ) ( 27.774318515294581 -30.786152543805912 -153.33066770140175 ) nail0sid [ 0.246837 -0.418643 0.873962 -1.80538 ] [ -0.360122 -0.946525 -0.12169 -16.9491 ] 22.6866 4 4
|
||||
( 27.251034754850608 -32.020274616764738 -153.47299479714667 ) ( 139.19401170450169 -116.16598157796352 164.58402825320175 ) ( 142.83297013826729 -117.61547132074767 163.77349694418282 ) nail0sid [ -0.147252 0.174713 -0.973546 -7.26375 ] [ -0.90974 0.362372 0.202633 0.873325 ] 263.641 4 4
|
||||
}
|
||||
// brush 5
|
||||
{
|
||||
( 107.63249815088147 -300.60628709576531 -11.469895128040761 ) ( -27.500634328950625 -5.8125499760672028 21.951300348155627 ) ( -56.738758286001499 -16.981764250365501 0.40180476314654356 ) plat_top1 [ 0.00806639 -0.690548 0.723242 -43.1376 ] [ 0.866573 0.365738 0.33954 60.6476 ] 330.076 4 4
|
||||
( -56.738758286001499 -16.981764250365501 0.40180476314654356 ) ( -27.500634328950625 -5.8125499760672028 21.951300348155627 ) ( -29.422937070303092 -7.0863329531279078 25.219668304455212 ) nail0sid [ -0.480576 -0.318446 0.817092 -17.8228 ] [ -0.769424 -0.293927 -0.567092 -21.5495 ] 10.312 4 4
|
||||
( -29.422937070303092 -7.0863329531279078 25.219668304455212 ) ( -27.500634328950625 -5.8125499760672028 21.951300348155627 ) ( 107.63249815088147 -300.60628709576531 -11.469895128040761 ) nail0sid [ 0.383748 0.716006 0.583157 12.2093 ] [ 0.609899 0.277651 -0.742249 0.156697 ] 12.3613 4 4
|
||||
( 105.71019540952881 -301.88007007282556 -8.2015271717451554 ) ( 76.472071452485082 -313.04928434711996 -29.751022756758928 ) ( -58.66106102735769 -18.255547227426405 3.6701727194335376 ) nail0sid [ 0.414509 -0.904253 -0.102516 3.93427 ] [ -0.771501 -0.28942 -0.566588 -19.9474 ] 63.7844 3.91106 3.6911
|
||||
( -58.66106102735769 -18.255547227426405 3.6701727194335376 ) ( 76.472071452485082 -313.04928434711996 -29.751022756758928 ) ( 78.39437419384133 -311.77550137005898 -33.019390713045922 ) nail0sid [ -0.383748 -0.716006 -0.583157 -12.2093 ] [ 0.609899 0.277651 -0.742249 0.156696 ] 347.639 4 4
|
||||
( 78.39437419384133 -311.77550137005898 -33.019390713045922 ) ( 76.472071452485082 -313.04928434711996 -29.751022756758928 ) ( 105.71019540952881 -301.88007007282556 -8.2015271717451554 ) nail0sid [ 0.480576 0.318446 -0.817092 17.8228 ] [ -0.769424 -0.293927 -0.567092 -21.5495 ] 349.688 4 4
|
||||
}
|
||||
// brush 6
|
||||
{
|
||||
( 12 150.14213562372692 62.142135623730631 ) ( -318 150.14213562372697 62.14213562373061 ) ( -190 123.27207793864181 35.272077938643633 ) plat_top1 [ -0.988399 -0.107397 -0.107397 -4.94739 ] [ 0.151882 -0.698903 -0.698903 36.8417 ] 8.73599 4 4
|
||||
( 140 120.44365081389603 38.100505063385761 ) ( 12 147.31370849898212 64.970562748475487 ) ( 12 150.14213562372692 62.142135623730631 ) nail0sid [ -3.21965e-15 0.707107 -0.707107 -30.5564 ] [ 0.958647 -0.201241 -0.201241 20.5269 ] 45 4 4
|
||||
( -190 123.27207793864181 35.272077938643633 ) ( -190 120.44365081389697 38.100505063386663 ) ( 140 120.44365081389603 38.100505063385761 ) nail0sid [ -1 4.01478e-16 4.67348e-13 14.5 ] [ 3.30319e-13 -0.707107 0.707107 30.5564 ] -0 4 4
|
||||
( 12 147.31370849898212 64.970562748475487 ) ( -318 147.31370849898215 64.970562748475473 ) ( -318 150.14213562372697 62.14213562373061 ) nail0sid [ 1 -3.89195e-16 2.27596e-15 -14.5 ] [ -1.7486e-15 -0.707107 0.707107 30.5564 ] -0 4 4
|
||||
( -318 150.14213562372697 62.14213562373061 ) ( -318 147.31370849898215 64.970562748475473 ) ( -190 120.44365081389697 38.100505063386663 ) nail0sid [ 9.29812e-15 -0.707107 0.707107 30.5564 ] [ 0.958647 -0.201241 -0.201241 20.5269 ] 315 4 4
|
||||
( -190 120.44365081389697 38.100505063386663 ) ( -318 147.31370849898215 64.970562748475473 ) ( 12 147.31370849898212 64.970562748475487 ) nail0sid [ 1 2.4885 2.4885 -113.134 ] [ 8.32667e-17 -0.707107 -0.707107 20.5269 ] -0 4 4
|
||||
}
|
||||
// brush 7
|
||||
{
|
||||
( -324 54.1421356237297 222.14213562373152 ) ( -652 54.142135623728713 62.142135623728869 ) ( -652 27.272077938638475 35.272077938636748 ) plat_top1 [ -0.98824 -0.119904 -0.0948894 -58.9216 ] [ 0.151857 -0.696981 -0.700825 27.9658 ] 8.73599 4 4
|
||||
( -652 27.272077938638475 35.272077938636748 ) ( -652 54.142135623728713 62.142135623728869 ) ( -652 51.313708498984852 64.97056274847273 ) nail0sid [ 0 -0.707107 0.707107 13.5858 ] [ 0 -0.707107 -0.707107 3.55635 ] 315 4 4
|
||||
( -652 51.313708498984852 64.97056274847273 ) ( -652 54.142135623728713 62.142135623728869 ) ( -324 54.1421356237297 222.14213562373152 ) nail0sid [ 0.99984 -0.012654 -0.012654 5.5 ] [ 0 -0.707107 0.707107 13.5858 ] -0 4 4
|
||||
( -324 51.313708498987019 224.97056274847421 ) ( -324 24.443650813894013 198.10050506338121 ) ( -652 24.443650813894017 38.100505063381206 ) nail0sid [ 0.99984 0.012654 -0.012654 5.5 ] [ 0.353869 -0.702628 -0.711585 61.2523 ] -0 4 4
|
||||
( -652 24.443650813894017 38.100505063381206 ) ( -324 24.443650813894013 198.10050506338121 ) ( -324 27.272077938638176 195.27207793863636 ) nail0sid [ -0.99984 0.012654 0.012654 -5.5 ] [ -2.16147e-15 -0.707107 0.707107 13.5858 ] -0 4 4
|
||||
( -324 27.272077938638176 195.27207793863636 ) ( -324 24.443650813894013 198.10050506338121 ) ( -324 51.313708498987019 224.97056274847421 ) nail0sid [ 0 0.707107 -0.707107 -13.5858 ] [ 0 -0.707107 -0.707107 3.55635 ] 45 4 4
|
||||
}
|
||||
// brush 8
|
||||
{
|
||||
( -198 291.23044737829952 16.88730162779191 ) ( -38 313.85786437626905 39.514718625761432 ) ( -38 291.23044737829952 16.88730162779191 ) nail0sid [ 1 0 0 -22.5 ] [ 0 -0.707107 -0.707107 22.468 ] -0 4 4
|
||||
( -158 291.23044737829952 16.88730162779191 ) ( -158 404.36753236814712 -50.994949366116671 ) ( -158 313.85786437626905 39.514718625761432 ) nail0sid [ 0 -0.707107 0.707107 0.497475 ] [ 0 -0.707107 -0.707107 22.468 ] 315 4 4
|
||||
( -198 312.44365081389594 38.100505063388333 ) ( -38 402.95331880577402 -52.409162928489764 ) ( -198 402.95331880577402 -52.409162928489764 ) nail0sid [ -1 0 0 22.5 ] [ 0 -0.707107 0.707107 0.497475 ] -0 4 4
|
||||
( -198 704 429.65685424949334 ) ( -38 794.50966799187734 339.14718625761384 ) ( -38 704 429.65685424949334 ) nail0sid [ 1 0 0 -22.5 ] [ 0 -0.707107 0.707107 0.497475 ] -0 4 4
|
||||
( -198 294.05887450304573 14.058874503045722 ) ( -38 316.68629150101526 36.686291501015241 ) ( -198 316.68629150101526 36.686291501015241 ) plat_top1 [ -0.988399 -0.107397 -0.107397 8.11483 ] [ 0.151882 -0.698903 -0.698903 5.174 ] 8.73599 4 4
|
||||
( -84 291.23044737829952 16.88730162779191 ) ( -84 404.36753236814712 -50.994949366116671 ) ( -84 381.74011537017759 -73.62236636408619 ) nail0sid [ 0 0.707107 -0.707107 -0.497475 ] [ 0 -0.707107 -0.707107 22.468 ] 45 4 4
|
||||
}
|
||||
// brush 9
|
||||
{
|
||||
( 268 704 429.65685424949152 ) ( 194 704 429.6568542494922 ) ( 194 706.82842712474667 426.82842712475201 ) nail0sid [ -0.333333 0.666667 0.666667 -22.5 ] [ -1.43302e-13 0.707107 -0.707107 0.497475 ] -0 4 4
|
||||
( 194 706.82842712474667 426.82842712475201 ) ( 194 704 429.6568542494922 ) ( -30 312.44365081390578 38.100505063377973 ) nail0sid [ 3.97817e-13 -0.707107 0.707107 0.497475 ] [ -0.374999 -0.655506 -0.655506 22.468 ] 315 4 4
|
||||
( -30 312.44365081390578 38.100505063377973 ) ( 194 704 429.6568542494922 ) ( 268 704 429.65685424949152 ) nail0sid [ -0.333333 0.760839 -0.572494 -138.457 ] [ 1.27182e-14 0.707107 0.707107 23.4041 ] -0 -1.2517 -4
|
||||
( -30 315.27207793863823 35.272077938640045 ) ( 44 315.27207793863818 35.272077938639995 ) ( 268 706.82842712474633 426.82842712475167 ) plat_top1 [ 0.329466 -0.551536 0.766329 39.7436 ] [ -0.0506272 0.800158 0.597649 0.313782 ] 8.73599 4 4
|
||||
( 268 706.82842712474633 426.82842712475167 ) ( 44 315.27207793863818 35.272077938639995 ) ( 44 312.44365081390606 38.100505063377568 ) nail0sid [ 3.25542e-13 0.707107 -0.707107 -0.497475 ] [ -0.374999 -0.655506 -0.655506 22.468 ] 45 4 4
|
||||
( 44 312.44365081390606 38.100505063377568 ) ( 44 315.27207793863818 35.272077938639995 ) ( -30 315.27207793863823 35.272077938640045 ) nail0sid [ 0.333333 -0.666667 -0.666667 22.5 ] [ 7.66137e-13 0.707107 -0.707107 0.497475 ] -0 4 4
|
||||
}
|
||||
// brush 10
|
||||
{
|
||||
( -310 291.23044737829952 16.88730162779191 ) ( -150 313.85786437626905 39.514718625761432 ) ( -150 291.23044737829952 16.88730162779191 ) nail0sid [ -4.37114e-08 -0.707107 -0.707107 -5.55821 ] [ -1 3.09086e-08 3.09086e-08 10.5469 ] 90 3.6697 3.15963
|
||||
( -270 291.23044737829952 16.88730162779191 ) ( -270 404.36753236814712 -50.994949366116671 ) ( -270 313.85786437626905 39.514718625761432 ) nail0sid [ 0 -0.707107 0.707107 0.497475 ] [ 0 -0.707107 -0.707107 22.468 ] 315 4 4
|
||||
( -310 312.44365081389594 38.100505063388333 ) ( -150 402.95331880577402 -52.409162928489764 ) ( -310 402.95331880577402 -52.409162928489764 ) nail0sid [ -1 0 0 -5.5 ] [ 0 -0.707107 0.707107 0.497475 ] -0 4 4
|
||||
( -310 704 429.65685424949334 ) ( -150 794.50966799187734 339.14718625761384 ) ( -150 704 429.65685424949334 ) nail0sid [ 1 0 0 5.5 ] [ 0 -0.707107 0.707107 0.497475 ] -0 4 4
|
||||
( -310 294.05887450304573 14.058874503045722 ) ( -150 316.68629150101526 36.686291501015241 ) ( -310 316.68629150101526 36.686291501015241 ) plat_top1 [ -0.988399 -0.107397 -0.107397 -19.5603 ] [ 0.151882 -0.698903 -0.698903 9.4267 ] 8.73599 4 4
|
||||
( -196 291.23044737829952 16.88730162779191 ) ( -196 404.36753236814712 -50.994949366116671 ) ( -196 381.74011537017759 -73.62236636408619 ) nail0sid [ 0 0.707107 -0.707107 -0.497475 ] [ 0 -0.707107 -0.707107 22.468 ] 45 4 4
|
||||
}
|
||||
// brush 11
|
||||
{
|
||||
( -478 704 429.65685424949152 ) ( -366 312.44365081389145 38.100505063377526 ) ( -366 315.27207793863636 35.272077938634531 ) nail0sid [ 0 -0.707107 0.707107 0.497475 ] [ 0 -0.707107 -0.707107 22.468 ] 315 4 4
|
||||
( -404 704 429.65685424948953 ) ( -478 704 429.65685424949152 ) ( -478 706.82842712474485 426.82842712474661 ) nail0sid [ 1 -4.54668e-16 -2.43083e-13 29.5 ] [ 1.71613e-13 -0.707107 0.707107 0.497475 ] -0 4 4
|
||||
( -478 706.82842712474485 426.82842712474661 ) ( -366 315.27207793863636 35.272077938634531 ) ( -292 315.27207793863636 35.272077938634538 ) plat_top1 [ 0.329466 -0.551536 0.766329 -43.2819 ] [ -0.0506272 0.800158 0.597649 13.0719 ] 8.73599 4 4
|
||||
( -404 704 429.65685424948953 ) ( -292 312.44365081389145 38.100505063377533 ) ( -366 312.44365081389145 38.100505063377526 ) nail0sid [ -4.37114e-08 -0.707107 -0.707107 -5.5582 ] [ -1 -0.149931 -0.149931 -3.20239 ] 90 3.6697 3.15963
|
||||
( -366 312.44365081389145 38.100505063377526 ) ( -292 312.44365081389145 38.100505063377533 ) ( -292 315.27207793863636 35.272077938634538 ) nail0sid [ -1 6.08858e-16 4.22995e-13 -29.5 ] [ 2.98983e-13 -0.707107 0.707107 0.497475 ] -0 4 4
|
||||
( -292 315.27207793863636 35.272077938634538 ) ( -292 312.44365081389145 38.100505063377533 ) ( -404 704 429.65685424948953 ) nail0sid [ 0 0.707107 -0.707107 -0.497475 ] [ 0 -0.707107 -0.707107 22.468 ] 45 4 4
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue