123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- /*
- * Copyright 2011-2013 Blender Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include "render/camera.h"
- #include "render/mesh.h"
- #include "subd/subd_dice.h"
- #include "subd/subd_patch.h"
- CCL_NAMESPACE_BEGIN
- /* EdgeDice Base */
- EdgeDice::EdgeDice(const SubdParams ¶ms_) : params(params_)
- {
- mesh_P = NULL;
- mesh_N = NULL;
- vert_offset = 0;
- params.mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
- if (params.ptex) {
- params.mesh->attributes.add(ATTR_STD_PTEX_UV);
- params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
- }
- }
- void EdgeDice::reserve(int num_verts)
- {
- Mesh *mesh = params.mesh;
- vert_offset = mesh->verts.size();
- tri_offset = mesh->num_triangles();
- /* todo: optimize so we can reserve in advance, this is like push_back_slow() */
- if (vert_offset + num_verts > mesh->verts.capacity()) {
- mesh->reserve_mesh(size_t((vert_offset + num_verts) * 1.2), mesh->num_triangles());
- }
- mesh->resize_mesh(vert_offset + num_verts, tri_offset);
- Attribute *attr_vN = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
- mesh_P = mesh->verts.data();
- mesh_N = attr_vN->data_float3();
- }
- int EdgeDice::add_vert(Patch *patch, float2 uv)
- {
- float3 P, N;
- patch->eval(&P, NULL, NULL, &N, uv.x, uv.y);
- assert(vert_offset < params.mesh->verts.size());
- mesh_P[vert_offset] = P;
- mesh_N[vert_offset] = N;
- params.mesh->vert_patch_uv[vert_offset] = make_float2(uv.x, uv.y);
- if (params.ptex) {
- Attribute *attr_ptex_uv = params.mesh->attributes.add(ATTR_STD_PTEX_UV);
- params.mesh->attributes.resize();
- float3 *ptex_uv = attr_ptex_uv->data_float3();
- ptex_uv[vert_offset] = make_float3(uv.x, uv.y, 0.0f);
- }
- params.mesh->num_subd_verts++;
- return vert_offset++;
- }
- void EdgeDice::add_triangle(Patch *patch, int v0, int v1, int v2)
- {
- Mesh *mesh = params.mesh;
- /* todo: optimize so we can reserve in advance, this is like push_back_slow() */
- if (mesh->triangles.size() == mesh->triangles.capacity())
- mesh->reserve_mesh(mesh->verts.size(), size_t(max(mesh->num_triangles() + 1, 1) * 1.2));
- mesh->add_triangle(v0, v1, v2, patch->shader, true);
- params.mesh->triangle_patch[params.mesh->num_triangles() - 1] = patch->patch_index;
- if (params.ptex) {
- Attribute *attr_ptex_face_id = params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
- params.mesh->attributes.resize();
- float *ptex_face_id = attr_ptex_face_id->data_float();
- ptex_face_id[tri_offset] = (float)patch->ptex_face_id();
- }
- tri_offset++;
- }
- void EdgeDice::stitch_triangles(Patch *patch, vector<int> &outer, vector<int> &inner)
- {
- if (inner.size() == 0 || outer.size() == 0)
- return; // XXX avoid crashes for Mu or Mv == 1, missing polygons
- /* stitch together two arrays of verts with triangles. at each step,
- * we compare using the next verts on both sides, to find the split
- * direction with the smallest diagonal, and use that in order to keep
- * the triangle shape reasonable. */
- for (size_t i = 0, j = 0; i + 1 < inner.size() || j + 1 < outer.size();) {
- int v0, v1, v2;
- v0 = inner[i];
- v1 = outer[j];
- if (j + 1 == outer.size()) {
- v2 = inner[++i];
- }
- else if (i + 1 == inner.size()) {
- v2 = outer[++j];
- }
- else {
- /* length of diagonals */
- float len1 = len_squared(mesh_P[inner[i]] - mesh_P[outer[j + 1]]);
- float len2 = len_squared(mesh_P[outer[j]] - mesh_P[inner[i + 1]]);
- /* use smallest diagonal */
- if (len1 < len2)
- v2 = outer[++j];
- else
- v2 = inner[++i];
- }
- add_triangle(patch, v0, v1, v2);
- }
- }
- /* QuadDice */
- QuadDice::QuadDice(const SubdParams ¶ms_) : EdgeDice(params_)
- {
- }
- void QuadDice::reserve(EdgeFactors &ef, int Mu, int Mv)
- {
- /* XXX need to make this also work for edge factor 0 and 1 */
- int num_verts = (ef.tu0 + ef.tu1 + ef.tv0 + ef.tv1) + (Mu - 1) * (Mv - 1);
- EdgeDice::reserve(num_verts);
- }
- float2 QuadDice::map_uv(SubPatch &sub, float u, float v)
- {
- /* map UV from subpatch to patch parametric coordinates */
- float2 d0 = interp(sub.P00, sub.P01, v);
- float2 d1 = interp(sub.P10, sub.P11, v);
- return interp(d0, d1, u);
- }
- float3 QuadDice::eval_projected(SubPatch &sub, float u, float v)
- {
- float2 uv = map_uv(sub, u, v);
- float3 P;
- sub.patch->eval(&P, NULL, NULL, NULL, uv.x, uv.y);
- if (params.camera)
- P = transform_perspective(¶ms.camera->worldtoraster, P);
- return P;
- }
- int QuadDice::add_vert(SubPatch &sub, float u, float v)
- {
- return EdgeDice::add_vert(sub.patch, map_uv(sub, u, v));
- }
- void QuadDice::add_side_u(SubPatch &sub,
- vector<int> &outer,
- vector<int> &inner,
- int Mu,
- int Mv,
- int tu,
- int side,
- int offset)
- {
- outer.clear();
- inner.clear();
- /* set verts on the edge of the patch */
- outer.push_back(offset + ((side) ? 2 : 0));
- for (int i = 1; i < tu; i++) {
- float u = i / (float)tu;
- float v = (side) ? 1.0f : 0.0f;
- outer.push_back(add_vert(sub, u, v));
- }
- outer.push_back(offset + ((side) ? 3 : 1));
- /* set verts on the edge of the inner grid */
- for (int i = 0; i < Mu - 1; i++) {
- int j = (side) ? Mv - 1 - 1 : 0;
- inner.push_back(offset + 4 + i + j * (Mu - 1));
- }
- }
- void QuadDice::add_side_v(SubPatch &sub,
- vector<int> &outer,
- vector<int> &inner,
- int Mu,
- int Mv,
- int tv,
- int side,
- int offset)
- {
- outer.clear();
- inner.clear();
- /* set verts on the edge of the patch */
- outer.push_back(offset + ((side) ? 1 : 0));
- for (int j = 1; j < tv; j++) {
- float u = (side) ? 1.0f : 0.0f;
- float v = j / (float)tv;
- outer.push_back(add_vert(sub, u, v));
- }
- outer.push_back(offset + ((side) ? 3 : 2));
- /* set verts on the edge of the inner grid */
- for (int j = 0; j < Mv - 1; j++) {
- int i = (side) ? Mu - 1 - 1 : 0;
- inner.push_back(offset + 4 + i + j * (Mu - 1));
- }
- }
- float QuadDice::quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d)
- {
- return triangle_area(a, b, d) + triangle_area(a, d, c);
- }
- float QuadDice::scale_factor(SubPatch &sub, EdgeFactors &ef, int Mu, int Mv)
- {
- /* estimate area as 4x largest of 4 quads */
- float3 P[3][3];
- for (int i = 0; i < 3; i++)
- for (int j = 0; j < 3; j++)
- P[i][j] = eval_projected(sub, i * 0.5f, j * 0.5f);
- float A1 = quad_area(P[0][0], P[1][0], P[0][1], P[1][1]);
- float A2 = quad_area(P[1][0], P[2][0], P[1][1], P[2][1]);
- float A3 = quad_area(P[0][1], P[1][1], P[0][2], P[1][2]);
- float A4 = quad_area(P[1][1], P[2][1], P[1][2], P[2][2]);
- float Apatch = max(A1, max(A2, max(A3, A4))) * 4.0f;
- /* solve for scaling factor */
- float Atri = params.dicing_rate * params.dicing_rate * 0.5f;
- float Ntris = Apatch / Atri;
- // XXX does the -sqrt solution matter
- // XXX max(D, 0.0) is highly suspicious, need to test cases
- // where D goes negative
- float N = 0.5f * (Ntris - (ef.tu0 + ef.tu1 + ef.tv0 + ef.tv1));
- float D = 4.0f * N * Mu * Mv + (Mu + Mv) * (Mu + Mv);
- float S = (Mu + Mv + sqrtf(max(D, 0.0f))) / (2 * Mu * Mv);
- return S;
- }
- void QuadDice::add_corners(SubPatch &sub)
- {
- /* add verts for patch corners */
- add_vert(sub, 0.0f, 0.0f);
- add_vert(sub, 1.0f, 0.0f);
- add_vert(sub, 0.0f, 1.0f);
- add_vert(sub, 1.0f, 1.0f);
- }
- void QuadDice::add_grid(SubPatch &sub, int Mu, int Mv, int offset)
- {
- /* create inner grid */
- float du = 1.0f / (float)Mu;
- float dv = 1.0f / (float)Mv;
- for (int j = 1; j < Mv; j++) {
- for (int i = 1; i < Mu; i++) {
- float u = i * du;
- float v = j * dv;
- add_vert(sub, u, v);
- if (i < Mu - 1 && j < Mv - 1) {
- int i1 = offset + 4 + (i - 1) + (j - 1) * (Mu - 1);
- int i2 = offset + 4 + i + (j - 1) * (Mu - 1);
- int i3 = offset + 4 + i + j * (Mu - 1);
- int i4 = offset + 4 + (i - 1) + j * (Mu - 1);
- add_triangle(sub.patch, i1, i2, i3);
- add_triangle(sub.patch, i1, i3, i4);
- }
- }
- }
- }
- void QuadDice::dice(SubPatch &sub, EdgeFactors &ef)
- {
- /* compute inner grid size with scale factor */
- int Mu = max(ef.tu0, ef.tu1);
- int Mv = max(ef.tv0, ef.tv1);
- #if 0 /* Doesn't work very well, especially at grazing angles. */
- float S = scale_factor(sub, ef, Mu, Mv);
- #else
- float S = 1.0f;
- #endif
- Mu = max((int)ceil(S * Mu), 2); // XXX handle 0 & 1?
- Mv = max((int)ceil(S * Mv), 2); // XXX handle 0 & 1?
- /* reserve space for new verts */
- int offset = params.mesh->verts.size();
- reserve(ef, Mu, Mv);
- /* corners and inner grid */
- add_corners(sub);
- add_grid(sub, Mu, Mv, offset);
- /* bottom side */
- vector<int> outer, inner;
- add_side_u(sub, outer, inner, Mu, Mv, ef.tu0, 0, offset);
- stitch_triangles(sub.patch, outer, inner);
- /* top side */
- add_side_u(sub, outer, inner, Mu, Mv, ef.tu1, 1, offset);
- stitch_triangles(sub.patch, inner, outer);
- /* left side */
- add_side_v(sub, outer, inner, Mu, Mv, ef.tv0, 0, offset);
- stitch_triangles(sub.patch, inner, outer);
- /* right side */
- add_side_v(sub, outer, inner, Mu, Mv, ef.tv1, 1, offset);
- stitch_triangles(sub.patch, outer, inner);
- assert(vert_offset == params.mesh->verts.size());
- }
- CCL_NAMESPACE_END
|