void material_internal::set(const char *pass_name) const { if(!pass_name) return; if(m_last_set_pass_idx>=0) unset(); m_last_set_pass_idx=get_pass_idx(pass_name); if(m_last_set_pass_idx<0) return; update_passes_maps(); const pass &p=m_passes[m_last_set_pass_idx]; p.m_shader.internal().set(); for(int uniform_idx=0;uniform_idx<(int)p.m_uniforms_idxs_map.size();++uniform_idx) m_params[p.m_uniforms_idxs_map[uniform_idx]].apply_to_shader(p.m_shader,uniform_idx); for(int i=0;i<(int)p.m_pass_params.size();++i) { const pass::pass_param &pp=p.m_pass_params[i]; p.m_shader.internal().set_uniform_value(pp.uniform_idx,pp.p.f[0],pp.p.f[1],pp.p.f[2],pp.p.f[3]); } nya_render::set_state(p.m_render_state); for(int slot_idx=0;slot_idx<(int)p.m_textures_slots_map.size();++slot_idx) { int texture_idx=p.m_textures_slots_map[slot_idx]; if(texture_idx<0) continue; if(m_textures[texture_idx].proxy.is_valid()) { if(!m_textures[texture_idx].proxy->internal().set(slot_idx)) { nya_log::warning()<<"invalid texture for semantics '"<<p.m_shader.internal().get_texture_semantics(slot_idx)<<"' in material '"<<m_name<<"\n"; missing_texture(is_shader_sampler_cube(p.m_shader,slot_idx)).internal().set(slot_idx); } } else { nya_log::warning()<<"invalid texture proxy for semantics '"<<p.m_shader.internal().get_texture_semantics(slot_idx)<<"' in material '"<<m_name<<"\n"; missing_texture(is_shader_sampler_cube(p.m_shader,slot_idx)).internal().set(slot_idx); } } }
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; }