void TerrainNode::update(ptr<SceneNode> owner) { deformedCameraPos = owner->getLocalToCamera().inverse() * vec3d::ZERO; SceneManager::getFrustumPlanes(owner->getLocalToScreen(), deformedFrustumPlanes); localCameraPos = deform->deformedToLocal(deformedCameraPos); mat4d m = deform->localToDeformedDifferential(localCameraPos, true); distFactor = max(vec3d(m[0][0], m[1][0], m[2][0]).length(), vec3d(m[0][1], m[1][1], m[2][1]).length()); ptr<FrameBuffer> fb = SceneManager::getCurrentFrameBuffer(); vec3d left = deformedFrustumPlanes[0].xyz().normalize(); vec3d right = deformedFrustumPlanes[1].xyz().normalize(); float fov = (float) safe_acos(-left.dotproduct(right)); splitDist = splitFactor * fb->getViewport().z / 1024.0f * tan(40.0f / 180.0f * M_PI) / tan(fov / 2.0f); if (splitDist < 1.1f || !(isFinite(splitDist))) { splitDist = 1.1f; } // initializes data structures for horizon occlusion culling if (horizonCulling && localCameraPos.z <= root->zmax) { vec3d deformedDir = owner->getLocalToCamera().inverse() * vec3d::UNIT_Z; vec2d localDir = (deform->deformedToLocal(deformedDir) - localCameraPos).xy().normalize(); localCameraDir = mat2f(localDir.y, -localDir.x, -localDir.x, -localDir.y); for (int i = 0; i < HORIZON_SIZE; ++i) { horizon[i] = -INFINITY; } } root->update(); }
static MT_Vector3 MatrixToAxisAngle(const MT_Matrix3x3& R) { MT_Vector3 delta = MT_Vector3(R[2][1] - R[1][2], R[0][2] - R[2][0], R[1][0] - R[0][1]); MT_Scalar c = safe_acos((R[0][0] + R[1][1] + R[2][2] - 1)/2); MT_Scalar l = delta.length(); if (!MT_fuzzyZero(l)) delta *= c/l; return delta; }
double PlanetViewController::interpolate(double sx0, double sy0, double stheta, double sphi, double sd, double dx0, double dy0, double dtheta, double dphi, double dd, double t) { vec3d s = vec3d(cos(sx0) * cos(sy0), sin(sx0) * cos(sy0), sin(sy0)); vec3d e = vec3d(cos(dx0) * cos(dy0), sin(dx0) * cos(dy0), sin(dy0)); double dist = max(safe_acos(s.dotproduct(e)) * R, 1e-3); t = min(t + min(0.1, 5000.0 / dist), 1.0); double T = 0.5 * atan(4.0 * (t - 0.5)) / atan(4.0 * 0.5) + 0.5; interpolateDirection(sx0, sy0, dx0, dy0, T, x0, y0); interpolateDirection(sphi, stheta, dphi, dtheta, T, phi, theta); const double W = 10.0; d = sd * (1.0 - t) + dd * t + dist * (exp(-W * (t - 0.5) * (t - 0.5)) - exp(-W * 0.25)); return t; }
void EditorHandler::redisplay(double t, double dt) { if (editors.empty()) { return; } if (update) { for (unsigned int i = 0; i < editors.size(); ++i) { if (editors[i]->isActive()) { editors[i]->update(); } } update = false; } // mouse in world space vec3d p = getPosition(lastPos.x, lastPos.y); // camera in local space, altitude in local space float altitude = -1.0f; for (unsigned int i = 0; i < editors.size(); ++i) { if (editors[i]->isActive()) { mat4d screenToLocal = editors[i]->getTerrain()->getLocalToScreen().inverse(); vec4d c = screenToLocal * vec4d(0.0, 0.0, 1.0, 0.0); double cx = c.x / c.w; double cy = c.y / c.w; double cz = c.z / c.w; if (isFinite(cx) && isFinite(cy)) { vec3d dv = editors[i]->getTerrainNode()->deform->deformedToLocal(vec3d(cx, cy, cz)); if (isFinite(dv.z)) { altitude = dv.z; break; } } } } // field of view angle vec4d frustum[6]; ptr<SceneManager> manager = editors[0]->getTerrain()->getOwner(); SceneManager::getFrustumPlanes(manager->getCameraToScreen(), frustum); vec3d left = frustum[0].xyz().normalize(); vec3d right = frustum[1].xyz().normalize(); float fov = (float) safe_acos(-left.dotproduct(right)); // pencil radius radius = altitude * tan(fov / 2.0) * relativeRadius; for (unsigned int i = 0; i < editors.size(); ++i) { if (editors[i]->isActive()) { editors[i]->setPencil(vec4f(p.x, p.y, p.z, radius), vec4f(brushColor[0], brushColor[1], brushColor[2], brushColor[3]), paint); } } if (newStrokes > 0) { for (unsigned int i = 0; i < editors.size(); ++i) { if (editors[i]->isActive()) { editors[i]->edit(strokes); } } } newStrokes = 0; }
static float angle(const MT_Vector3& v1, const MT_Vector3& v2) { return safe_acos(v1.dot(v2)); }
bool TextureSystemImpl::environment(TextureHandle* texture_handle_, Perthread* thread_info_, TextureOpt& options, const Imath::V3f& _R, const Imath::V3f& _dRdx, const Imath::V3f& _dRdy, int nchannels, float* result, float* dresultds, float* dresultdt) { // Handle >4 channel lookups by recursion. if (nchannels > 4) { int save_firstchannel = options.firstchannel; while (nchannels) { int n = std::min(nchannels, 4); bool ok = environment(texture_handle_, thread_info_, options, _R, _dRdx, _dRdy, n, result, dresultds, dresultdt); if (!ok) return false; result += n; if (dresultds) dresultds += n; if (dresultdt) dresultdt += n; options.firstchannel += n; nchannels -= n; } options.firstchannel = save_firstchannel; // restore what we changed return true; } PerThreadInfo* thread_info = m_imagecache->get_perthread_info( (PerThreadInfo*)thread_info_); TextureFile* texturefile = verify_texturefile((TextureFile*)texture_handle_, thread_info); ImageCacheStatistics& stats(thread_info->m_stats); ++stats.environment_batches; ++stats.environment_queries; if (!texturefile || texturefile->broken()) return missing_texture(options, nchannels, result, dresultds, dresultdt); const ImageSpec& spec(texturefile->spec(options.subimage, 0)); // Environment maps dictate particular wrap modes options.swrap = texturefile->m_sample_border ? TextureOpt::WrapPeriodicSharedBorder : TextureOpt::WrapPeriodic; options.twrap = TextureOpt::WrapClamp; options.envlayout = LayoutLatLong; int actualchannels = Imath::clamp(spec.nchannels - options.firstchannel, 0, nchannels); // Initialize results to 0. We'll add from here on as we sample. for (int c = 0; c < nchannels; ++c) result[c] = 0; if (dresultds) { for (int c = 0; c < nchannels; ++c) dresultds[c] = 0; for (int c = 0; c < nchannels; ++c) dresultdt[c] = 0; } // If the user only provided us with one pointer, clear both to simplify // the rest of the code, but only after we zero out the data for them so // they know something went wrong. if (!(dresultds && dresultdt)) dresultds = dresultdt = NULL; // Calculate unit-length vectors in the direction of R, R+dRdx, R+dRdy. // These define the ellipse we're filtering over. Imath::V3f R = _R; R.normalize(); // center Imath::V3f Rx = _R + _dRdx; Rx.normalize(); // x axis of the ellipse Imath::V3f Ry = _R + _dRdy; Ry.normalize(); // y axis of the ellipse // angles formed by the ellipse axes. float xfilt_noblur = std::max(safe_acos(R.dot(Rx)), 1e-8f); float yfilt_noblur = std::max(safe_acos(R.dot(Ry)), 1e-8f); int naturalres = int((float)M_PI / std::min(xfilt_noblur, yfilt_noblur)); // FIXME -- figure naturalres sepearately for s and t // FIXME -- ick, why is it x and y at all, shouldn't it be s and t? // N.B. naturalres formulated for latlong // Account for width and blur float xfilt = xfilt_noblur * options.swidth + options.sblur; float yfilt = yfilt_noblur * options.twidth + options.tblur; // Figure out major versus minor, and aspect ratio Imath::V3f Rmajor; // major axis float majorlength, minorlength; bool x_is_majoraxis = (xfilt >= yfilt); if (x_is_majoraxis) { Rmajor = Rx; majorlength = xfilt; minorlength = yfilt; } else { Rmajor = Ry; majorlength = yfilt; minorlength = xfilt; } sampler_prototype sampler; long long* probecount; switch (options.interpmode) { case TextureOpt::InterpClosest: sampler = &TextureSystemImpl::sample_closest; probecount = &stats.closest_interps; break; case TextureOpt::InterpBilinear: sampler = &TextureSystemImpl::sample_bilinear; probecount = &stats.bilinear_interps; break; case TextureOpt::InterpBicubic: sampler = &TextureSystemImpl::sample_bicubic; probecount = &stats.cubic_interps; break; default: sampler = &TextureSystemImpl::sample_bilinear; probecount = &stats.bilinear_interps; break; } TextureOpt::MipMode mipmode = options.mipmode; bool aniso = (mipmode == TextureOpt::MipModeDefault || mipmode == TextureOpt::MipModeAniso); float aspect, trueaspect, filtwidth; int nsamples; float invsamples; if (aniso) { aspect = anisotropic_aspect(majorlength, minorlength, options, trueaspect); filtwidth = minorlength; if (trueaspect > stats.max_aniso) stats.max_aniso = trueaspect; nsamples = std::max(1, (int)ceilf(aspect - 0.25f)); invsamples = 1.0f / nsamples; } else { filtwidth = options.conservative_filter ? majorlength : minorlength; nsamples = 1; invsamples = 1.0f; } ImageCacheFile::SubimageInfo& subinfo( texturefile->subimageinfo(options.subimage)); int min_mip_level = subinfo.min_mip_level; // FIXME -- assuming latlong bool ok = true; float pos = -0.5f + 0.5f * invsamples; for (int sample = 0; sample < nsamples; ++sample, pos += invsamples) { Imath::V3f Rsamp = R + pos * Rmajor; float s, t; vector_to_latlong(Rsamp, texturefile->m_y_up, s, t); // Determine the MIP-map level(s) we need: we will blend // data(miplevel[0]) * (1-levelblend) + data(miplevel[1]) * levelblend int miplevel[2] = { -1, -1 }; float levelblend = 0; int nmiplevels = (int)subinfo.levels.size(); for (int m = min_mip_level; m < nmiplevels; ++m) { // Compute the filter size in raster space at this MIP level. // Filters are in radians, and the vertical resolution of a // latlong map is PI radians. So to compute the raster size of // our filter width... float filtwidth_ras = subinfo.spec(m).full_height * filtwidth * M_1_PI; // Once the filter width is smaller than one texel at this level, // we've gone too far, so we know that we want to interpolate the // previous level and the current level. Note that filtwidth_ras // is expected to be >= 0.5, or would have stopped one level ago. if (filtwidth_ras <= 1) { miplevel[0] = m - 1; miplevel[1] = m; levelblend = Imath::clamp(2.0f * filtwidth_ras - 1.0f, 0.0f, 1.0f); break; } } if (miplevel[1] < 0) { // We'd like to blur even more, but make due with the coarsest // MIP level. miplevel[0] = nmiplevels - 1; miplevel[1] = miplevel[0]; levelblend = 0; } else if (miplevel[0] < min_mip_level) { // We wish we had even more resolution than the finest MIP level, // but tough for us. miplevel[0] = min_mip_level; miplevel[1] = min_mip_level; levelblend = 0; } if (options.mipmode == TextureOpt::MipModeOneLevel) { // Force use of just one mipmap level miplevel[1] = miplevel[0]; levelblend = 0; } else if (mipmode == TextureOpt::MipModeNoMIP) { // Just sample from lowest level miplevel[0] = min_mip_level; miplevel[1] = min_mip_level; levelblend = 0; } float levelweight[2] = { 1.0f - levelblend, levelblend }; int npointson = 0; for (int level = 0; level < 2; ++level) { if (!levelweight[level]) continue; ++npointson; int lev = miplevel[level]; if (options.interpmode == TextureOpt::InterpSmartBicubic) { if (lev == 0 || (texturefile->spec(options.subimage, lev).full_height < naturalres / 2)) { sampler = &TextureSystemImpl::sample_bicubic; ++stats.cubic_interps; } else { sampler = &TextureSystemImpl::sample_bilinear; ++stats.bilinear_interps; } } else { *probecount += 1; } OIIO_SIMD4_ALIGN float sval[4] = { s, 0.0f, 0.0f, 0.0f }; OIIO_SIMD4_ALIGN float tval[4] = { t, 0.0f, 0.0f, 0.0f }; OIIO_SIMD4_ALIGN float weight[4] = { levelweight[level] * invsamples, 0.0f, 0.0f, 0.0f }; vfloat4 r, drds, drdt; ok &= (this->*sampler)(1, sval, tval, miplevel[level], *texturefile, thread_info, options, nchannels, actualchannels, weight, &r, dresultds ? &drds : NULL, dresultds ? &drdt : NULL); for (int c = 0; c < nchannels; ++c) result[c] += r[c]; if (dresultds) { for (int c = 0; c < nchannels; ++c) { dresultds[c] += drds[c]; dresultdt[c] += drdt[c]; } } } } stats.aniso_probes += nsamples; ++stats.aniso_queries; if (actualchannels < nchannels && options.firstchannel == 0 && m_gray_to_rgb) fill_gray_channels(spec, nchannels, result, dresultds, dresultdt); return ok; }
bool DrawOceanTask::Impl::run() { if (Logger::DEBUG_LOGGER != NULL) { Logger::DEBUG_LOGGER->log("OCEAN", "DrawOcean"); } ptr<FrameBuffer> fb = SceneManager::getCurrentFrameBuffer(); ptr<Program> prog = SceneManager::getCurrentProgram(); if (o->nbWavesU == NULL) { o->nbWavesU = prog->getUniform1f("nbWaves"); o->wavesU = prog->getUniformSampler("wavesSampler"); o->cameraToOceanU = prog->getUniformMatrix4f("cameraToOcean"); o->screenToCameraU = prog->getUniformMatrix4f("screenToCamera"); o->cameraToScreenU = prog->getUniformMatrix4f("cameraToScreen"); o->oceanToCameraU = prog->getUniformMatrix3f("oceanToCamera"); o->oceanToWorldU = prog->getUniformMatrix4f("oceanToWorld"); o->oceanCameraPosU = prog->getUniform3f("oceanCameraPos"); o->oceanSunDirU = prog->getUniform3f("oceanSunDir"); o->horizon1U = prog->getUniform3f("horizon1"); o->horizon2U = prog->getUniform3f("horizon2"); o->timeU = prog->getUniform1f("time"); o->radiusU = prog->getUniform1f("radius"); o->heightOffsetU = prog->getUniform1f("heightOffset"); o->lodsU = prog->getUniform4f("lods"); assert(o->nbWavesU != NULL); o->generateWaves(); } vector< ptr<TileSampler> > uniforms; SceneNode::FieldIterator ui = n->getFields(); while (ui.hasNext()) { ptr<TileSampler> u = ui.next().cast<TileSampler>(); if (u != NULL && u->getTerrain(0) != NULL) { u->setTileMap(); } } // compute ltoo = localToOcean transform, where ocean frame = tangent space at // camera projection on sphere o->radius in local space mat4d ctol = n->getLocalToCamera().inverse(); vec3d cl = ctol * vec3d::ZERO; // camera in local space if ((o->radius == 0.0 && cl.z > o->zmin) || (o->radius > 0.0 && cl.length() > o->radius + o->zmin) || (o->radius < 0.0 && vec2d(cl.y, cl.z).length() < -o->radius - o->zmin)) { o->oldLtoo = mat4d::IDENTITY; o->offset = vec3d::ZERO; return true; } vec3d ux, uy, uz, oo; if (o->radius == 0.0) { // flat ocean ux = vec3d::UNIT_X; uy = vec3d::UNIT_Y; uz = vec3d::UNIT_Z; oo = vec3d(cl.x, cl.y, 0.0); } else if (o->radius > 0.0) { // spherical ocean uz = cl.normalize(); // unit z vector of ocean frame, in local space if (o->oldLtoo != mat4d::IDENTITY) { ux = vec3d(o->oldLtoo[1][0], o->oldLtoo[1][1], o->oldLtoo[1][2]).crossProduct(uz).normalize(); } else { ux = vec3d::UNIT_Z.crossProduct(uz).normalize(); } uy = uz.crossProduct(ux); // unit y vector oo = uz * o->radius; // origin of ocean frame, in local space } else { // cylindrical ocean uz = vec3d(0.0, -cl.y, -cl.z).normalize(); ux = vec3d::UNIT_X; uy = uz.crossProduct(ux); oo = vec3d(cl.x, 0.0, 0.0) + uz * o->radius; } mat4d ltoo = mat4d( ux.x, ux.y, ux.z, -ux.dotproduct(oo), uy.x, uy.y, uy.z, -uy.dotproduct(oo), uz.x, uz.y, uz.z, -uz.dotproduct(oo), 0.0, 0.0, 0.0, 1.0); // compute ctoo = cameraToOcean transform mat4d ctoo = ltoo * ctol; if (o->oldLtoo != mat4d::IDENTITY) { vec3d delta = ltoo * (o->oldLtoo.inverse() * vec3d::ZERO); o->offset += delta; } o->oldLtoo = ltoo; mat4d ctos = n->getOwner()->getCameraToScreen(); mat4d stoc = ctos.inverse(); vec3d oc = ctoo * vec3d::ZERO; if (o->oceanSunDirU != NULL) { // TODO how to get sun dir in a better way? SceneManager::NodeIterator i = n->getOwner()->getNodes("light"); if (i.hasNext()) { ptr<SceneNode> l = i.next(); vec3d worldSunDir = l->getLocalToParent() * vec3d::ZERO; vec3d oceanSunDir = ltoo.mat3x3() * (n->getWorldToLocal().mat3x3() * worldSunDir); o->oceanSunDirU->set(oceanSunDir.cast<float>()); } } vec4<GLint> screen = fb->getViewport(); vec4d frustum[6]; SceneManager::getFrustumPlanes(ctos, frustum); vec3d left = frustum[0].xyz().normalize(); vec3d right = frustum[1].xyz().normalize(); float fov = (float) safe_acos(-left.dotproduct(right)); float pixelSize = atan(tan(fov / 2.0f) / (screen.w / 2.0f)); // angle under which a screen pixel is viewed from the camera o->cameraToOceanU->setMatrix(ctoo.cast<float>()); o->screenToCameraU->setMatrix(stoc.cast<float>()); o->cameraToScreenU->setMatrix(ctos.cast<float>()); o->oceanToCameraU->setMatrix(ctoo.inverse().mat3x3().cast<float>()); o->oceanCameraPosU->set(vec3f(float(-o->offset.x), float(-o->offset.y), float(oc.z))); if (o->oceanToWorldU != NULL) { o->oceanToWorldU->setMatrix((n->getLocalToWorld() * ltoo.inverse()).cast<float>()); } if (o->horizon1U != NULL) { float h = oc.z; vec3d A0 = (ctoo * vec4d((stoc * vec4d(0.0, 0.0, 0.0, 1.0)).xyz(), 0.0)).xyz(); vec3d dA = (ctoo * vec4d((stoc * vec4d(1.0, 0.0, 0.0, 0.0)).xyz(), 0.0)).xyz(); vec3d B = (ctoo * vec4d((stoc * vec4d(0.0, 1.0, 0.0, 0.0)).xyz(), 0.0)).xyz(); if (o->radius == 0.0) { o->horizon1U->set(vec3f(-(h * 1e-6 + A0.z) / B.z, -dA.z / B.z, 0.0)); o->horizon2U->set(vec3f::ZERO); } else { double h1 = h * (h + 2.0 * o->radius); double h2 = (h + o->radius) * (h + o->radius); double alpha = B.dotproduct(B) * h1 - B.z * B.z * h2; double beta0 = (A0.dotproduct(B) * h1 - B.z * A0.z * h2) / alpha; double beta1 = (dA.dotproduct(B) * h1 - B.z * dA.z * h2) / alpha; double gamma0 = (A0.dotproduct(A0) * h1 - A0.z * A0.z * h2) / alpha; double gamma1 = (A0.dotproduct(dA) * h1 - A0.z * dA.z * h2) / alpha; double gamma2 = (dA.dotproduct(dA) * h1 - dA.z * dA.z * h2) / alpha; o->horizon1U->set(vec3f(-beta0, -beta1, 0.0)); o->horizon2U->set(vec3f(beta0 * beta0 - gamma0, 2.0 * (beta0 * beta1 - gamma1), beta1 * beta1 - gamma2)); } } o->timeU->set(n->getOwner()->getTime() * 1e-6); if (o->radiusU != NULL) { o->radiusU->set(o->radius < 0.0 ? -o->radius : o->radius); } o->heightOffsetU->set(-o->meanHeight); o->lodsU->set(vec4f(o->resolution, pixelSize * o->resolution, log(o->lambdaMin) / log(2.0f), (o->nbWavesU->get() - 1.0f) / (log(o->lambdaMax) / log(2.0f) - log(o->lambdaMin) / log(2.0f)))); if (o->screenGrid == NULL || o->screenWidth != screen.z || o->screenHeight != screen.w) { o->screenWidth = screen.z; o->screenHeight = screen.w; o->screenGrid = new Mesh<vec2f, unsigned int>(TRIANGLES, GPU_STATIC); o->screenGrid->addAttributeType(0, 2, A32F, false); float f = 1.25f; int NX = int(f * screen.z / o->resolution); int NY = int(f * screen.w / o->resolution); for (int i = 0; i < NY; ++i) { for (int j = 0; j < NX; ++j) { o->screenGrid->addVertex(vec2f(2.0*f*j/(NX-1.0f)-f, 2.0*f*i/(NY-1.0f)-f)); } } for (int i = 0; i < NY-1; ++i) { for (int j = 0; j < NX-1; ++j) { o->screenGrid->addIndice(i*NX+j); o->screenGrid->addIndice(i*NX+j+1); o->screenGrid->addIndice((i+1)*NX+j); o->screenGrid->addIndice((i+1)*NX+j); o->screenGrid->addIndice(i*NX+j+1); o->screenGrid->addIndice((i+1)*NX+j+1); } } } fb->draw(prog, *(o->screenGrid)); return true; }